Notice
Recent Posts
Recent Comments
Link
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

MOONSUN

[D3D] Vertex Shader에서 이루어지는 월드/뷰/투영 변환 본문

D3D

[D3D] Vertex Shader에서 이루어지는 월드/뷰/투영 변환

MoonSun_v 2025. 9. 24. 10:51

 

렌더링 파이프라인에서 핵심 단계 중 하나인 버텍스 셰이더(Vertex Shader)에서,

월드(World), 뷰(View), 투영(Projection) 변환이 어떻게 이루어지는지 단계별로 정리하고자 한다.

 

 

1. 월드 변환 (World Transform)

모델 좌표계 → 월드 좌표계로 변환

우리가 만든 모델(캐릭터, 큐브, 몬스터)의 위치, 회전, 크기를 게임 월드에 맞게 배치
  • 모델 좌표계: 각 정점(Vertex)은 모델 기준 원점(0,0,0)에서 정의
  • 월드 좌표계: 게임 세계 전체에서 객체가 실제 있는 위치

여기서 는 월드 행렬(World Matrix)으로, 위치, 회전, 크기 정보를 모두 포함한다.

 

 

1-1. 행렬 공식 

일반적으로 복합 변환:

WorldTransform = ScaleTransform  *  RotationTransform * TranslationTransform

 

정점에 적용:

Vector4(WorldPos) = vector4(ModelPos) * Matrix4x4(WorldTransform)

 

  • vector4(ModelPos) 을 곱해주는 이유? 
    • 동차 좌표 활용하면 이동도 행렬 곱으로 표현할 수 있음
      • ( 동차 좌표란? 해당 코드의 경우, (x, y, z)에 w=1 을 붙여서 4차원 벡터로 만든 것 )
    • 회전, 스케일은 행렬 곱으로 표현 가능하지만, 이동은 단순한 덧셈이므로 행렬 곱으로 표현 X

 

 

위의 수식 과정을 코드에서 확인해보면,

 

1. C++ 쪽 코드

 

WorldTransform = ScaleTransform * RotationTransform * TranslationTransform

m_World = scale * translate * orbit // Scale → Rotate → Translate 

 

2. HLSL 쪽 코드

Vector4(WorldPos) = vector4(ModelPos) * Matrix4x4(WorldTransform)

output.Pos = mul(Pos, World);                  // ModelPos * WorldTransform
// output.Pos = mul(output.Pos, View);         
// output.Pos = mul(output.Pos, Projection);

이어서 View, Projection 곱하면 월드 → 카메라 → 투영 좌표 변환까지 완료

 

 

3. GPU에 넘겨줄 때 Transpose해서 전달 하면 됨.

    • ( Transpose 해야하는 이유는 다른 글에서 설명 했음 )

C++에서 전치(Transpose) 안하고 GPU에게 넘겨주면 HLSL 파일을 작성할 때 mul(World, Pos) 하면 되긴하는데,,

  • 코드 유지보수와 직관성 측면에서 잘 안 쓰임
  • 특히 부모-자식 월드 행렬 곱처럼 여러 행렬을 곱할 때 헷갈릴 수 있음

그러지 말자,,!!

 

권장 방식은 Transpose 해서 mul(Pos, World) 그대로 사용하는 것.

 

 

 

2. 뷰 변환 (View Transform)

월드 좌표계 → 카메라 좌표계로 변환

카메라가 항상 원점에 있고, 바라보는 방향이 +Z라고 생각하게 만듦
  • 카메라 위치와 방향에 맞춰 월드 좌표를 재배치
  • 카메라를 기준으로 모델이 어디 있는지 계산

 

여기서 는 뷰 행렬(View Matrix).
일반적으로 카메라 위치, 카메라 방향, 업벡터를 이용해 생성한다. 

 

 

2-1. 만드는 방법

  1. 카메라 월드 행렬의 역행렬
    • 카메라가 원점으로 오게 하는 변환
  2. 함수 사용
    • XMMatrixLookAtLH(Eye, Target, Up) → 특정 위치를 바라보는 카메라
    • XMMatrixLookToLH(Eye, Direction, Up) → 방향 벡터 기준 카메라

 

2-2. 예시 

  • 월드에 (10,0,5)에 있는 캐릭터
  • 카메라 위치 (0,2,-5), 원점 바라봄
  • → View 행렬 곱하면 카메라 기준 좌표가 됨

 

 

 

3. 투영 변환 (Projection Transform)

카메라 좌표계 → 클립(Projection) 좌표계

3D 장면을 2D 화면에 원근감 있게 보여주기
  • 카메라 좌표(x,y,z,1) → 클립 좌표(x',y',z',w')
  • 이후 GPU가 x'/w, y'/w 로 나눠 Normalized Device Coordinate (NDC) 로 변환
  • 화면에 맞게 스케일/잘림(clip) 처리 

 

는 투영 행렬(Projection Matrix)이다.

 

  • 원근 투영(Perspective Projection)   : 멀리 있는 객체가 작게 보임
  • 직교 투영(Orthographic Projection) : 거리와 상관없이 동일한 크기

 

 

3-1. 함수 예시

  • 원근 투영 
XMMATRIX XMMatrixPerspectiveFovLH(FovY, Aspect, NearZ, FarZ)
FovY 세로 시야각
Aspect 가로 / 세로 비율
NearZ / FarZ 카메라 절두체 앞뒤 범위

 

  • 직교 투영
XMMATRIX XMMatrixOrthographicLH( ViewWidth, ViewHeight, NearZ, FarZ );
ViewWidth 가시 영역 너비 
ViewHeight 가시 영역 높이 
NearZ / FarZ 카메라 절두체 앞뒤 범위

 

 

 

3-3. 예시

  • 카메라 앞 1~1000 유닛 안에 있는 객체만 보임
  • 가까운 물체 → 크게, 먼 물체 → 작게 보임
  • Perspective Divide로 x/w, y/w 계산 → 원근감 적용

 

 

4. 정리 : 최종 변환 

Vertex Shader에서 일반적으로 사용하는 순서는 다음과 같다.

 

  • pos_local → 모델 좌표
  • W → 월드 변환
  • V → 뷰 변환
  • P → 투영 변환
  • pos_clip → 최종 클립 공간 좌표
    • 최종적으로 GPU는 이 좌표를 정규화(NDC) 후 래스터라이저에 넘겨서 화면 픽셀로 변환

 

변환 단계 입력 좌표 출력 좌표 역할
월드 변환 Local World 객체 위치/회전/스케일 적용
뷰 변환 World View 카메라 기준 좌표로 변환
투영 변환 View Clip 원근 또는 직교 적용, 2D 화면으로 변환 준비

 

 

4-1. 전체 변환 코드 흐름

C++ 코드 예시 

// World Matrix 
m_World = XMMatrixIdentity();

// View Matrix 
XMVECTOR Eye = XMVectorSet(0.0f, 1.0f, -5.0f, 0.0f);
XMVECTOR At = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMVECTOR Up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

m_View = XMMatrixLookAtLH(Eye, At, Up);

// Projection Matrix
m_Projection = XMMatrixPerspectiveFovLH(XM_PIDIV2, m_ClientWidth / (FLOAT)m_ClientHeight, 0.01f, 100.0f);

 

Vertex Shader 코드 예시 

PS_INPUT main(float4 Pos : POSITION)
{
    PS_INPUT output;
    output.Pos = mul(Pos, World);       // 월드 변환
    output.Pos = mul(output.Pos, View); // 뷰 변환
    output.Pos = mul(output.Pos, Projection); // 투영 변환
    return output;
}