버그 수정(카메라 좌표)

Jaemyeong Lee·2025년 4월 1일
0

수업


✅ 주제

  • 본 강의의 핵심은 Emissive 조명 효과가 카메라 움직임에 따라 비정상적으로 동작하는 현상을 분석하고,
    그 원인을 파악하여 CameraPosition 계산 방식을 올바르게 수정하는 것이다.
  • GPU 쉐이더에서 카메라 좌표 추출 방식의 오류를 찾아내고, 이를 뷰 행렬의 수학적 원리로 증명하고 역행렬 기반으로 수정하는 전체 과정을 설명한다.

📘 개념

🔹 뷰 행렬(View Matrix)의 의미

  • 뷰 행렬은 월드 공간(World Space) → 카메라 공간(View Space) 으로 변환하기 위한 행렬이다.
  • 카메라의 위치를 (0,0,0) 으로 간주하고, 카메라가 바라보는 방향을 Z축(룩 벡터) 으로 만드는 것이 핵심이다.

🔹 카메라 위치 추출 오류

  • 쉐이더에서 카메라 위치를 -V._41_42_43로 계산했으나,
    이 방식은 회전이 포함된 경우 잘못된 좌표를 추출하게 된다.
  • 원인은 뷰 행렬의 마지막 열이 단순한 위치 벡터가 아니기 때문이다.

🔹 올바른 카메라 위치 계산 방법

  • 뷰 행렬의 역행렬 = 카메라의 월드 변환 행렬
  • 즉, 카메라 위치를 얻기 위해서는 View Matrix의 역행렬의 마지막 열을 참조해야 한다.

🧾 용어정리

용어설명
View Matrix (V)월드 → 카메라 좌표계로의 변환 행렬
World Matrix로컬 → 월드 변환 행렬
Inverse View Matrix (VInv)View Matrix의 역행렬 (카메라의 월드 변환)
CameraPosition()쉐이더에서 카메라 좌표를 반환하는 함수
Emissive물체 자체에서 방출되는 빛 효과

🧠 코드 분석


🔸 Emissive 조명 테스트

MaterialDesc& desc = material->GetMaterialDesc();
desc.ambient  = Vec4(1.f);
desc.diffuse  = Vec4(1.f);
desc.specular = Vec4(1.f);
desc.emissive = Vec4(1.f);
LightDesc lightDesc;
lightDesc.ambient  = Vec4(0.f);
lightDesc.diffuse  = Vec4(0.f);
lightDesc.specular = Vec4(0.f);
lightDesc.emissive = Vec4(1.f, 0.f, 0.f, 1.f); // 빨간색 Emissive
  • 의도: 다른 조명은 모두 끄고 Emissive만 켜서 테스트
  • 증상: 카메라가 정면일 때는 정상, 이동하면 Emissive가 사라지는 문제 발생

🔸 쉐이더 내 오류 지점

MeshOutput output;
output.worldPosition = input.position.xyz; // ❌ 오류!

input.positionLocal 좌표이므로 여기에 World 변환이 필요하다.

output.position = mul(input.position, W); // 월드 좌표 계산
output.worldPosition = output.position.xyz; // ✅ 수정

🔸 쉐이더 디버깅 기법

float4 PS(MeshOutput input) : SV_TARGET
{
    ComputeNormalMapping(input.normal, input.tangent, input.uv);

    float3 pos = CameraPosition(); // 카메라 좌표 출력용
    return float4(pos, 1);
}
  • 색상 값으로 CameraPosition을 시각화 → 값이 변하지 않음 → 좌표 계산 오류 확정

🔸 기존 오류 코드 (문제 원인)

float3 CameraPosition()
{
    return -V._41_42_43; // ❌ 잘못된 카메라 좌표 추출
}
  • 이 식은 이론적으로는 카메라 위치를 의미하지 않음
  • View Matrix의 _41_42_43-C•Right, -C•Up, -C•Look 내적값
  • 회전이 섞이면 실제 카메라 위치가 아님

🔸 View Matrix의 구성 원리

  • 수학적으로 View 행렬은 다음과 같이 구성된다:
V = | R  U  L  0 |
    |            |
    | -C•R -C•U -C•L 1 |
  • C는 카메라 위치, R은 Right, U는 Up, L은 Look 벡터
  • 따라서 마지막 행이 단순한 -C가 아님 → 역행렬 필요

🔸 수정 코드 (정상 구현)

cbuffer GlobalBuffer
{
    matrix V;
    matrix P;
    matrix VP;
    matrix VInv; // ✅ View 역행렬 추가
};

float3 CameraPosition()
{
    return VInv._41_42_43; // ✅ 정확한 카메라 위치
}

🔸 C++ 측 수정 (RenderManager)

struct GlobalDesc 
{
	Matrix V     = Matrix::Identity;
	Matrix P     = Matrix::Identity;
	Matrix VP    = Matrix::Identity;
	Matrix VInv  = Matrix::Identity; // ✅ View 역행렬 저장
};

void RenderManager::PushGlobalData(const Matrix& view, const Matrix& projection)
{
	_globalDesc.V     = view;
	_globalDesc.P     = projection;
	_globalDesc.VP    = view * projection;
	_globalDesc.VInv  = view.Invert(); // ✅ 역행렬 계산
	_globalBuffer->CopyData(_globalDesc);
	_globalEffectBuffer->SetConstantBuffer(_globalBuffer->GetComPtr().Get());
}

✅ 핵심 요약

  • 카메라가 움직일 때 Emissive 효과가 사라지는 문제의 원인은 CameraPosition 계산 오류였다.
  • 기존 -V._41_42_43 방식은 회전 성분까지 포함되어 잘못된 좌표를 반환하게 된다.
  • View 행렬의 역행렬을 계산하여 정확한 카메라 위치를 추출해야 한다.
  • 쉐이더에서는 CameraPosition() 함수를 통해 정확한 좌표를 사용해야 조명 계산이 일관성 있게 작동한다.
  • 디버깅 시에는 쉐이더의 색상 출력을 활용하여 좌표를 시각적으로 확인하는 것이 효과적이다.
  • 버그는 단순한 코드 실수가 아니라 수학적 오해에서 비롯될 수 있다.
  • 뷰 행렬의 구조와 의미를 정확히 이해하면 카메라와 관련된 문제를 원천적으로 방지할 수 있다.
  • “가설 → 검증 → 추적 → 수정 → 확인”의 사고 흐름은 모든 디버깅의 핵심 전략이다.
  • 프로그래머는 완벽을 기하기보다, 실험하듯 “부딪히며 증명하고 학습하는 자세”가 중요하다.
  • 라이브러리 도입이나 고급 기술도 마찬가지로, 두려워하지 말고 하나씩 부딪혀 보는 것이 해결의 첫걸음이다.

profile
李家네_공부방

0개의 댓글