책을 보며 개인적으로 공부하는 내용이므로 틀린 부분이 있을 수 있습니다.
자료 및 내용 출처
<게임 프로그래밍을 위한 3차원 그래픽스>
정점 또는 폴리곤 메쉬에 대한 내용은 이전 글 참고하세요.
정점 처리
그래픽 아티스트에 의해 폴리곤 메쉬가 완성이 되고, 게임 프로그램에 넘어오게 되면 폴리곤 메쉬의 각 정점들이 정점 버퍼에 저장이 된다. 우리의 목표는 폴리곤 메쉬를 단순히 화면에 그리는 것이 아니라, 게임 내 적절한 공간에 배치하는 것이다. 이를 위해선 정점 버퍼에 저장된 정점들을 그대로 사용하면 안되고, 변환을 통해 적절한 공간으로 이동시킨 후에 사용해야 한다. 이처럼 각 정점에 대해 여러 변환을 수행하는 단계를 정점 처리라고 한다.
정점 처리 단계는 프로그래머에 의해 작성된 정점 처리 루틴에 맞게 동작한다. 즉 우리가 정점 처리 코드를 작성하면, GPU에 의해 정점 버퍼에 저장된 모든 정점들이 우리가 프로그래밍한 로직대로 처리(변환)된다. 여기서 작성한 정점 처리 프로그램(함수)를 정점 셰이더(vertex shader)라 한다. 정점 셰이더는 하나의 정점에 대한 처리를 담당한다. 어떤 물체가 24개의 정점으로 이루어져 있다면 정점 셰이더는 24번 호출된다. 정점 처리는 GPU의 렌더링 파이프라인 중 한 단계에 해당한다.
파이프라인이란 대단한 것이 아니라 그냥 한 단계의 출력이 다음 단계의 입력으로 이어지는 특징을 가진 구조를 말한다. 컴퓨터 그래픽스에서 렌더링이란 3차원 공간에서 정의된 폴리곤 메쉬를 여러 단계에 거쳐 최종적으로 우리가 보는 모니터, 즉 2D 화면상에 그리는 것을 의미한다. 따라서 렌더링 파이프라인이란 3D 물체를 2D 화면에 그리는 여러 단계를 일컫는 말이다. 렌더링 파이프라인을 프로그래머가 관여하는 주요 단계만 나타내면 다음과 같다.
위 과정은 순서대로 정점 처리, 래스터화, 프래그먼트 처리, 출력 병합이다. 우선 가장 처음엔 이전에 살펴본 폴리곤 메쉬가 게임 프로그램의 입력으로 넘어오게 된다. 그러면 각 정점에 대해 정점 처리를 수행한다. 그 결과(출력)를 입력으로 받아 래스터화를 진행한다.
래스터(raster)라는 의미는 픽셀(pixel)로 이루어진 이미지를 말한다.(bmp, jpg와 같은 이미지) 따라서 래스터화는 정점으로 이루어진 폴리곤의 내부를 픽셀로 채워 래스터 이미지로 만드는 것이라고 해석할 수 있다. 다만 해당 단계에선 픽셀이 아닌 프래그먼트(fragment)라는 것으로 폴리곤을 채운다. 픽셀이 최종적으로 색이 결정되어 우리에게 보이는 조각이라면, 프래그먼트는 색상이 정해지기 전 non-color의 픽셀이라 생각하면 된다.(컬러를 가질 수도 있다) 즉 뼈대와 비슷한 느낌이다. 참고로 Direct3D에선 프래그먼트와 픽셀을 모두 픽셀이라 부른다고 한다. 하지만 혼동하진 말자. 한편, 래스터화를 수행하는 래스터라이저(rasterizer)는 프로그래밍이 불가능하며 그 규격과 기능이 하드웨어로 고정되어 있다.
프래그먼트 처리 단계에선 정해진 각 프래그먼트에 대해 실제로 색상을 결정한다. 해당 단계는 프래그먼트 셰이더(fragment shader) 혹은 픽셀 셰이더(pixel shader) 라 불리는 프로그램에 의해 수행된다. 이 셰이더 또한 정점 셰이더와 마찬가지로 프로그래밍 가능하며 우리가 코드를 작성하면 GPU에 의해 실행된다. 픽셀 수만큼 호출된다.
마지막으로 출력 병합 단계에선 프래그먼트와 결정된 색상을 결합, 병합 또는 섞어(blending) 최종적인 출력물을 얻는다. 이 단계는 래스터화 단계랑 마찬가지로 프로그래밍이 불가능하며 고정되어 있다. 이처럼 고정되어 있는 단계들의 기본적인 구조를 변경할 순 없지만, 몇몇 파라미터를 통해서 그 세부 기능들을 조절하는 것이 가능하다.
정점 처리 과정
파이프라인의 첫 단계인 정점 처리는 여러 과정을 거쳐 출력물을 생성한다. 정점 처리는 다음과 같이 세 번의 변환을 통해 최종적인 정점을 생성한다.
오브젝트 공간(object space)
아직 어떠한 변환도 되지 않은 상태의 폴리곤 메쉬들이 존재하는 공간을 말한다. 아까 처음에 말한 것 처럼 폴리곤 메쉬의 정점들은 초기 위치에서 실제 게임 화면에 출력될 의미있는 위치로 변환되어야 한다고 했다. 여기서 초기 위치가 오브젝트 공간을 의미한다. 오브젝트 공간은 폴리곤 메쉬에 대한 개인적인 공간이다. 여기서 공간이란 모두 좌표계를 의미한다. 오브젝트 공간은 로컬 공간(local space)이라고도 한다.
월드 공간(world space)
오브젝트 공간에서 월드 변환(world transform)을 통해 월드 공간으로 이동된다. 월드 공간은 여러 폴리곤 메쉬들이 함께 존재하는 의미있는 공간을 말한다. 즉 개인적으로 흩어져있던 폴리곤 메쉬들이 하나의 공간에서 관계를 형성하기 위해 하나의 좌표계에 모이는 곳이다. 아래 그림은 각각의 오브젝트 공간에서 월드 공간으로 변환된 예이다.
카메라 공간(camera space)
카메라 공간은 뷰 공간(view space)라고도 하며, 월드 공간의 정점들이 뷰 변환(view transform)을 통해 해당 공간으로 이동한다. 카메라 공간이란 쉽게 말하면 카메라가 보는 시점에서의 상대적인 좌표들이다. 예를 들어 월드 공간의 메쉬들은 위에서 보는 모양과 아래서 보는 모양과 옆에서 보는 모양이 다를 것이다. 게임에서 자신의 캐릭터가 어떤 물체를 바라볼 때 매번 달라지는 모습을 생각하면 이해하기 쉽다. 따라서 특정 시점에서의 새로운 공간(좌표계)으로 변환한다.
클립 공간(clip space)
카메라 공간에서 물체들을 바라볼 때 모든 물체를 볼 순 없다. 즉 볼 수 있는 시야가 제한되어 있다. 이처럼 시야를 정하고, 보이지 않는 물체들을 잘라내어(culling) 최종적으로 우리 시야에서 보이는 물체들만 위치한 공간을 클립 공간이라 한다. 카메라 공간의 정점들이 투영 변환(projection transform)에 의해 마지막 공간인 클립 공간으로 변환된다.
월드 변환(world transform)
정점 처리의 첫 변환은 월드 변환이다. 월드 변환은 축소확대(scaling), 회전(rotation), 이동(translation)를 이용하여 정점을 변환한다. 축소확대는 말 그대로 좌표에 실수배를 하여 축소 및 확대를 한다. 회전은 임의의 세타만큼 회전을 시킨다. 이동은 각 좌표에 값을 더하거나 빼서 위치를 이동시킨다.
축소확대와 회전은 기준이 고정되어 있는 상태에서 변환되고, 변화가 선형적인 특성을 가지기 때문에 선형 변환(linear transform)이라 한다. 반면에 이동은 기준의 위치가 변하기 때문에 선형 변환이 아니다. 그리고 선형 변환과 이동 변환을 함께 수행하는 것을 아핀 변환(affine transform)이라 한다.
이러한 변환들은 행렬을 통해 표현된다. 벡터 v를 30도 만큼 회전하고 싶다면 벡터에 30도 회전 행렬을 곱한다. 축소확대와 회전을 같이 적용하고 싶다면 축소확대 행렬과 회전 행렬을 곱하고 그 결과 행렬을 v에 곱해주면 된다.
축소확대(scaling)
위 연산은 벡터 (x,y,z)를 각각 sx, sy, sz만큼 실수배를 하여 축소 또는 확대를 한다. 참고로 위 식은 행렬x열벡터 순서인데, 이는 열벡터를 사용하는 OpenGL의 방식이다. Direct3D에선 행벡터를 사용하기 때문에 행벡터x행렬의 순서가 된다. 여기에 나오는 행렬들은 모두 열벡터 기준이므로 행벡터 기준과 행렬의 모양이 다를 수 있다.
회전(rotation)
위 행렬들은 각각 z, x, y축을 중심으로 세타만큼 회전시키는 행렬이다. 여기서 증명은 따로 하지 않는다. 축소확대와 마찬가지로 열벡터와 곱해지기 때문에 행벡터 기준의 행렬과는 모양이 다른 부분이 존재한다.
이동(translation)
이동은 다음과 같이 기존 좌표에 상수를 더하여 벡터를 이동시킨다. 축소확대 및 회전 변환과의 다른 점은 행렬과 벡터의 곱셈으로 표현이 안된다는 것이다.
(x', y', z') = (x+a, y+b, z+c)
행렬의 곱으로 각 좌표에 상수를 더하기 위해선 어쩔 수 없이 값 하나를 추가하여 4x4 행렬과 4열을 가진 열벡터를 만들어야 한다.
위와 같이 벡터의 마지막 행인 1이 dx, dy, dz와 곱해져 각 좌표에 더해질 수 있다. 그리고 이처럼 n차원 좌표를 n+1개의 좌표로 나타내는 것을 동차 좌표(homogeneous coordinates) 라 한다.
여러 변환들은 곱셈을 통해 최종적으로 하나의 행렬을 만들어서 벡터에 곱하게 된다. 그런데 이동 행렬은 동차 좌표계를 사용하므로 축소확대와 회전 또한 동차좌표계를 사용해야 서로의 곱셈이 가능하다. 따라서 모든 변환 행렬은 4x4 행렬을 사용하게 된다. DirectX와 같이 행벡터를 이용할 경우 해당 행렬의 전치 행렬이 사용될 것이다.
월드 행렬
지금까지 보았던 변환들로 오브젝트 공간의 메쉬들을 월드 공간으로 이동시킬 수 있다.
예를 들어 위 그림의 두 물체는 각각의 오브젝트 공간에서 하나의 월드 공간으로 이동되었다. 구체는 크기가 확대되었고, 주전자는 회전과 이동이 함께 적용되었다. 구체는 단순하게 y축의 크기가 2배만큼 증가하였고, y축을 2배만큼 확대하는 행렬이 구체에 대한 월드 행렬이 된다.
주전자에 대한 월드 행렬은 회전과 이동에 대한 행렬이다. 회전은 y축을 중심으로 회전하였고, x축 방향으로 이동하였다. 따라서 두 행렬을 곱해주면 주전자에 대한 월드 행렬이 된다. 행렬의 곱셈은 S->R->T, 즉 축소확대 -> 회전(z->x->y) -> 이동 순으로 곱해야 한다. 열벡터는 행렬의 뒤에 위치하므로 곱할 때 순서를 주의하자. 그리고 회전 시 회전하는 방향은 오른손 좌표계에선 반시계 방향이며, 왼손 좌표계에선 시계 방향이다. 예를 들어 90도 회전이면 반시계 방향으로 회전하며, -90도일 경우 시계 방향으로 회전한다.
이동을 먼저하게 되면 기준점인 원점이 다른 좌표로 이동하게 된다. 이는 이후 다른 연산에 영향을 미치게 된다. 예를 들어 사각형 중 한 점이 원점이라고 할 때, 2배 확대를 하면 원점을 포함하여 크기만 2배가 되는 것을 기대할 것이다. 또한 원점에 어떠한 행렬을 곱해도 원점은 변하지 않는다. 그러나 이동이 된 상태, 즉 원점을 포함하지 않는 상태에서 2배 확대를 하게 되면 단순히 크기만 확대되는 것이 아니라 위치를 이동하게 된다.
한편, 변환되기 전의 노멀들은 변환 후에 수직 관계를 그대로 유지할 수도 그렇지 않을 수도 있다. 예를 들어 월드 행렬에 비균등 축소확대가 포함될 경우 수직 관계를 유지하지 않는다. 비균등 축소확대란 각 좌표에 동일한 실수배가 아닌 각기 다른 실수배를 말한다. 만약 이 변환을 포함하지 않으면 그대로 유지가 된다. 월드 행렬을 M이라고 했을 경우, 비균등 축소확대가 포함되었다면 노멀에는 다음과 같이 M이 아닌 M의 역행렬에 대한 전치행렬을 곱해주어야 한다.
위 그림에서 첫 번째 월드행렬은 비균등 축소확대 행렬이다. 이 행렬을 그대로 노멀에 곱할 경우 수직이 유지되지 않는다. 두 번째 월드행렬은 첫 번째 행렬에 역을 취하고 전치를 취한 행렬이다. 그 결과 변환된 직선에 수직이 유지되는 것을 볼 수 있다.
뷰 변환(view transform)
하나의 월드 공간으로 변환된 메쉬들을 특정 시점에서 보기 위해 뷰 변환을 통해 카메라 공간으로 이동시켜야 한다. 뷰 변환은 카메라 변환이라고도 하는데, 목적은 메쉬들을 카메라의 시점에서 보기 위함이다. 쉽게 말하면, 물체들의 위치는 그대로인데, 관찰자의 시점이 달라지면 보이게 되는 장면도 달라지는 것은 당연하다. 관찰자인 카메라의 시점에서 보이는 화면을 표현하기 위해 뷰 변환을 적용한다. 다음은 카메라 공간과 월드 공간을 표현한 그림이다.
위 오른쪽 그림을 보면 하나의 그림에 2개의 공간이 있다. 두 공간은 원점을 기준으로 분리된다. 따라서 월드 공간에서의 원점과 카메라 공간에서의 원점이 존재한다. 우선 월드 공간은 원점 O와 e1, e2, e3라는 표준이면서 정규 직교 기저로 정의된다. 즉 {O, e1, e2, e3}
가 3차원 월드 공간을 이룬다. 카메라 공간은 원점 EYE와 u, v, n이라는 비표준 정규 직교 기저로 정의된다. 즉 {EYE, u, v, n}
이 3차원 카메라 공간을 이룬다.
EYE는 월드 공간에서의 카메라 좌표(정점)를 말하며, AT은 카메라의 시점에서 바라보는 지점의 좌표(정점)로 위 그림은 주전자에 설정되어 있다. UP은 카메라 상단의 방향을 나타내는 벡터이며 보통 y축으로 설정된다. 위 공식들을 보면 카메라 공간의 세 기저를 구할 수 있다.
n 벡터는 AT에서 EYE로의 방향과 크기를 가지는 벡터이다. 추가로 자신의 크기로 나누어 정규화를 한다. 그리고 UP벡터와 n벡터를 외적한 뒤 정규화를 한다. 해당 연산의 결과는 UP과 n이 이루는 평면에 대한 노멀, 즉 u 벡터가 나온다. 마지막으로 n과 u를 외적하여 n과 u가 이루는 평면에 수직인 벡터 v를 생성한다. v를 만드는 두 벡터가 단위 벡터이므로 이 경우 정규화는 따로 하지 않아도 된다. 정리해보면 세 벡터 u,v,n은 서로 수직이며 크기가 1인 정규 직교 기저이다.
아래 그림을 예로 월드 공간의 물체들을 뷰 공간으로 이동시켜 보자.
우선 현재 보이는 좌표는 모두 월드 공간에서의 좌표이다. EYE는 카메라 공간을 의미하지만 월드 공간에서의 카메라 공간의 위치이다. 카메라가 바라보는 지점인 AT 또한 월드 공간에서의 좌표인데 이 지점을 카메라 공간에서의 좌표로 변경해보자.
처음에 n이라는 벡터를 만들 때 AT에서 EYE를 향하는 방향으로 만들었다. 따라서 AT과 EYE는 n축에 존재한다. n축에 존재한다는 것은 나머지 좌표는 0이라는 뜻이다. 또한 EYE는 카메라 공간에서의 원점이므로 카메라 공간에서의 좌표는 (0,0,0)이다. AT은 n축에 존재하므로 카메라 공간에서 (0,0,n)을 가질 것이다. 그런데 -n축으로 √101만큼 떨어져 있으므로 AT의 좌표는 (0,0,-√101)을 가지게 된다. 즉 (0,0,-√101)이 카메라 공간에서의 AT 좌표이다.
그러나 위와 같은 방법은 여러 개의 메쉬에 대해선 비효율적일 수 있다. 따라서 생각을 바꿔서 더 효율적인 방법으로 계산한다. 우선 카메라 공간과 월드 공간에서의 물체들의 좌표가 다른 이유는 두 공간의 원점이 서로 다르기 때문이다. 따라서 이 방법은 카메라 공간인 {EYE, u, v, n}
를 월드 공간인 {O, e1, e2, e3}
로 정확히 일치시키는 것을 목표로 한다. 물론 카메라 공간만 이동시키면 안된다. 카메라 공간에 적용되는 변환을 월드 공간의 물체들에게도 동일하게 적용한다. 즉 카메라 공간과 나머지 물체들이 서로 그룹화되어 있다고 가정하고 동일하게 움직이도록 하는 것이다.
우선 카메라 공간의 원점을 월드 공간의 O로 일치시킨다. 이를 위해선 u축을 -20만큼, v축을 -3만큼 이동 변환하면 된다. 그러면 u,v,n과 x,y,z의 방향은 다를지라도 원점은 일치된 상태가 된다. 이동 변환을 행렬로 나타내고, AT에 그대로 적용해주면 다음과 같다.
그리고 아래와 같이 기저의 방향을 제외하면 원점이 일치하는 것을 볼 수 있다.
이제 월드 공간과 카메라 공간을 정확히 일치시키려면 회전 변환을 적용해야 한다. 따라서 회전 각도를 계산해야 하지만, 카메라 공간의 기저가 정규 직교 기저라면 각도를 알 필요가 없다.
만약 카메라 공간의 기저가 정규 직교 기저라면 다음과 같이 각 기저를 행으로 배치하여 행렬을 만들면 그것이 바로 회전 행렬이 된다. (여기서 따로 증명은 하지 않는다.)
u,v,n의 좌표는 주어지는 EYE,UP,AT을 통해 구할 수 있으므로 우리는 회전 행렬을 얻은거나 마찬가지이다. 이제 이동(transform)과 회전(rotation)을 통해 한 공간을 다른 공간과 일치시켰다. 즉 한 공간을 다른 공간으로 이동시킨 것이다. 이러한 공간 이동을 공간 이전(space change)이라 한다.
이처럼 뷰 변환을 수행하는 행렬은 이동과 회전의 조합으로 표현된다. 다음과 같이 이동 행렬과 회전 행렬을 합성하면 카메라 공간으로 변환되는 뷰행렬이 완성된다. 행벡터를 이용할 경우 아래 행렬의 전치 행렬을 뷰행렬로 사용하면 된다.
투영 변환(projection transform)
이제 특정 시점에서의 공간이 완성되었다. 이제 시야에 들어오지 않는 물체들을 없애고, 최종적으로 화면에 보일 물체들만 존재하는 클립 공간으로 변환해야 한다. 또한 멀리있는 물체를 작게 표현하는 원근법을 실현하게 된다. 이러한 작업을 수행하는 변환을 투영 변환이라 한다. projection 혹은 perspective transformation이라고도 한다. 우선 투영 변환을 하기 전 시야 밖의 물체들을 제거하기 위한 사전 작업이 필요하다.
우선 카메라가 볼 수 있는 시야(가시 영역)을 뷰 볼륨(view volume)
이라 한다. 뷰 볼륨은 다음과 같이 fovy
, aspect
, n
, f
네 가지 파라미터로 구성된다.
- fovy: y축에 따른 시야각(field of view)을 의미한다.
- aspect: 가로(width)와 세로(height)의 비율(종횡비)을 의미한다.
- n: 해당 점이 존재하는 지점의 세로를 전방(near) 평면이라 하는데, n은 원점으로부터의 거리를 의미한다.
- f: 해당 점이 존재하는 지점의 세로를 후방(far) 평면이라 하는데, f는 원점으로부터의 거리를 의미한다.
위와 같은 뷰 볼륨은 -z축 방향으로 무한한 피라미드를 그린다. 무한한 피라미드는 전방 평면과 후방 평면에 의해 오른쪽 그림과 같이 유한한 공간으로 잘리게 되는데 이 부분을 뷰 프러스텀(view frustum)
혹은 절두체
라고 한다. 뷰 프러스텀 바깥에 존재하는 구와 원기둥은 시야 바깥 쪽에 있다고 간주하고 최종 화면에 나타내지 않도록 제외한다. 이처럼 뷰 프러스텀 영역 밖의 그릴 필요가 없는 물체를 제거하는 작업을 뷰 프러스텀 컬링(culling)
이라 한다. 뷰 프러스텀 컬링 알고리즘은 사용자가 작성하며, CPU에 의해 실행된다. 만약 컬링 알고리즘이 제공되지 않으면 카메라 공간의 모든 물체가 GPU 파이프라인의 입력으로 들어온다. 이 경우 시야 밖의 물체들은 잠시 후 볼 클리핑에 의해 제거된다.
뷰 프러스텀 컬링을 위해 보통 다음과 같이 폴리곤 메쉬를 감싸는 작업을 하는데, 이를 바운딩 볼륨이라 한다.
바운딩 볼륨은 폴리곤 메쉬의 디테일한 테두리 부분에 대한 검사를 쉽게 하도록 도와준다. 예를 들어 캐릭터끼리 접촉하는 조건을 검사할 때, 곡선이 너무 많으므로 검사가 복잡하고 애매할 수 있다. 이때 캐릭터에 직사면체를 바운딩 볼륨으로 활용함으로써 직사면체와의 충돌을 검사하도록 하면 더욱 간단해진다.
이러한 컬링 작업은 GPU로 하여금 메쉬를 그만큼 그리지 않도록 하므로, 렌더링의 성능을 높이는 중요한 작업 중 하나이다. 예를 들어 건물 뒤에 10명의 사람이 존재하는데 건물 앞의 시야에선 사람이 보일 필요가 없으므로, 애초에 그리지 않는다.
다음으로 아까의 주전자를 자세히 보면 손잡이 또한 뷰 프러스텀의 바깥 쪽에 있는 것을 볼 수 있다. 이러한 부분 역시 잘리게 되는데, 이처럼 메쉬의 일부분을 자르는 것을 클리핑(clipping)
이라 한다. 클리핑은 컬링처럼 미리 제외되는 것이 아니라, 투영 변환 후 클립 공간에서 수행된다.
뷰 프러스텀 컬링과 클리핑의 차이?
컬링은 메쉬 자체를 제외시키는 것이고, 클리핑은 컬링에서 1차적으로 걸러지고 난 후의 메쉬에 대해 수행하며, 주로 메쉬의 일부분인 폴리곤을 잘라내는 작업을 한다. 컬링은 해당 메쉬가 시야를 완전히 벗어나는지만 검사하면 되지만, 클리핑은 추가로 어느 영역이 벗어나는지를 검사해야 하기 때문에 컬링에 비해 더 많은 오버헤드를 필요로 한다. 또한 컬링은 주로 정적인 물체에 적용한다면, 클리핑은 동적인 물체에 적용한다.
그런데 뷰 프러스텀에 약간의 문제점이 있다. 위와 같이 피라미드 형태의 프러스텀에서 영역 밖의 폴리곤을 검사하는 것은 쉽지 않다. 피라미드는 비스듬한 평면이 많기 때문이다. 따라서 아래와 같이 뷰 프러스텀 내의 물체들을 모든 면이 좌표계와 평행한 직육면체와 같은 공간 내로 이동시킨다.
위와 같이 뷰 프러스텀 내의 물체들을 직육면체 공간(뷰 볼륨)으로 변환시키는 것을 투영 변환(projection transform)
이라 한다. 이러한 투영 변환은 뷰 프러스텀 내의 물체에도 동일하게 적용된다. 변환 후 직육면체 공간을 클립 공간(clip space)이라 한다. 이는 클리핑을 위한 공간이기 때문에 클립 공간이라 불린다.
두 공간에 대한 물체의 단면을 보기 위해 아래와 같이 뷰 프러스텀의 단면도를 생각해보자.
변환 전의 뷰 프러스텀인 왼쪽을 먼저 보자. COP라는 원점에 카메라가 존재한다고 할 때, 이 원점으로 수렴하는 선들을 투영선이라 한다. 투영선은 픽셀 하나에 대응된다. 즉 평면의 크기가 10x10이라면 100개의 투영선이 생성된다.(가상이지만) 그리고 y축과 평행한 파란 선을 투영 평면이라 한다. (또한 가상이다)
여기서 투영 변환을 수행하게 되면 뷰 프러스텀의 단면도는 오른쪽과 같이 직육면체(혹은 정육면체)의 단면도로 변하게 된다. 이에 따라 투영선 또한 서로 평행해지며, 물체의 모양 또한 변하게 된다. 변환 전의 주전자의 일부 길이인 l2와 l1을 보면 멀리 있는 l2가 더 긴 것을 확인할 수 있다. 그러나 변환 후 l2와 l1은 동일한 크기가 되었다.
이는 멀리 떨어진 물체는 더 작게, 가까이 있는 물체는 크게 보이는 원근 효과를 구현한 것이다. 3차원 자체에서 원근법을 실현하긴 했지만 투영 변환이란 이름과 달리 2차원으로 변환시키는 것은 아니다. 여전히 3차원 물체라는 것을 기억하자.
투영이란?
투영(사영)이라는 것은 어떤 대상을 3차원 또는 2차원과 같은 공간 상에 비쳐내는 행위를 말한다.
수학에서의 정사영(직교 투영)과 비슷한 의미이다.
하지만 위에서 말했듯 아직 실제로 2D 화면에 투영하는 것은 아니다.
이러한 투영 행렬은 다음과 같의 정의된다.
이러한 투영 행렬을 통해 마지막 공간인 클립 공간으로 이동된 메쉬들은 이제 래스터화 단계로 넘어가게 된다. 그러나 래스터라이저는 모든 입력이 왼손 좌표계에 의해 정의된 공간이라는 가정 하에 작업을 수행한다. 여기선 오른손 좌표계를 기준으로 변환했기 때문에 미리 왼손 좌표계로 변환을 해주어야 한다. 이를 위해선 간단하게 z 좌표의 부호만 변경해주면 된다. 따라서 위 행렬의 z 좌표에 곱해지는 3행의 모든 부호를 변경해준다. (z 값을 뷰볼륨의 깊이라는 양수의 값으로 사용하기 위함)
뷰 프러스텀같은 것은 이해를 돕기 위한 것이며, 실체화 되지 않는다. 즉 투영 변환은 폴리곤 메쉬에만 적용된다.
'게임 공부 > Computer graphics' 카테고리의 다른 글
4 - 프래그먼트 처리(Fragment processing) (0) | 2020.08.10 |
---|---|
3 - 래스터화(Rasterization) (5) | 2020.08.07 |
빠른 렌더링을 위한 오브젝트 제외 기술(유영천님 발표) (0) | 2020.08.02 |
1 - 폴리곤 메쉬(Polygon mesh) (0) | 2020.07.13 |
게임 개발을 위한 선형대수학 기본 정리 (1) | 2020.07.12 |
댓글