DirectX

14. Projection 변환 행렬

devRiripong 2023. 12. 22.
반응형

루키스님 DirectX12 강의 쪽이 DirectX11쪽 보다 더 설명이 자세하다

 

카메라 좌표까지 왔으면 이제 남은 건 납작하게 만드는 것이다.

어떠한 영역의 물체들이 어떤 비율로 찍힐지 카메라 기준으로 한 좌표계에서 투영해서 납장하게 만드는 단계이다.

 

1. 절두체(Frustum) 내의 객체 확인:

3차원 공간에서 카메라 앞에 있는 절두체 내의 객체만 화면에 표시됩니다. 절두체는 near plane과 far plane으로 정의되며, 이 영역 안에 있는 객체들만 화면에 그려진다.

 

2. 깊이에 따른 크기 조정:

원근감을 표현하기 위해, 객체의 `z` 좌표에 따라 크기가 조정됩니다. 객체가 카메라로부터 멀어질수록 (즉, `z` 값이 커질수록) 화면 상에서 더 작게 표현됩니다. 이를 위해 `z` 좌표로 나눠준다.

 

3. 화면 비율 보정:

화면 비가 직사각형이 될 경우 정사각형의 이미지가 가로가 길게 직사각형으로 출력되는 것을 볼 수 있었다. 미리 화면 비율이 늘어날걸 예상을 해가지고 거꾸로 얘를 한번 줄여주는 작업을 해주면 된다. 예를 들어 스크린 사이즈가 800x600이었다 하면 r = w/h = 800/600 대략적으로 1.3이 된다. 그러면 이 r화면 비율이 될텐데 그 r을 이용해서 일단은 이렇게 x를 한번 나눠주도록 한다.

이는 화면의 종횡비에 맞게 객체의 원본 비율에 맞게 표시되도록 한다.

 

4. Field of View 보정: 

유니티에서 설정하는 Field of View(FOV)는 카메라가 포착하는 시야각을 의미한다.. 이 각도를 고려하여 좌표를 조정합니다.

각도가 커지면 찍히는 영역이 훨씬 더 넒어 진다는 것이곳, 이는 더 많은 물체를 찍을 수가 있게 되었다는 얘기가 되는 거니까 원래는 이 각도에서는 얘가 한 이 정도 크기를 차지하고 있었다는 게 훨씬 더 넓은 공간을 찍을 수 있게 되면 쪼그라드는 효과가 일어나게 될 것이다. 그래서 `tan(각도/2)`로 나누어  좌표를 조정한다. near를 1, 각도를 180도로 가정했기 때문에 tan90 즉 1인 상태가 디폴트다. 그래서 tan 각도/2 로 나눠서 좌표를 맞춰준다.

 

5. 행렬의 값 채워주기:

이제 `x, y, z` 좌표를 프로젝션 행렬에 적용하여 2D 화면 상의 좌표로 변환합니다. 이를 위해 행렬의 특정 위치에 않맞은 값을 넣어 2~4에서 만들어준 X, Y 값이 행렬 연산후 나올 수 있게 해야 한다.

변수가 x,z 그리고 y,z 이렇게 두개씩 있으면 지금까지 배운 행렬식으로는 표현을 못하니 연산 후  X,Y에서 z를 제외한 값이 나올 수 있게 알맞은 값을 1행 1열, 2행 2열에 각각 채워 줍니다. 

그리고 3열 3행에 1을 넣어서연산을 하고 Z가 z로 나오게 하면 애매하다. 왜냐하면 깊이 값은 0~1이어야 하는데 z를 보존한 값은 X, Y를 구하는 연산에 필요한 값이지 연산의 결과인 깊이값이 아니기 때문이다. 그래서 결과의 네번째 값이 z를 보존해 주기로 하고 1을 3행 4열에 넣는다. 연산후 네번째 값으로 나오는 z는 rasterizer에 넘겨지고 연산 결과 값 X,Y,Z를 z로 나눠주게 된다.

 

6. 깊이 값을 구해줄 프로젝션 행렬의 A와 B 값 구하기:

`z` 좌표를 0~1 사이의 값으로 변환해줄 행렬의 A와 B를 구해야 하는데

3행3열에 A, 3행 4열에 B를 넣는다 가정하면 

 

결과값 Az+B을 rasterizer에서 z로 나눠주기 때문에

G(z) = A+B/z라 가정을 하면

z=near일 때 A+B/n =0

z=far일 때 A+B/f = 1

이렇게 힌트를 구할 수 있고,

첫번째 힌트를 통해 B = -An를 구하고, 이걸 두번째 힌트에 대입하면

A-An/f =1

A(1-n/f) = 1

A ((f-n)/f) =1

A = f/(f-n)이라는 걸 알 수 있다. 이걸 첫번째 힌트에 대입해서 

B = -nf/(f-n)인 것을 알 수 있다. 

A, B값을 행렬에 넣어주면 프로젝션 행렬이 완성된다. 

 

7. 전 시간에 행렬 없어도 그림이 잘 나왔던 이유

여기서 힌트 얻을 수 있는게 지난 시간에 행렬 신경 아무것도 안해줬는데 그냥 화면에 잘 떴어. 
왜 그런 걸까? 
원래는 전 과정을 거쳐서 -1~1 사이의 값을 연산을 통해 구해줬어야 했지만 지난 시간엔 아래와 같이 그냥 -0.5f, -0.5f의 값을 넣어줬기 때문이야. position에 -1~1사이의 값을 세팅해 줬기 때문에 잘 나온것이다.

void Game::CreateGeometry()
{
	// VertexData - CPU 메모리에 있는 거 
	// 1 3
	// 0 2
	{
		_vertices.resize(4);

		_vertices[0].position = Vec3(-0.5f, -0.5f, 0.f); 
		_vertices[0].uv = Vec2(0.f, 1.f); 
		// _vertices[0].color = Color(1.f, 0.f, 0.f, 1.f); 

		_vertices[1].position = Vec3(-0.5f, 0.5f, 0.f);
		_vertices[1].uv = Vec2(0.f, 0.f);
		// _vertices[1].color = Color(1.f, 0.f, 0.f, 1.f);

		_vertices[2].position = Vec3(0.5f, -0.5f, 0.f);
		_vertices[2].uv = Vec2(1.f, 1.f);
		// _vertices[2].color = Color(1.f, 0.f, 0.f, 1.f);

		_vertices[3].position = Vec3(0.5f, 0.5f, 0.f);
		_vertices[3].uv = Vec2(1.f, 0.f);
		// _vertices[3].color = Color(1.f, 0.f, 0.f, 1.f);
	}
반응형

'DirectX' 카테고리의 다른 글

16. SimpleMath 실습  (0) 2023.12.29
15. Screen 변환 행렬  (0) 2023.12.22
13. View 변환 행렬  (0) 2023.12.21
12. World 변환 행렬  (0) 2023.12.20
11. 좌표계 변환  (0) 2023.12.19

댓글