본문 바로가기
대학생활/수업

게임그래픽프로그래밍 12주차 - 렌더링의 활용

by se.jeon 2023. 6. 5.
728x90
반응형

지난 시간의 복습

- 뷰공간

- 뷰포트

- 스크린 공간

- 2D 렌더링 파이프라인과 공간

- 3D 렌더링 파이프라인과 공간

- Local Space > Model Matrix > World Space > View Matrix > View Space > Projection matrix > Clip Space > Viewport Transform > Screen Space

- 지난 시간에는 View Space까지 수업을 진행함.

- 오른손 좌표계를 사용하는 이유

- Yaw, Pitch, Roll

- Z축 회전 > X축 회전 > Y축 회전

- 오일러각의 회전 공식은 절대적인 것이 아니다.

 

렌더링의 활용

Clip 공간

- 뷰 공간에서 화면에 투영하기 위해서 사용되는 공간

- 객체들의 좌표 값은 정규화 되어 있으며 -1, 1범위이다.

- View 공간과 Clip 공간의 관계

 

투영 변환 (Projection Transform)

뷰 공간에서 클립공간으로 변환하는 것

 

투영 행렬 (Projection Matrix)
투영 변환을 위해서 사용되는 행렬  

 

사용되는 구성

- 카메라의 시점 : 카메라의 위치

- 카메라의 시야각 : FOV(File of View), 시야로 보이는 범위 (카메라는 일반적으로 좌우, 상하 시야각을 동일하게 사용한다.)

- 카메라의 종횡비 : aspect ratio, 가로 & 세로 비율 (가로 길이 / 세로 길이)

 

그래픽스에서의 FOV의 차이

- FOV가 클수록 화면에 표시되는 가시 범위가 증가하고, 주변 환경에 중점을 두게 된다.

- FOV가 작으면 세부적인 환경에 중점을 두게 된다.

- FOV는 일반적으로 좌우, 상하 각이 동일하다.

FPS 게임에서 굉장히 중요한 개념. 오버워치와 같은 경우 103정도가 기본 값으로 지정됨.

 

그래픽스에서의 aspect ratio 종횡비의 차이

- 디스플레이의 차이에 따라서 종횡비의 종류도 다양하다.

- 과거의 종횡비는 4:3이었고, 최근의 대중적인 종횡비는 16:9(1920*1080)이다.

- 종횡비에 따라서 보여지는 화면이 더 넓게 보이거나 길게 보인다.

 

투영 평면(Projection plane)

- 3D 객체들을 2D로 보이기 위해 투영하는 평면
- 카메라 시점과 수직이며, 카메라의 거리 및 방향에 따라 크기가 변화한다. (초점 거리 : focal length)
- 상하좌우의 범위가 -1~1(정규화)로 지정되어 있으며, 따라서 투영 평면의 width, height는 2이다.
- 투영 평면은 정사각형의 모습으로 객체들을 투영시킨다.


NDC (Normalized device coordinate, 정규화 장치 좌표)

- 화면 공간의 위치와 관계없이 일관된 좌표 시스템을 제공하기 위해 사용되는 컴퓨터 그래픽스 좌표 시스템 중 하나.

- 직교 좌표계처럼 가운데가 원점인 좌표 시스템이다.

- 투영변환(Projection transformation)과 *클리핑(Clipping) 단계 이후에 적용되는 좌표계이다.
- 객체들의 투영된 좌표를 정규화 하여 NDC 좌표로 변환한다.

- 일반적으로 컴퓨터 화면에 렌더링 되는 객체의 최종 위치를 나타낸다.

- 화면 해상도나 종횡비와 같은 디스플레이 특성에 독립적이기 때문에 다양한 장치에서 일관된 그래픽 출력을 얻을 수 있다.

* Clipping : 투영 변환된 객체의 일부가 화면의 경계를 벗어나는 경우 해당 부분을 제거하는 과정. 화면에 표시될 영역을 정의한다.

 

투영평면의 크기 고정시 FOV에 따른 초점 거리를 구하는 방법

초점 거리가 d, FOV를 θ, 투영평면의 높이를 h라고 할 때,

tanθ/2 = (h/2)/d = 1/d

초점거리 d를 기준으로 정리했을 때

d = 1/tan(θ/2)

 

뷰 공간의 점이 투영평면으로 투영되는 과정

직선의 방정식의 기울기를 구하는 것과 같다.

뷰 공간 상의 점 : P1
투영평면 위의 점 : P2
// YZ축 기준
// Y = 0, x, z축만을 고려해서 간략화

P1 = (P1x, 0, P1z)
P2 = (P2x, 0)

-d:P2x = P1z:P1x (d>0)
P2x = (-d*P1x)/P1z (d>0)
// XZ축 기준
// x = 0, y, z축만을 고려해서 간략화

P1 = (0, P1y, P1z)
P2 = (0, P2y)

-d:P2y = P1z:P1y (d>0)
P2y = (-d*P1y)/P1z (d>0)

 

// 뷰 공간의 점이 투영평면으로 투영되는 최종점 P2
P2 = (P2x, P2y) = (-d*P1x)/P1z, (-d*P1y)/P1z
                = d/-P1z(P1x, P1y)

- 투영 평면으로 계산 후에는 NDC 좌표로 수정 후 최종 화면으로 표시하는 작업의 진행이 필요하다.

 

NDC to Screen

- NDC 좌표를 최종적으로 스크린 화면의 크기에 맞게 조정 작업을 진행해야 한다.

- 스크린 화면에서 가로 세로 비율이 1:1이 아니면 정상적으로 출력되지 않는다.

- 스크린 종횡비의 한쪽 비율이 크면 객체가 눌린 상태로 표현되는 문제가 발생한다.

- NDC에서 스크린의 종횡비를 미리 역으로 적용시킨 후 스크린 화면으로 출력하는 방식으로 해결할 수 있다.

 

종횡비의 역적용 예시

example 스크린 w:1280, h:720/ 종횡비 가로(w)/세로(h) = 1.7777...

NDC에 적용하기 위해서는 스크린 종횡비의 역수를 계산해야 한다. (example : 세로/가로 = 0.5625)

- 스크린 종횡비 : k, NDC에 적용하기 위한 종횡비 역수 : 1/k
- 좌우로 NDC를 변화시키려면 x축의 값만 변경이 필요하다.

- 투영되는 최종점에 종횡비 역수를 x축에 적용한다.

P2 = (P2x, P2y) = d/-P1z * (P1x/k, P1y)

 

NDC까지 적용한 최종 투영 행렬 P 구현

P2 = P·P1 = (d/-P1z) * (P1x/k, P1y) (P1z < 0)

           [ (d/-P1z)*(1/k)  0      ] · [P1x]
          =[ 0               d/-P1z ]   [P1y]

위와 같이 뷰 공간의 점 P1과 투영변환 NDC의 점 P2에 대해서 P 정리가 가능하지만, 만들어진 투영행렬 P가 뷰 공간의 P1의 z값을 사용하므로 뷰 공간의 점 P1을 변환할 시 행렬을 새롭게 생성해야 하는 단점이 있다.

 

- 투영 행렬 P의 0이 아닌 원소들이 분모로 P1z를 가지고 있다.

따라서 이를 최적화하여 기존 투영 행렬에서 P1z을 제거하고 결과값에 P1z을 나누기 위해 3x3 행렬로 확장한다.

P·P1 = [d/k  0   0 ]·[P1x]·[(d/k)·P1x]
       [0    d   0 ] [P1y] [    d·P1y]
       [0    0   -1] [P1z] [     -P1z]
       
Pclip = ((d·P1x/k),        d·P1y,        -P1z)
Pndc  = ((d·P1x)/(-k·P1z), (d·P1y)/-P1z, 1   )

 

원근 투영의 이해

깊이가 없을 경우 뒤에 있는 오브젝트가 앞에 있는 오브젝트를 가리거나 크기가 다르게 나타나게 된다.

 

깊이값(deapth)에 따른 원근 투영

- 깊의 값의 정의는 객체와 카메라의 거리를 나타내는 값이다.

- 카메라와 멀어질 수록 깊이값은 커진다.

- 3차원 좌표계에서 일반적으로 z축을 기준으로 측정된다.

 

원근 투영 (Perspective Projection)

- 투영 방법 중에 하나로, 현실처럼 객체가 멀리 있을 수록 작아져 보인다.

- 3D 그래픽스에서 대중적으로 가장 많이 사용되는 투영 방법이다.

 

원근 투영으로 생긴 NDC

- 카메라를 기준으로 NDC가 구성되며 z값은 (-1, 1)까지로 구성된다.

- width, height, depth의 크기가 2인 정육면체이다.

- 정육면체의 중심에 카메라가 위치 해 있다.

- 카메라보다 멀어지면 z값이 커져야 하므로 왼손 좌표계 구조를 가진다.
- 월드 좌표계와 뷰좌표계는 오른손 좌표계
- NDC 좌표계는 왼손 좌표계

 

깊이를 구하는 과정

- 3x3 투영 행렬을 깊이 값을 구하는 용도를 위해 4x4 행렬로 확장한다.

P·P1 = [d/k   0     0     0  ]·[P1x] = [(d/k)·P1x]
       [0     d     0     0  ] [P1y]   [    d·P1y]
       [a31   a32   a33   a34] [P1z]   [         ]
       [0     0     -1    0  ] [1  ]   [     -P1z]

- 깊이 값은 뷰 좌표계의 x축과 y축에 직교하므로 x, y에 대한 고려가 불필요하다.

P·P1 = [d/k  0    0    0   ]·[P1x]=[(d/k)·P1x]
       [0    d    0    0   ] [P1y] [    d·P1y]
       [0    0    a33  a34 ] [P1z] [         ]
       [0    0    -1   0   ] [1  ] [     -P1z]

 

뷰 공간에서의 좌표와 클립좌표, NDC 좌표

// 절두체에서 near plane의 거리를 n, far plane의 거리를 f로 설정한다.
// 뷰 공간에서의 near plane상의 위치 점의 좌표 Pn=(0, 0, -n, 1)
// 뷰 공간에서의 far plane상의 위치 점의 좌표 Pf = (0, 0, -f, 1)

Pn = [d/k 0    0    0   ]·[0 ]=[0]
     [0   d    0    0   ] [0 ] [0]
     [0   0    a33  a34 ] [-n] [ ]
     [0   0    -1   0   ] [1 ] [n]

Pf = [d/k 0    0    0   ]·[0 ] [0]
     [0   d    0    0   ] [0 ] [0]
     [0   0    a33  a34 ] [-f] [ ]
     [0   0    -1   0   ] [1 ] [f]
// NDC 좌표에서는 near plane은 (0, 0, -1), far plane은 (0, 0, 1)에 대응한다.

- 클립좌표의 네 번째 원소를 모든 원소로 나눈 값이 NDC 좌표이다.

- NDC 좌표의 z값은 -1과 1이다.

- near plane의 클립 좌표는(0, 0, -n, -n), far plane의 클립좌표는 (0, 0, f, f)

Pn = -a33n + a34 = -n
Pf = -a33f + a34 = f

둘을 서로 빼서 a33을 구한다.
a33(f-n) = -f -n
a33 = (n+f)/(n-f)

-a33n + a34 = -n에 구한 a33을 대입하면
((n+f)/(n-f))*-n + a34 = -n이 나오게 된다.

a34 = -n + ((n+f)/(n-f))*n
= (-nn+nf+nn+nf)/(n-f)
= 2nf/(n-f)

분자와 분모의 부호는 결과적으로 합쳐지기 때문에 왔다 갔다 할 수 있다.

최종 행렬 P = [d/k   0      0           0        ]
            [0     d      0           0        ]
            [0     0      (n+f)/(n-f) 2nf/(n-f)]
            [0     0      -1          0        ]

좌표계에 대한 고민을 해야 한다.

clip과 ndc는 바깥쪽 z가 양의 방향이다.

near plane과 far plane이 두개 있을 경우에 view 공간에 있는 좌표를 기준으로 -n, -f가 된다.

clip 공간에서는 

 

다음 주 코드 실습. 객체 기준으로 바꾸어 적용하는 법을 알아야 한다.

 

직교 투영의 이해

직교 투영 (Orthogonal Projection)

투영 방법 중에 하나이며 객체의 크기와 형태가 변하지 않는다.

원근 투영과 다르게 카메라의 거리와 무관하다.

3D로 2D 게임 프로그래밍을 구현할 때 많이 사용한다.

near, far의 개념은 존재한다. depth는 있어야 하기 때문에.

사각뿔에서 위를 잘라낸 모양을 띈다.

 

직교 투영으로 생긴 NDC

- 원근 투영과 동일하다.

- 카메라를 기준으로 NDC가 구성되며, z값은 -1~1까지로 구성된다.

- 카메라보다 멀어지면 z값이 커져야 하므로 왼손 좌표계 구조이다. (하지만 크게 의미는 없다.)

- width, height, depth의 크기가 2인 정육면체이다.

- 정육면체의 중심에 카메라가 위치한다.

월드 좌표계와 뷰좌표계는 오른손 좌표계이다.
NDC 좌표계는 왼손좌표계.

 

변환 과정[l, r]이 [-1, 1]로 변환되게 하는 법

사이 간격을 계산하여 나누어주어야 한다.

ex) [2:8] -> [10:12] = 6:2

 

View 평면의 점V(Vx, Vy, Vz)일 경우 Near 평면에 투영된 점 N(Nx, Ny, Nz)

Nx은 직교투영이므로 [l, r]이 [-1, 1]로 변환되어야 함.

1-(-1)/ r−l = 2/ r−l
Nx = (2/(r-l))*Vx + b
Vx = r
Nx = 1

1 = (2/(r-l))*r+b
b = 1 - (2/(r-l))*r = (-r-l)/(r-l)
Nx = (2/(r-l))*Vx - ((r+l)/(r-l))
Ny는 직교투영이므로 [b, t]이 [-1, 1]로 변환되어야 함.

1-(-1)/ t−b = 2/ t−b
Ny = (2/(t-b))*Vy + b
Vy = t
Ny = 1

1 = (2/(t-b))*t+b
b = 1 - (2/(t-b))*t = (-t-b)/(t-b)
Ny = (2/(t-b))*Vy - ((t+b)/(t-b))
4x4 직교투영 행렬을 구하면

P·V = P·[Vx] = [( 2/(r-1))*Vx-((r+l)/(r-l))]
        [Vy]   [( 2/(t-b))*Vy-((t+b)/(t-b))]
        [Vz]   [(-2/(f-n))*Vz-((f+n)/(f-n))]
        [1 ]   [1                         ]

P = [2/(r-l) 0       0        -(r+l)/(r-l)]
    [0       2/(t-b) 0        -(t+b)/(t-b)]
    [0       0       -2/(f-n) -(f+n)/(f-n)]
    [0       0       0        1           ]
    
[Vx] = [( 2/(r-1))*Vx-((r+l)/(r-l))]
[Vy]   [( 2/(t-b))*Vy-((t+b)/(t-b))]
[Vz]   [(-2/(f-n))*Vz-((f+n)/(f-n))]
[1 ]   [1                          ]

뷰포트 변환

스크린 스페이스로 가기 위한 마지막 변환.

최종적으로 디스플레이에 2D 결과화면을 가시화하는 단계

- 화면에서 최종 출력을 위해 (x, y) 좌표 값과 깊이 값을 통해 어떤 객체를 보여야 할지 depth buffer를 기반으로 결정된다.

- 뷰포트, 출력할 윈도우와 윈도우 내 출력 공간의 결정이 필수적이다.

- 윈도우에서는 좌상단이 원점, OpenGL은 좌하단이 원점이다.

뷰포트

뷰포트는 x, y, width, height를 매개변수로 지닌다.

- 뷰포트는 스크린 상에서 보여지는 사각형 영역.

- 좌상단이 (0, 0)인 화면 좌표계 사용 시 화면 좌표계 변환 행렬이 필요하다.

좌표계 이동 변환

x, y를 이동시켜 좌상단이 원점인 화면 좌표계로 이동 변환한다.

z는 -1 <= z <= 1에서 계산의 편의를 위해 0 <= z <= 2로 변환한다.

- x축 : x+1 → [-1,1] > 0, 2

- y축 : 1-y → [-1,1] > [2,0]

- z축 : z+1 → [-1,1] > [0,2]

뷰포트의 범위로 확장

x, y를 viewport의 width, height 범위로 확장한다.

z는 0 <= z <= 2에서 정규화를 해서 0 <= z <= 1로 변환한다.

x축 : (width-0)/(2-0) * (x+1)
(width/2)*x + (width/2)
[0, 2] -> [0, width]
y축 : (height-0)/(2-0) * (1-y)
(-height/2)*y + (height/2)
[0, 2] -> [0, height]
z축 : (1-0)/(2-0) * (z+1)
(1/2)*z + (1/2)
[0, 2] -> [0, 1]

좌상단 원점 변경

x, y를 viewport x, y에 따라 그려지는 좌상단 원점을 변경한다. (기본 0, 0)

z축은 0 <= z <= 1값을 그대로 유지한다.

x축 : (width/2)*x + (width/2) + ViewportX
y축 : (-height/2)*y + (height/2) + ViewportY
z축 : (1/2)*z + (1/2)

뷰포트 변환 행렬과 NDC 좌표계 위의 점 N과의 관계

뷰포트 변환을 위한 행렬을 Mviewport로 정의해서 N과의 관계를 정리한다.

지금까지 구한 행렬들을 전부 곱하면 된다.

총 변환 과정

Local Space + Model Matrix

View Space + View Matrix

Clip Space + Projection Matrix

NDC 좌표계를 만드는 것까지 Clip Space에서 해야 하는 일.

최종적으로 2D 화면에 보여주기 위한 Viewport Matrix를 통한 변환

이 모든 과정을 통해 화면에 그릴 수 있다.

 

맥스와 마야의 결과물을 월드 스페이스에 배치하고, 이동, 회전, 크기변환 하기 위해 모델 스페이스를

카메라를 사용하기 위해서 카메라의 위치/회전값을 통해 뷰 스페이스가 만들어진다.

원근 투영/ 직교투영을 할 수 있다.

 

마지막으로 화면에 띄워주기 위하여 뷰포트를 알아야 한다.

어느 위치에 그릴 것인지, 화면 해상도와 동일하거나 다르게 그릴 수 있다.

뷰포트 자체를 Matrix화 시켜서 변환을 해야 한다.

 

이 과정이 렌더링 과정이다.

우리가 이것을 생각하지 않으면 3D 월드에 대해 이해할 수 없다.

렌더링 내에는 지금까지의 모든 과정이 숨어있다.

 

다음 시간에는 게임 엔진에서의 객체, 카메라에 대한 부분을 통해서 matrix의 행렬들이 곱해지는 과정을 실습 할 예정.

3D Graphics API에서는 이런 레스터한 부분을 다루지 않는다. 알고리즘적인 부분에 대한 이해가 필요하다.

레스터 알고리즘까지 학습 진행 할 예정.

 

정사각형은 8개의 점을 가지고 있다. 정사각형은 2개의 삼각형으로 이루어져 있다.

총 8개의 점을 인덱스에 따라 그린다. 하지만 이것은 텍스처 매핑을 제외한 이야기.

텍스처 매핑을 한 점이 uv값, 노말 값이 다를 수 있다.

Lighting - DirectX에서 다룰 예정.

사원수와 짐벌락 - 2학기에 수업 예정.

 

과제 있음. 오늘 배운 내용 다시 정리.

 

 

 

728x90
반응형