Local Space => World Space
물체는 모두 자신만의 Local Space를 가지고 있다.
이를 월드상에 적절하게 배치하려면 자표 변경 변환을 수행해야 한다. 이것을 World Transform이라고 부르고 해당 변환 행렬을 World Matrix라고 부른다고 한다. 각 물체를 WorldSpace 기준으로 변환하고 나면, 모든 좌표가 동일한 좌표계를 기준으로 한다.
어떻게 로컬 좌표에서 월드 좌표로 변환할 수 있을까?
월드 좌표 정점 = 로컬 좌표 * 월드 변환 행렬 (Scale * Rotation * Transform)
변환 행렬을 곱해 월드 좌표로 변환할 수 있다. 1학기 때는 이 부분에서 머리를 싸매고 들어도 잘 모르겠어서... 선생님께서 이번엔 간소화해서 가르쳐주셨다.
Scale
일단 Scale부터 알아보자. 이름에서부터 알 수 있듯이, 물체의 크기를 담고 있는 행렬이다.
X, Y, Z마다 크기를 저장할 수 있는데, 각각 11, 22, 33 자리에 대각 행렬로 표현한다.
이렇게 대각 행렬로 표현하면 로컬의 X, Y, Z 좌표가 모두 곱해질 수 있다.
Rotation
S 다음에 R은 Rotation이다. 회전도 표현이 가능한데, 이 회전 행렬은 어렵다...
먼저 Z축 회전 행렬을 살펴보자. 아래 그림은 점 P를 Z축으로 θ 각도만큼 회전하는 그림이다.
이 블로그를 참고해 작성했다.
회전한 좌표를 코코싸사, 싸코코싸... 즉 삼각함수의 덧셈 정리를 통해 정리할 수 있다.
식을 이렇게 정리할 수 있다. 원래 x = rcos(ϕ), y = rsin(ϕ), z=z였으므로 치환해서 정리할 수 있다. (x, y, z) * 행렬 = (x', y', z')으로 표현해보자.
Z축 회전 행렬은 이렇게 나오게 된다. 편의상 그림에서 θ는 뺐지만, cosθ와 sinθ이다. Y축과 X축 모두 알아보자.
Y축 회전 변환을 알아보기 위해 아까와 똑같은 뷰로 축만 바꾸었다. 그럼, 문자만 치환하면 된다. 아까와 같이 식을 세워보자.
P(x, y, z) = P(r sin(ϕ), y, r cos(ϕ))
P'(x', y', z') = P'(rsin(θ+ϕ), y, rcos(θ+ϕ))
이렇게 된다. 삼각함수의 덧셈 정리를 통해 식을 정리해보자.
x' = r(sin(θ)cos(ϕ)) + r(cos(θ)sin(ϕ))
y' = y
z' = r(cos(θ)cos(ϕ)) - r(sin(θ)sin(ϕ))
x = rsin(ϕ), z = rcos(ϕ)이므로 x, z를 활용하여 정리할 수 있다.
x' = x * cos(θ) + z * sin(θ)
y' = y
z' = z * cos(θ) - x * sin(θ)
(x, y, z) * Y축 회전 행렬 = (x', y', z') 꼴로 만들어보자.
X축 회전 행렬도 유도해 보았는데, 컴퓨터로 복사해서 붙여넣기하는 것보다 손으로 쓰는 게 공부가 더 잘될 것 같아서 손으로 써왔다. 방식은 다를 것 없다.
아무튼! 각 축의 회전 행렬은 구한대로 아래처럼 구성된다.
이를 X축 회전 행렬 * Y축 회전 행렬 * Z축 회전 행렬을 곱한 값을 회전 행렬이라고 한다.
Transform
마지막! 이동을 변환하면 끝난다. 하지만, 3x3 행렬은 평행이동을 표현할 수 없다. 왜냐하면, 벡터는 위치와 무관하게 오직 방향과 크기만을 나타내기 때문이다. 그렇기 때문에 x, y, z에 w를 추가한 동차 좌표계로 표현한다.
만약, w에 1이 들어있다면 x + w*N 형태로 행렬 곱셈을 할 수 있게 된다.
만약에(x, y, z)에 각각 특정한 값을 평행이동한 (x+a, y+b, z+c)를 표현하고 싶다면 어떻게 할까?
(x, y, z, 1) * 이동 행렬 = (x+a, y+b, z+c, 1)의 형태로 나타내는 것이다.
이렇게 하면 이동 행렬까지 끝이다~
정리
월드 변환 행렬 = 크기 행렬 * 회전 행렬 * 이동 행렬. 즉 W = S*R*T가 된다. 이를 로컬 좌표 정점에 곱해주면, 월드 변환 행렬로 나오게 되는 것!
시야 공간
로컬 좌표를 월드 좌표로 옮겼다고 끝나는 게 아니다. 게임은 항상 카메라가 있기에, 카메라로 보는 모습 또한 구해주어야 한다. 그렇게 하기 위해서는 월드상의 모든 물체를 카메라 기준으로 맞춰주는 게 필요하다.
이를 잘 표현한 사진이 있어 가져와봤다. 이 카메라 좌표계는 어떻게 구할 수 있을까?
일반적으로 생각해보면, 우리가 카메라를 오른쪽 위로 이동시키면, 물체는 왼쪽 아래로 이동하는 것처럼 보인다. 카메라 위치와 방향을 기준으로 월드상의 물체들이 반대가 된다는 것을 알 수 있다.
DirectX에서는 카메라 변환 행렬을 만들어주는 함수가 있다고 한다!
이 함수에서는 (pAt-pEye)으로 Look 벡터를 구하고, (pUp x pEye)으로 Right 방향을 구한다. Look과 Right 또한 내적해 수직인 Up 벡터 또한 구하고 이를 변환 행렬에 넣어준다.
아까 언급했듯 물체는 카메라의 반대로 작용되어야 하므로, 카메라의 위치 값을 각 카메라의 방향에 곱해주고, 음수로 만든 다음 변환 행렬을 만들어준다.
투영과 동차 절단 공안
카메라에 보이기만 한다고 끝나는 게 아니다. Near 값과 Far 값을 가지고 있는, 유니티에서도 으레 설정할 수 있는 카메라 절두체 안에 물체가 있어야 한다. 절두체 안에 있다는 것은, Near 값에 가까울 수록 크게 보인다는 것, Far 값에 가까울수록 작게 보여야 한다...
이를 투영이라고 한다. 평행선들이 하나의 소실점으로 수렴하고, 깊이에 따라 투영의 크기가 감소하는 방식으로 수행하는데, 이를 원근 투영이라고 한다.
원근 투영 변환 행렬
행렬이... 정말 그래픽스에서는 중요한 요소인가보다. 투영을 하기 위해서도 투영 행렬이 필요하다. 투영 반환에서 중요한 점은 화면 좌표계의 비율을 고려해서 반환하는 것이다.
정육면체를 출력하고 싶었는데, 왼쪽 그림처럼 찌부가 되어서 나오는 건 잘못된 일이기 때문이다. 따라서, 원근 투영 반환은 z값을 기준으로 x, y를 나눈다. 아까 동차 좌표계! 이동 행렬과 비슷한 방법이다. 아까는 4차원을 3차원으로, 지금은 3차원을 2차원으로 표현한다.
이런 투영 행렬이 있다고 가정해보자.
이 그림에서 d = 1/tan(2/θ)임을 알 수 있다.
투영되는 과정은 이러하며, 닮은 삼각형의 비를 이용할 수 있다. 비례식을 사용한다면...
ny : d = vy : -vz
ny = (d * vy) / -vz
nx = (d * vx) / -vz
결과적으로 Pndc를 구하는 식을 도출해낼 수 있다.
아까 정육면체가 찌부가 되는 문제를 해결하기 위해 종횡비를 거꾸로 적용시킬 것이다. 모니터는 양옆으로 더 긴 직사각형이기 때문에 종횡비인 Width/Height를 구해주고 역수를 취해 Sx에 곱해준다.
이렇게 되면 홀쭉해지고 나중에는 원래대로 돌아올 것이다.
정말 마지막으로, 절두체의 표현 범위를 설정할 것이다. 아까 말했듯이 절두체는 항상 Near과 Far 값을 가진다. 투영이 끝나더라도 깊이 버퍼링 알고리즘을 위해서 Z값이 필요하다고 한다. 따라서 깊이 성분도 일정 구간(0~1)로 정규화해 변환 행렬에 추가한다.
이런 투영 행렬을 만들게 되면,
곱하기 연산한 값이 나오게 된다. 이때, z가 만약 zn이라면 z 자리는 0이, zf라면 그대로 zf가 나오게 된다. 이를 모두 z로 나누면 각각 0, 1이 나온다.
z값이 0~1 범위 안에 있으면, 절두체 깊이 범위 안에 있다는 것이고, 그렇지 않다면 범위 밖에 있는 것으로 정리할 수 있다.
DX에는 투영 행렬을 구하는 함수로 구현할 수 있다.
솔직히 말해서... 뷰까지는 괜찮았는데... 투영 행렬은 아직 잘 감이 안 오긴 한다... ㅠ_ㅠ
이제 개학하면 3D 수업에서 배우게 될텐데, 그때 배우고 이해해서 포스팅을 내가 이해하는 언어로 고쳐야겠다. 지금은 수업 내용 + 다른 블로그 인용밖에 되지 않는 것 같다. 특히 투영 행렬 부분에서는 더...
참고
'Direct X' 카테고리의 다른 글
[DX12] 정육면체의 정점을 묶고 렌더링해보자! (0) | 2023.02.27 |
---|---|
[DX12] 렌더링 파이프라인에 대해 알아보자! (3) | 2023.02.27 |
[DX12] 빈 화면을 렌더링해보자! (1) | 2023.02.26 |
[DX12] 장치를 초기화 해보자! (3) (RTV, DSV, Viewport) (0) | 2023.02.26 |
[DX12] 장치를 초기화 해보자! (2) (Command Object, Swap Chain) (0) | 2023.02.13 |