*Clip Space
=> 대표적인 투영 방법
원근 투영 / 직교 투영
*직교 투영 ( Orthogonal Projection )
=> 평면상 수직으로 투영된다.
=> 원근감이 존재하지 않는다.
=> UI ( User Interface ) 를 랜더링할 때 많이 쓰임.
@viewport width : w
@viewport height : h
@near plane : n
@far plane : f
* 직교 투영 행렬
=> 직교 투영 행렬은 원근 투영 행렬보다 비교적 어렵지 않게 구할 수 있다.
몇 가지만 짚고 넘어가자.
* 왜 2 / w , 2 / h 인가 ??
=> 투영좌표계에선 x, y 가 (-1, 1) 이기 때문.
가운데 ( 0,0 ) 에서,
x 기준 : 1,4 사분면 / 2,3 사분면
y 기준 : 1,2 사분면 / 3,4 사분면
으로 각각 나누어줘야함.
* 왜 깊이를 설정해야하는가 ??
=> 원근 투영과 마찬가지로, NDC 공간으로 선형변환하는 것이기에
NDC : x,y ( -1, 1 ), z ( 0, 1 ) 범위로 맞춰주어야함.
=> 계산의 편의성을 위해서
직교 투영을 하는 물체들끼리 깊이 계산이 필요할 수도 있다.
( 보통은 깊이를 0 으로 둘 것이다. )
참고할 것은
직교 행렬을 적용하게 되면 바로 NDC 공간에 가게 된다.
( z 를 나누어주는 과정이 없기 때문. )
* A, B 를 구해보자.
=> 원근 투영에서 했던 것과는 다르게,
직교 투영에서는 행렬에 z 를 나누는 부분이 없기 때문에
최종 식은
z' = Az + B 가 된다.
g (z) = Az + B
g (n) = 0
g (f) = 1
해당 방정식을 풀게 되면 A, B 를 구할 수 있다.
A = -1 / n - f -> ( 1 / f - n )
B = n / n - f
* XMMatrixOrthographicLH
=> 위에서 설명한대로 DX 에 제공되는 함수도 같은 방식을 사용하는 것을 볼 수 있다.
=> 하나씩 해당 행들을 M 에 넣으면서 마지막에 M 을 리턴한다.
inline XMMATRIX XM_CALLCONV XMMatrixOrthographicLH
(
float ViewWidth,
float ViewHeight,
float NearZ,
float FarZ
)
XMMATRIX M;
float fRange = 1.0f / (FarZ-NearZ);
// Note: This is recorded on the stack
XMVECTOR rMem = {
2.0f / ViewWidth,
2.0f / ViewHeight,
fRange,
-fRange * NearZ
};
// Copy from memory to SSE register
XMVECTOR vValues = rMem;
XMVECTOR vTemp = _mm_setzero_ps();
// Copy x only
vTemp = _mm_move_ss(vTemp,vValues);
// 2.0f / ViewWidth,0,0,0
M.r[0] = vTemp;
// 0,2.0f / ViewHeight,0,0
vTemp = vValues;
vTemp = _mm_and_ps(vTemp,g_XMMaskY);
M.r[1] = vTemp;
// x=fRange,y=-fRange * NearZ,0,1.0f
vTemp = _mm_setzero_ps();
vValues = _mm_shuffle_ps(vValues,g_XMIdentityR3,_MM_SHUFFLE(3,2,3,2));
// 0,0,fRange,0.0f
vTemp = _mm_shuffle_ps(vTemp,vValues,_MM_SHUFFLE(2,0,0,0));
M.r[2] = vTemp;
// 0,0,-fRange * NearZ,1.0f
vTemp = _mm_shuffle_ps(vTemp,vValues,_MM_SHUFFLE(3,1,0,0));
M.r[3] = vTemp;
return M;
*ScreenSpace ( Window Space )
*viewport
=> 후면버퍼의 한 직사각형 영역.
=> 전체영역은 아님.
=> 여러 개의 viewport 생성 가능.
예를 들어, 1p, 2p 의 화면... 등
=> 이 변환을 마치고 나면 x,y 성분은 픽셀 단위의 값이 된다.
* ScreenSpace 행렬 만들기
Width, Height : viewport 의 가로,세로 길이
Left, Top : viewport 의 왼쪽 상단의 시작점 위치
Min/Max Depth : viewport 의 최대, 최소 깊이
=> 해당 데이터를 기준으로 행렬을 만들어 갈 것.
x 좌표를 기준으로 생각할 때,
NDC 의 가장 왼쪽은 -1 이다.
viewport 의 가장 왼쪽은 0 이다.
-1 -> 0 으로 비례해야하기 때문에
w / 2 만큼 곱하고 다시 w / 2 더하게 되면 0 으로 비례하게 된다.
이어서,
viewport 의 Left 지점까지 고려해야하므로 +Left 를 해주어야 한다.
정리하면,
x' = x * w / 2 + w / 2 + Left
해당 식을 도출하기 위해 ( x,y,z,1 ) * ( 행렬 ) 의
1열은 ( w / 2 , 0, 0, Left + w / 2 )
y 부분도 같은 방식으로 도출 가능.
다만,
NDC 에서 y 의 1 은 viewport 에서 0 이기 때문에
1 -> 0 으로 비례해야한다. 따라서, ( -1 ) 을 곱해주어야 한다.
따라서, 아래의 행렬을 구할 수 있겠다.
여기서 A, B 를 비워둔 이유는
이전과 마찬가지로 Z 값을 조작하기 위해서인데 Min/Max Depth 에 관한 값이다.
보통은 z 값을 그대로 [ 0, 1 ] 사용하겠지만, 특정한 상황에서 사용할 수 있다.
예를 들어,
다수의 viewport 를 사용할 때,
viewport 간 Depth 차이를 구별하기 위해서이다. ( RenderTarget )
* Screen Space 로 선형변환 행렬
참고 자료
- 프랭크 D.루나. ( DX11 을 활용한 3D 게임 프로그래밍 입문 ), 류광(역)
'DirectX > 개념' 카테고리의 다른 글
[DX] ##7. GJK ( GILBERT-JOHNSON-KEERTHI ) Algorithm (0) | 2022.08.15 |
---|---|
[DX] ##6. AABB, OBB 충돌 SAT 분리축 이론( Separating Axis Theorem ) (4) | 2022.08.05 |
[DX] ##4. Z-Fighting (0) | 2022.07.31 |
[DX] ##2. Local, World, View Space (0) | 2022.07.29 |
[DX] ##1. 좌표계 변환 (0) | 2022.07.28 |