MOONSUN
[D3D] Vertex Shader에서 이루어지는 월드/뷰/투영 변환 본문
렌더링 파이프라인에서 핵심 단계 중 하나인 버텍스 셰이더(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. 만드는 방법
- 카메라 월드 행렬의 역행렬
- 카메라가 원점으로 오게 하는 변환
- 함수 사용
- 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;
}
'D3D' 카테고리의 다른 글
| [D3D11] 01. RenderingTriangle 삼각형 그리기 (0) | 2025.09.29 |
|---|---|
| [D3D] Direct3D 11 렌더링 파이프라인 주요 객체 (0) | 2025.09.29 |
| [게임 수학] 반사 벡터(Reflection Vector)와 투영 벡터(Projection Vector)) (0) | 2025.09.23 |
| [D3D 이론] 빛(Lighting) 모델 : Phong, Blinn-Phong 와 수식 의미 (0) | 2025.09.22 |
| [게임 수학] 행렬 연산의 특징 : 비가환성, 역행렬, 전치(Transpose) 등.. (0) | 2025.09.19 |