본문 바로가기

컴퓨터 그래픽스

컴퓨터 그래픽스 기초 - Vertex shader([KUOCW] 한정현 교수님 강의)

이 포스팅은 [KUOCW] 한정현 교수님 강의 5장- 정점 처리를 듣고 정리한 내용입니다.

선형대수학의 기초적인 지식이 요구됩니다.

 

앞선 포스팅에는 vertex(정점) 들을 어떻게 정의되고 변환할 수 있는지를 알아보았습니다.

이번 포스팅에는 컴퓨터 그래픽스에서 이 정점들을 어떻게 처리하는지 알아보겠습니다.


 

  • Rendering Pipeline

Rendering Pipeline

들어가기 앞서, Rendering Pipeline을 먼저 보겠습니다. 그림으로 나타내면 위와 같습니다. 이 파이프라인을 이해하는 것이 그래픽스를 이해하는 맥락의 중요한 핵심이라고 저는 생각합니다. 사전에 이 지식 없이 여러 정보를 봤다가 왜 이렇게 하는지 몰라서 이해가 안 됐던 기억들이 있습니다. 

 

따라서, 앞으로의 포스팅은 위의 파이프라인 순서대로 쭉 진행된다고 보시면 됩니다. 간략하게 설명하자면, 파란색은 사용자가 프로그래밍이 가능한, 즉 변환 가능한 부분을 나타내고, 노란색은 하드웨어에서 처리하는 변경 불가능한 부분입니다.

 

하나씩 보면, vertex shader에서는 정점 관련한 연산을 처리하고 오늘 나올 camera space에 나타날 vertex가 뭔지 가려냅니다. 그 다음 rasterizer가 해당 polygon을 픽셀화 합니다. 결국 컴퓨터 모니터의 픽셀에 매핑이 돼야 하기 때문이죠. 그리고 fragment shader는 그 픽셀마다의 정보를 연산하여 매핑하고 output merger는 최종적으로 모니터에 보이는 모습을 처리합니다.


 

  • Vertex shader overview

Vertex shader의 연산

이번 포스팅에서 알아볼 Vertex shader는 위와 같은 순서로 연산이 진행됩니다. 최종적으로 가장 오른쪽에 clip space라는 출력을 내놓게 되는 것이죠. 우선 world transform은 이전 포스팅에서 언급한 것 같이, 각각 독립된 좌표계를 가진 object space들을 하나의 통일된 좌표계에 위치시키는 것이 world transform입니다.(자세한 내용은 이전 포스팅 참조 바랍니다)


 

 

  • Normal Vector 연산

 

하지만 저번 포스팅에서 다루지 않았던 것이 있는데 바로 world space에서의 좌표계에서의 normal vector 입니다. 각 object가 affine transform을 통해 world 좌표계에 위치하게 되고 모양이 바뀌게 되면 각 object의 surface normal vector와 vertex normal vector 또한 바뀌게 되죠. 여기서 normal vector를 계산할 때는 각 object에 적용한 affine transform matrix [L|t]에서 Linear transform matrix를 그대로 연산한다고 해서 알맞은 값이 나오지 않는 것을 아래 그림에서 확인할 수 있습니다. (translation matrix는 위치만 바꾸므로 normal vector에 영향을 끼치지 않습니다)

Linear transform matrix를 통해 계산한 surface normal vector: 평면과 수직이 아님을 알 수 있다.

그럼 어떻게 해야할까요? 바로 Linear transform matrix의 역행렬로 계산을 해주면 됩니다. 이때 길이를 1로 normalize 한다는 것도 잊으면 안 되는 부분입니다. 아래 그림처럼 Linear transform matrix의 역행렬을 이용했을 때, 변환된 vertex와 수직으로 normal vector의 성질을 유지하는 것을 알 수 있습니다.(증명은 따로 언급하지 않습니다) 이렇게 변환된 object에서 surface normal vector가 연산이 되면, vertex normal vector 또한 손쉽게 연산이 가능하죠(vertex를 공유하는 surface normal vector들의 평균으로 계산)

Linear transform matrix의 역행렬을 통해 계산한 surface normal vector: 평면과 수직을 이룬다.


 

  • 카메라 좌표계(camera space)

다음으로 View Transform에 대해서 알아보겠습니다. 이제 우리는 각 object를 world space에 위치시켰습니다. 하지만 이 좌표계는 단지 지구에 어떤 물체가 있는 것과 같고 우리가 보는 시점(view)을 전혀 반영하지 않습니다. 따라서 우리가 보는 시점에서의 물체는 어떤 위치에 맵핑되어있는지 알아야 하죠. 이를 위해 View Transform을 사용합니다.

 

그래픽스에서는 우리의 눈(혹은 카메라)의 위치를 먼저 world space에서 고려합니다. 이때 카메라의 위치를 EYE로 카메라가 향해있는 특정 포인트(reference point)를 AT 라고 정의하고 마지막으로 카메라가 향해있는 방향과 수직 위로 뻗은 방향을 UP이라고 정의합니다. 

camera space

또한, u,v,n을 카메라 좌표계(camera space)의 3가지 축으로 지칭합니다. 이는 world space에서 x, y, z와 같은 개념이죠. 그림으로 나타내면 아래와 같습니다. 여기서 n는 EYE에서 AT을 가리키는 방향으로 형성됩니다. 그리고 방금 구한 n과 UP방향으로 외적 한 결과를 normalize 한 것이 u가 되죠. 마지막으로 v는 n과 u의 외적입니다. 이때 n과 u는 이미 normalize 되어있기 때문에 따로 크기를 바꿔줄 필요 없이 1이 됩니다. 


 

  • View Transform

이제 여기서 보는 시점에서의 좌표 기준으로 바꿔주어야 하는데요. 1인칭 총 게임에서 한 캐릭터가 보는 기준에서의 사물의 위치 정보를 알아야 하기 때문이라고 이해하시면 좋을거 같습니다. 아래 그림을 보시면, world space에서의 파란 박스의 좌표와 camera space에서 파란 박스의 좌표는 다를 것임을 알 수 있죠.

world space에서의 파란 박스의 좌표와 camera space에서 파란박스의 좌표는 다르다.

카메라가 보는 시점으로 모든 object를 새로 맵핑하는 방법은 간단합니다. 바로 world space의 축 x,y,z와 camera space의 축 u, v, n을 일치시키는 것이죠. 일치시키는 방법 또한 이미 알고 있습니다. 바로 camera position을 world space의 원점으로 affine transform 시키는 것이죠. Rotation과 Translation을 이용해서요.(scaling은 필요 없습니다.)

 

애초에 camera position이 원점으로부터의 좌표로 표현되있기 때문에 Translation의 그 좌표의 부호를 바꿔 역행렬을 구하면 되고, 회전에 대한 역행렬 또한 Object의 좌표계가 World space에서 어디를 향해있는지 알면 Object를 회전시킨 회전 변환 행렬 R을 알 수 있기 때문에 그 역행렬인 R^T를 구하는 것도 어렵지 않습니다. (이전 포스팅에 나와있습니다.) 따라서, 아래와 같이 View Transform을 수행하는 View Transform matrix를 표현할 수 있습니다.

View Transform matrix


 

  • Right-hand System(RHS)와 Left-hand System(LHS)

Right-hand System(RHS)와 Left-hand System(LHS)이 있습니다. RHS은 OpenGL에서 쓰는 방식, LHS은 Direct3D라고 하는데, 하드웨어가 LHS 방식으로 제작되어있어 결국 바꿔주는 연산이 요구되죠. 따라서 이 부분을 언급하고 넘어가겠습니다. 우선 RHS와 LHS의 차이는 xy plane에서 z 축이 시계방향으로 뻗어나가는지 반시계로 뻗어나가는지 차이로 보시면 될 거 같습니다. 이 때문에 아래 그림같이 같은 좌표로 연산하더라도 RHS에서 수행한 연산과 LHS에서 수행한 연산은 반대로 나타나죠.

RHS(위)와 LHS(아래)에서의 시점

RHS에서 LHS로 혹은 반대로 위와 같이 시점이 바뀌지 않게 하기 위해서는 z의 값의 부호를 바꿔주면 됩니다. 아래 그림은 위의 그림의 LHS좌표계에서 z의 부호를 반대로하여 RHS와 통일된 시점을 만들 수 있음을 보여줍니다.

LHS(아래)에서의 RHS(위)로의 변환


 

  • View Frustum 

이제 카메라의 좌표계로 object의 좌표를 맵핑하였으니, 카메라가 보는 물체들을 가려내야 합니다. 시점 바깥의 object가 있을 수도 있고, 일부만 보이는 object들도 있을 수 있기 때문이죠. 아래 그림은 View Frustum을 이해하기 쉽게 보여줍니다.

 

View Frustum은 fovy, aspect, n, 그리고 f 이렇게 4가지 변수가 있는데, fovy는 보는 시야각, aspect는 넓이와 높이의 비율, n은 가장 가까운 시점, f는 가장 먼 시점을 나타냅니다. 이 변수들을 이용하면 아래와 같이 사다리꼴 또는 사각뿔이 잘린 거 같은 공간을 정의할 수 있습니다. 이 안에 있는 object만 camera가 담을 수 있다는 의미이기도 합니다. 이렇게 해서 시야 밖의 object를 없애는 것은 View Frustum Culling이라고 말하고, object의 부분만 취하는 것을 clip이라고 말합니다. 

View Frustum을 보여주는 사진

 

 

  • Projection Transform와 Clip Space

이제 View Frustum을 통해 시점(view)안의 object를 View Frustum Culling 또는 clip을 통해 알아냈습니다. 이제 알아낸 View Frustum을 projection plane에 projection을 할 텐데요. 2D space인 모니터에 어떻게 상이 맺히는지 알기 해서라고 저는 이해했습니다. 하지만 사다리꼴에서 projection을 시키는 것은 조금 복잡할 수 있기 때문에 Projection Transform을 통해 한번 더 Transform을 수행합니다. 이를 통해 정 사각형의 공간에서 object를 표현 할 수 있죠. 이렇게 생긴 정사각형의 공간을 Clip space라고 합니다.  

 

View Frustum(좌)은 Projection Transform을 통해 Clip Space(우)에 맵핑된다.

Clip space를 만들기 위해 사용되는 transform matrix는 아래와 같이 정의됩니다.View Frustum을 위해 사용했던 변수들을 통해 정의된 것임을 알 수 있습니다. 이 transform matrix는 이전과 같은 affine transform matrix가 아닌데요. 마지막 행이 (0,0,0,1)이 아닌 것으로 확인할 수 있죠. 또한 -1로 음의 부호가 붙은 것을 확인할 수 있습니다. 이것은 앞서 설명했던 RHS를 LHS로 바꾸기 위함입니다(이 강의는 OpenGL기준입니다). 또한 저 -1은 원근을 나타내는데 다음 포스팅에서 더 설명하겠습니다.

Projection transform matrix

 


이렇게 Projection transform를 마지막으로 Clip space를 만들어 내는 과정까지 설명을 해봤습니다. 여기까지가 rendering pipeline에서 vertex shader에서 수행하는 연산임을 알아가시면 좋을 거 같습니다.  

re)Vertex shader의 연산


감사합니다.

 

References

강의: [KUOCW] 한정현 교수님 강의

https://www.youtube.com/channel/UCfyXTCv0QlZxG1S1rteGI7A

강의 자료: 한정현 교수님 연구실 홈페이지
http://media.korea.ac.kr/books/