본문 바로가기

Game

3D그래픽스의 개념과 렌더링파이프라인

최근까지의 3D그래픽스의 역사를 대충 이해했으니, 이번에는 3D그래픽스의 처리 흐름을 해설하고 싶다.

3D그래픽스 파이프라인의 모식도(模式圖)

3D그래픽스의 흐름을 모식화한 것이 아래의 그림1이다. 이것은, 다이렉트X 10 세대 / SM 4.0 대응의 GPU까지의 흐름을 모식화하고 있는데, 일부 흐름의 순서가 GPU에 따라서 다른 경우나 또는 작은 처리단계에 대해서는 일부, 간략화한 부분도 있다. 이점은 양해해 주었으면 좋겠다.

우선, 왜 3D그래픽스 처리가 이렇게 되었는가 하는 근본적인 이야기에 대해서 짚어 보자. 이렇게 된 것은 길고도 짧은 실시간 3D그래픽스의 역사 속에서, 이것이 가장 처리가 매끄럽게 될 것 같고, 그리고 GPU(하드웨어) 의 설계로서도 구현(implementaion)이 (더) 쉽다는 이유 때문이다. 이 흐름은 Direct3D에서도 OpenGL에서도 큰 차이는 없다.

                                       그림1: GPU 내부에서의 렌더링 흐름

<그림 설명>
1: 3D 모델 구축
2: 가상공간에 배치
정점파이프라인
정점셰이더
3: 정점단위의 음영계산
지오메트리셰이더
4: 정점(프리미티브)의 증감(增減)
5: 카메라공간으로의 전개
6: 클리핑이나 음면처리
7: 트라이앵글 셋업과 래스터라이징 처리
픽셀파이프라인
픽셀셰이더
8: 픽셀단위의 음영처리
9: 텍스처 적용
10: 렌더 백엔드(필터, 깊이, 스텐실)(포그, 블렌딩)
11: 출력

CPU가 담당하는 3D그래픽스 처리부분 = 게임엔진!?

그림1에 있는 [1]과 [2] 항목은 주로 CPU에 의해서 수행되는 처리계이다.

3D오브젝트를 배치한다든지, 이동해서 재배치 한다든지....하는 부분에 해당되는 곳으로 이것을 시스템적으로 처리하는 것이 소위 「 게임엔진 」이라고 부르는 부분이다.

게임엔진에서는, 키 입력, 마우스 입력, 게임컨트롤러 입력에 따라서 3D 캐릭터를 이동시킨다든지, 총격이 적에게 명중했는지 안했는지 충돌판정을 한다든지, 충돌의 결과로, 3D 캐릭터를 날려버리기 위한 물리 시뮬레이션을 한다든지 하는데 이런 게임로직 부분은 어떤 의미에서 [1][2]에 해당하는 부분이다.

그리고 [2]는 다이렉트X 10 / SM 4.0 대응의 GPU인 경우 지오메트리셰이더를 활용하면, GPU에서도 가능하게 되어 있다. 예를들면 파티클이나 빌보드 같은 포인트스프라이트에 대해서는 생성이나 소멸을 지오메트리셰이더에 시켜서 CPU를 개입시키지 않고 처리하는 것이 가능하다. 그렇다고 해도 일반적인 3D게임 처리등에서는 아직 이부분은 CPU가 담당하는 부분이라고 할 수 있다. 

정점파이프라인과 정점셰이더~ 좌표계란?

그림속에서 빨간라인에 걸쳐있는 [3][4][5][6] 부분은, 정점차원의 처리를 하는 정점파이프라인이다.

보통은 여기부터가 GPU 내부에서 처리가 이루어지는 부분이 된다. 다만, 내부 로직을 간략화해서 낮은 비용으로 그래픽스 기능을 통합시킨, 이른바 「통합칩셋」등에서는, 이 정점파이프라인을 CPU에서 대신하는(emulation하는) 시스템도 존재한다.

좀전까지는 이 정점파이프라인을 「지오메트리 처리」등으로 부르는 경우도 많았다. 지오메트리(Geometry)라는 것은 「기하학」으로 고등학생 이상이면 수학에서 「대수, 기하」등의 수업시간에 「벡터연산」이나 「사영 또는 일차변환」등을 배운 적이 있었을텐데, 이것이 그런 세계의 것이다. 여담이지만, NVIDIA의 GPU인, GeForce 시리즈의 이름의 유래는 「Geometric Force(기하학적인 힘)」을 줄여서 만든 이름이고, 「G-Force(중력)」에 낚였다(끌렸다)는 농담도 존재한다.

이야기로 돌아가서, 3D그래픽스를 말할 때 반드시 등장하는 「삼차원 벡터」라는 개념은 간단히 말하면 「삼차원 공간상의 "방향"」 이라고 생각하면 된다. 그런 "방향"은 x, y, z 3개의 축의 좌표값으로 표시되고, 그 "방향"의 기준을 「좌표계」라고 한다.

이 좌표계에는 로컬좌표계와 월드좌표계(글로벌좌표계)라는 것이 있다.

「로컬좌표계」는, 구체적으로 말하면, 어떤 3D 캐릭터로부터 적당히 결정된 기준이 되는 좌표계이다. 3D의 방향은 그 3D캐릭터의 기준 좌표계에서 「어느쪽을 향하고 있다」고 관리하고 제어하는 방법이 편하다. 그래서 로컬좌표계라는 개념을 사용하는 것이다.

그런데, 일반적인 3D캐릭터에는 팔이나 다리가 붙어 있는 것이 많은데 이것을 그 관절로 부터 회전한다든지 하는 것을 생각하는 경우는 관절을 기준으로 한 로컬좌표계에서 제어하는 편이 쉽다. 그러나 이렇게 생각하면 로컬좌표계는 계층구조가 되버려 최종적으로 처리를 마무리 할 때에는 기준을 알수 없게 된다. 

거기서, 그 3D 공간전체를 지배하는 좌표계가 필요해 진다. 그것이 「월드 좌표계」이다. 3D그래픽스의 정점파이프라인의 정점단위 처리에서는 이 로컬좌표계로 부터 월드좌표계로의 변환이 빈번하게 일어난다.

이런 정점단위의 좌표계 변환 처리를 셰이더프로그램에 따라서 실행하는 것이 [3]의 「정점셰이더」(Vertex Shader)인 것이다. 셰이더프로그램을 다시 짜면 유니크하고 특수한 좌표변환을 할 수 있다는 것이다.(계속)

            그림2: 좌표계의 개념도

<그림 설명>
로컬 좌표계,
월드 좌표계

                                          그림1: GPU 내부에서의 렌더링 흐름

정점셰이더가 하는 또하나의 일~ 정점단위의 음영처리

그림1에 있는 [3]의 정점셰이더가 하는일은 좌표변환 뿐만은 아니다. 정점단위의 음영처리/광원처리(라이팅)도 정점셰이더의 중요한 역할이다.

「좌표변환」은 「수학적」인 느낌이 들고, "계산한다", 라고 하는 이미지가  떠올라 알기 쉽다. 하지만, 컴퓨터 안에서 라이팅을 한다.... 즉 "빛을 비춘다." 라고 하는 이미지는 쉽게 연상되지 않을지도 모른다. 물론 GPU는 카메라가 아니고 계산기이므로 실제로 빛을 비쳐 사진을 찍을리 없다. 계산을 해서 이것을 구하는 것이다.

빛이 물체에 닿으면 빛은 거기서 반사/확산되거나 흡수된다. 그 물체에 색깔이나 모양이 있으면 그 색이 보일지도 모르고 비쳐진 빛에 색이 있으면 그 물체의 색이나 모양과 합성된 색이 보일 것이다. 이런 처리를 계산해서 구하는 것이 컴퓨터 그래픽스의 기본적인 방식이다.

이 처리를 어떤식으로 해서 계산기가 잘 계산할까? 이것도 실은 벡터연산을 이용한다.

빛의 방향을 나타내는 「광원벡터」와 시선방향을 나타내는 「시선벡터」, 그리고 빛이 닿는 폴리곤을 구성하고 있는 정점의 방향을 나타내는 「법선벡터」의 3개의 벡터를 사용해 이런저런 벡터의 상대관계로 부터 어느 정도의 빛이 시선방향에 대해 반사하는지를 나타내는 반사방정식을 이용해 계산하는 것이다.

이 반사방정식에는 표현하고 싶은 재질에 따라 다양한 종류들이 있고, 이 반사방정식을 프로그램으로 표현한 것이 정점셰이더 프로그램이다. 그리고 이 정점단위의 반사방정식 프로그램을 실행하는 것도 역시 정점셰이더인 것이다.

정점셰이더에서는 정점단위의 음영처리뿐 아니라 폴리곤에 붙일 텍스처 좌표의 계산도 한다. 텍스처좌표의 계산이라는 것은 어는 폴리곤에 어떤 텍스처를 어떻게 붙여 나갈까라는 대응을 계산하는 것이다. 실제로 텍스처매핑은 [8][9]의 픽셀셰이더가 하고 여기서는 텍스처 매핑을 수행할 준비를 한다라는 이미지이다.

그림3: 정점셰이더가 하는일의 예: 정점셰이더를 활용한 
굴절 표현

<그림 설명>
(버블 상)
光源ベクトルL(광원벡터 L)、
法泉ベクトルN(법선벡터 N),視線ベクトルE(시선벡터 E),屈折ベクトルR(굴절벡터 R)、(원)環境マップ(환경맵)
(원) 頂点シェーダ(정점셰이더)
(버블 하)
정점셰이더프로그램
보통의 광원처리를 하고, 나아가 굴절벡터를 구해 면의 건너편의 환경맵을 가리키도록 텍스처 좌표를 수정해 버리자
(우)
건너편이 투명하게 보이는 것 같은 반투명 사과 완성!!

지오메트리셰이더~ 정점의 증감(增減)이 가능한 무시무시한 녀석

다이렉트X 9 / SM 3.0 세대 이전의, 지오메트리셰이더가 없던 세대의 GPU에서는 3D모델의 정점 정보는 CPU쪽 소프트웨어에서 미리 준비해 두는 것이였고, 한번 GPU에 입력되면 이것을 GPU쪽에서 마음대로 증가/감소 시킬수 없었다.

그때까지의 "대원칙의 틀"을 깨고 정점을 자유자재로 증가/감소 시킬수 있는 기능을 가진 셰이더가 [4]의 「 지오메트리셰이더 」이다.

어떻게 증가/감소 시키는가는 지오메트리셰이더를 실행 시키는 셰이더프로그램이 지정한다. 또 실제로 증가/감소 시킬 수 있는 것은 복수의 정점이기 때문에 실질적으로는 선분, 폴리곤, 파티클이라는 각종 프리미티브의 증감이 가능하게 되어 있다.

지오메트리셰이더의 활용방법은 여러가지가 나와 있지만 폴리곤을 자유자재로 생성 가능하기 때문에 지면에 풀(grass)이 되는 폴리곤을 만들게 한다든지 또는 3D 캐릭터의 털(fur)을 만들게 한다든지 하는 것이 가장 기본적인 활용 방침으로 되어있다. 게임등에서는 게임로직과의 인터랙티브(interactive) 처리가 별로 필요 없는 불꽃등의 이펙트 표현을 지오메트리셰이더에서 생성한 파티클로 표현한다... 라는 것도 가능할 것이다.

지오메트리셰이더에서 생성한 정점은 다시 정점셰이더에 돌려보낼 수 있기 때문에 재귀적인 정점처리가 가능하다. 예를들면 (실제로는 보통의 방법으로는 할수 없지만), 로우폴리곤으로 만든 어떤 3D모델로 부터 지오메트리셰이더에서 폴리곤을 보간해서 둥그스름한 하이폴리곤모델을 생성한다... 라는 것도 이론상으로는 가능하다.(계속). 

로우폴리곤 모델(좌)로 부터, 산술적으로 폴리곤을 보충해서 하이폴리곤 모델(우)로 변형하는 활용도 생각할 수 있다.

그림4: 지오메트리셰이더가 하는일의 예: 
지오메트리셰이더로 털을 생성한다.

<그림 설명>
(원) 지오메트리셰이더
(버블) 털을 표현하는 폴리곤을 심어 버리자

                                       그림1: GPU내부에서의 렌더링 흐름

정점파이프라인의 최종처리

[5][6]은 실제로 그리기 위한 마지막 준비단계적인 처리에 해당된다.

월드좌표계로 변환된 좌표계를, [5]에서는 카메라(시점)에서 잡은 좌표계로 변환한다. 그리고 화면에 표시할 때 어떻게 보일지... 구체적으로는 어떻게 시계(視界, 시야)로 할까하는 변환도 한다. 이것은 사진 촬영의 경우에 카메라의 프레이밍(framing)이나 렌즈의 선택에 해당하는 부분이라 할 수 있다. 이런 일련의 처리를 뭉뚱그려 「투시변환처리」라고도 한다. 

어쨋든 3D그래픽스는 시야에 잡힌 영상을 그리기만 하면 되기 때문에, [5]의 처리가 끝나면 시계주체(視界主體, 시야주체)에서 생각하는 방식으로 옮겨 온다 . 

[6]은 그리지 않아도 된다고 판단되는 폴리곤들을, 실제로 그리는 처리를 하는 픽셀파이프라인에 돌입하기 전 단계에서 파기해 나가는 프로세스이다.(컬링처리라고도 한다.)

「클리핑처리」는, 시야로 부터 완전히 벗어난 3D모델의 폴리곤들을 파기하고, 3D모델의 폴리곤 중에 시계에 걸쳐진 폴리곤은 시계 범위내의 폴리곤에서 잘라내는 처리도 한다.

「음면(陰面)처리」는 시점 방향을 향하지 않는, 이론상으로는, 시점으로 부터 보이지 않는 폴리곤을 파기하는 처리이다. 투명오브젝트가 붙어 온 경우에는 이 처리를 하면 이상한 결과가 생기는 경우도 있다.
 

픽셀단위의 테스크로 분해해서 보내는 래스터라이저

시야주체로의 변환도 끝내고, 불필요한 폴리곤도 파기한 후, [7]에서 하는 것은 지금까지 실태(實態)가 없었던 폴리곤을 지금부터 그릴 화면상의 화소(픽셀)에 대응시켜 붙여주는 처리이다. 최신 3D그래픽스에서는 화면에 표시할 프레임을 그리는 것 뿐만 아니라 씬을 텍스처에 렌더링할 경우도 있고, 그럴 경우 [7]에서는 폴리곤과 텍스처화소(픽셀)를 대응시키는 처리를 한다.

이 [7]에서의 처리는 실질적으로는 정점파이프라인에서 정점단위(폴리곤단위)로 출력된 계산결과를 픽셀단위이 일로 분해해서, 이어진 픽셀파이프라인으로 보낸다(발주한다). 말하자면 중개업적인 역할이다.

이 [7]의 처리는 「 트라이앵글 셋업 」, 또는 「  래스터라이즈 처리 」라고 불리고, 틀에 박힌 정해진 처리계이기 때문에, 1990년대의 초기 GPU 때부터 줄곧 고정기능으로 GPU에 들어있고, 지금도 큰 진화는 없다.

통상적으로 1개의 폴리곤은 복수의 픽셀로 그려지기 때문에, 폴리곤은 래스터라이저에 의해서 대량의 픽셀 테스크로 분해된다. GPU에 픽셀셰이더의 개수가 압도적으로 많은 것은 아무래도 픽셀셰이더의 쪽의 일이 (더) 증가해 버리기 때문이다.(계속)

그림5: 래스터라이저는 픽셀셰이더로의 발주서를 작성하는 곳... 이라고도 할 수 있다. 하나의 폴리곤으로부터 복수의 픽셀 테스크가 생겨난다.

<그림 설명>
정점셰이더, 폴리곤, 래스터라이저, 픽셀셰이더 발주서, 픽셀셰이더
(버블) 삼각형->여러개 픽셀

                                       그림1: GPU 내부에서의 렌더링 흐름

픽셀단위의 음영처리를 실행하는 픽셀셰이더~
텍스처는 화상 텍스처만 있는 것이 아니다.

[7]의 「래스터라이즈 처리」에 의해서 생성된 픽셀단위의 음영처리 일을 해내는 것이 [8][9]로 표시된 픽셀셰이더(Pixel Shader)이다. 그리고, 렌더 백엔드(render back-end)까지를 포함한 틀 전체를 「픽셀파이프라인」이라 부른다. 
 
GPU에 따라 그 구현된 형태는 여러가지이고, [8]의 픽셀단위의 다양한 음영처리를 수행하는 기능 블럭만을 「픽셀셰이더」라고 부르는 경우도 있으며, 뒤에 기술할 「텍스처 유닛」인 [9]를 총괄해서 픽셀셰이더라고 부르기도 한다. 

이제, 그 [8]의 픽셀셰이더에서 수행하는 계산인데, 실은 단위가 픽셀로 되어 있다는 것뿐 수행하는 처리 내용은 정점쉐이더와 닮은 부분이 많다.
 
픽셀단위로 광원벡터, 시선벡터, 그 픽셀의 법선벡터들을 사용해서 반사방정식을 풀고, 그 픽셀이 어떤 색이 될지를 구하는 「픽셀단위의 라이팅」을 하게 된다.

그 경우, 정점단위의 라이팅 결과를 단지 보간해서 그 픽셀의 색으로 하는 간단한 라이팅 보다도 온화한 음영이나 아름다운 하이라이트가 나올수 있다. 이것을 특히 「퍼픽셀라이팅」(Per Pixel Lighting) 이라 부른다.

정점쉐이더에서 구해진 텍스처 좌표로 텍스처에서 텍셀을 읽어 내는 것이 [9]의 텍스처 유닛이다.

이 텍스처 유닛으로 부터 가져온 텍셀의 색과 앞에서 구한 픽셀단위의 음영처리 결과에 의해 구해진 픽셀색 양쪽을 고려해서 최종적인 픽셀색을 구한다.

이 픽셀셰이더에서 실행시키는 셰이더 프로그램이 픽셀셰이더 프로그램이고, 그것에 의해서 픽셀 단위의 라이팅을 특수한 것으로 만들 수 있다.

통상적으로 「텍스처」라고 하면, 폴리곤에 붙이는 화상을 연상하지만, 현재의 프로그래머블 셰이더 시대에서는 그 응용 방법이 확장되어져 왔고,  텍스처에 화상이 아닌 수학적인 (또는 물리적인) 의미를 가진 여러가지 수치 데이터를 넣어 두는 응용들이 생겨났다. 픽셀셰이더에서 픽셀 단위로 음영처리를 할 때는 그 수치 텍스처로 부터 수치데이터를 순차적으로 가져와서 계산에 이용하게 되었다.

텍스처도, PC화면의 화소(픽셀)가 αRGB 각 8비트로 구성되어 있는 것과 동일한 이치로, αRGB의 4개의 요소로 구성되어 있다. 예를 들면 32비트 칼러 텍스처라면 α (투명도) 8비트, R (빨강) 8비트, G (녹색) 8비트, B(파랑) 8비트로 배분되어 있다. 수치 데이터를 텍스처에 넣는 경우, αRGB 4요소에 넣는 것을 생각하면 최대 4개 요소인 벡터나 행렬을 넣어 둘 수 있게 된다. 예를 들어 3차원 벡터이면, 그 X, Y, Z의 3요소의 수치를 αRGB의 RGB에 넣어 둘 수 있다.

실제 픽셀세이더 처리에서는 이 벡터 텍스처로 부터 적당한 텍셀을 꺼내 와서 그것을 벡터 데이터로 해, 시선벡터, 광원벡터, 법선벡터 등과 조합해 특수한 반사방정식을 풀어 독특한 재질을 표현한다.
 
그림6에서는 법선벡터를 텍스처에 넣은 「법선맵」을 사용해 범프맵핑을 하는 예를 보여주고 있다. 이것은 이후의 연재 속에서 한번더 해설 할 것이므로 여기서는 「픽셀셰이더가 하는일이 이런 거다.」라고만 알면 될것 같다. (계속)

그림6: 픽셀셰이더가 하는일의 예 -- 범프맵핑이 완료되기 까지의 개념도. 
높이맵에서 법선맵으로의 변환 역시 픽셀셰이더에서 할 수 있다. 법선맵은 법선벡터를 넣어둔 텍스처로 하나의 텍셀에 하나의 XYZ로 표현되는 3차원 법선벡터의 값이 넣어져 있다.(XY만 넣고 Z는 계산해서 구하는 방법도 있음)

<그림 설명>
좌상 ~ 좌하:
높이맵
밝다 = 높다
어둡다 = 낮다
오목과 볼록을 나타내는 텍스처를 작성. 이것이 높이맵
법선맵(노말맵)
어둡다 밝다 어둡다
높이맵의 명암으로 부터 법선벡터를 계산
법선맵의 이미지
법선벡터들이 들어있는 텍스처가 법선맵

우상 ~ 우하:
법선맵으로 부터 꺼내 온 요철(凹凸)의 법선벡터
광원, 시선, 폴리곤
꺼낸 법선벡터로 폴리곤 상의 각 픽셀에 대해 음영계산을 수행한다.
범프매핑의 완성
실제로는 평면이지만 요철이 있는 것처럼 빛을 비추므로 그렇게 보여 버린다.

                                   그림1: GPU 내부에서의 렌더링 흐름

렌더링 최종공정~ 렌더 백엔드

픽셀셰이더의 출력은, 정확히 말하면 「 폴리곤을 구성하는 그 화소가, 그 장면에서는 그 색으로 결정되었습니다. 」라는 것이고, 그대로 비디오 메모리에 써 넣어서 「 한 픽셀의 그리기 완료 」라고 하고 싶지만, 아직 할 일이 있다.

그것이 [10]의 렌더 백엔드 (Render Backend)이다. 덧붙이면 NVIDIA의 경우는 이 부분을 ROP유닛이라고 부르기도 한다. ROP유닛은 Rendering Output Pipeline, 또는 Raster Operation의 약어라는 설이 있지만 확실치는 않다. 본 연재에서는 전자를 정확한 해석이라고 해 두자. 

어쨋든, 여기에서는 픽셀셰이더의 출력을 「 써넣어도 좋은것인가의 검증 」, 써 넣을 때는 「 어떻게 써넣을까의 결정 」등의, 비디오 메모리의 쓰기 제어 부분이다. 픽셀셰이더 자신은 텍스처를 읽어 낼 수는 있어도, 비디오 메모리에 써낼 수는 없기 때문에 이 처리는 지극히 중요한 부분이다.
 
그런데, 다이렉트X 9 / SM 2.0 세대 이전의 GPU에서는 픽셀셰이더의 개수와 ROP유닛의 개수가 항상 일치했었기 때문에, 픽셀셰이더와 ROP유닛은 "대(對)" 와 같은 관계를 상상할 수 있었다. 하지만, 다이렉트X 9 / SM 3.0 세대 이후의 GPU에서는 픽셀셰이더 프로그램의 고도화와 함께 픽셀셰이더 개수의 증강이 중점적으로 이루어진 결과, ROP유닛의 개수는 픽셀셰이더의 개수 보다도 적은 것이 일반화 되었다. 

최근 GPU에서는 픽셀셰이더 > ROP유닛 구성이 일반적이다. 
그림은 GeForce 7800 GTX의 블럭다이어그램. 
중간의 4x6 = 24기(基)가 픽셀셰이더. 최하단의 16기(基)가 ROP유닛.

「 써 넣어도 좋은것인가의 검증」으로써는 「 알파테스트 」「 스텐실테스트 」「 깊이테스트 」라는 것이 있다. 

알파테스트는 출력하는 픽셀색이 완전히 투명한가 아닌가의 테스트. α성분이 0으로 투명하다면 그릴 필요가 없으므로 그 픽셀은 그리지 않는다.

그림7: 알파테스트 - 불투명한 부분의 픽셀만 그린다.

<그림 설명>
알파테스트의 예
투명 α = 0
불투명 α = Max
이 픽셀의 그리기는 취소된다


스텐실테스트는 다목적 연산 프레임 버퍼로 이용되는 스텐실버퍼의 내용에 따라서, 어플리케이션이 설정한 조건을 통과할 수 없으면 그 픽셀은 그리지 않는다. 화면의 일부를 도려낸다든지, 스텐실쉐도우볼륨 기법의 그림자 생성 시 그림자 형태를 뽑는 처리등에 응용된다.

그림8: 스텐실테스트 - 스텐실버퍼의 내용을 참조해서, 미리 설정한 테스트 조건을 만족하면 그린다. 이 그림은 「 스텐실버퍼의 내용이 A의 부분만을 씬에 그린다」는 예시이다.

<그림 설명>
(제목) 스텐실테스트의 예
좌 상: 렌더링하는 씬
좌 하: 스텐실 버퍼
우: A 부분은 스텐실테스트를 통과했으므로 그리고, B 부분은 통과되지 않아 취소한다

깊이테스트는 지금부터 그릴 픽셀이 시점에서 봤을 때 가장 앞에 있어서 잘 보이는 픽셀인지 아닌지를 검사하는 것이다. 그리는 픽셀과 1 대 1로 대응하는 Z버퍼라고 불리는 깊이값(Z값)을 넣어두는 버퍼를 미리 준비해 여기서 읽어 낸 깊이값과 지금부터 그릴려는 픽셀의 깊이값을 비교하는 것이 「깊이테스트의 실제 형태」이다. 깊이값은 픽셀셰이더에 의해서 계산되어 나온다.

또한, 반투명 3D 오브젝트를 구성하는 반투명 픽셀을 그리는 경우등은 이 깊이테스트 자체를 하지 않는 경우도 있다.

그림9a: 깊이테스트 - 순서 없이 그릴 때, 깊이테스트는 중요해 진다.

그림9b: Z버퍼에 깊이값을 써넣은 흔적이 없는 곳에는 무조건 써 넣는다. 써넣은 흔적이 있는 곳에서는 깊이테스트를 해서 지금부터 그려내려는 픽셀이 앞이라고 판단할 수 있는 경우만 그린다.

<그림 설명>
그림9a:
상:
(원) 시점
(버블) 본래는 이렇게 보일 것이다
안쪽(깊이값 = 15)
중간(깊이값 = 10)
앞쪽(깊이값 = 3)
하:
고양이 -> 얼굴-> 개 순서로 그렸다고 하면
아무 생각없이 고양이->얼굴->개 이렇게 그리면, 나중에 그린 개가 가장 앞에 그려져 버린다.
이러면 않된다.
그림9b:
프레임버퍼, z 버퍼
상:
중:
A: 여기는 깊이값이 15인 값이 그려진 흔적이 있다. old 15 > new 10 으로 지금부터 그릴 것이 앞이라고 판단. 깊이테스트를 통과하므로 그린다.
B: 여기는 이미 깊이값 3인 값이 그려진 흔적이 있기 때문에 old 3 < new 10으로 앞에 무언가가 있다고 판단.
깊이테스트를 통과하지 못하므로 그리지 않는다.
C: 그려진곳은 Z버퍼를 업데이트해서 다음 깊이테스트에 대비한다.


「 어떻게 써넣을까의 결정」에 대한 변형(variation)으로는 「 α합성(α블렌딩) 」, 「 포그(안개) 」등이 있다.

α합성은 단지 픽셀을 덮어쓰기해서 그려넣는 것이 아니라, 이미 써넣어진 픽셀색과 반투명합성 계산을 해서 다시 쓰는 처리를 하는 것이다. 렌더링 대상의 프레임버퍼로 부터 픽셀색을 읽어 낸다... 즉 비디오메모리 읽기가 들어가고, 거기에  α합성계산까지도 할 필요가 있기 때문에 의외로 부하가 큰 처리이다. 여담이지만, 3D벤치마크  소프트웨어등에서 반투명 폴리곤 겹쳐쓰기를 연속적으로 한다거나 하는 것은 이 성능을 평가하려는 목적이 있기 때문이다.

그림11: α합성 - α합성에서는 이미 렌더링한 결과를 읽어내 거기에 합성계산을 해서 그려내는 것이기 때문에 부하가 크다.

<그림 설명>
(제목) α합성
(좌) ~ (우)
지금부터 그려 낼 내용 + (프레임버퍼이미 그려져 있는 내용 합성상태를 나타내는 α값에 따라서 합성

안개(fog)는 지금부터 그릴 픽셀의 깊이값에 따라서 미리 설정해 둔 포그컬러의 섞는상태를 조정하는 처리를 하는 것이다. 안으로 깊이 들어가면 갈수록 그 포그의 픽셀색을 흰색에 가깝게 하는 것과 같은 설정을 하면, 안쪽이 뿌옇게 보이는 공기원근(空氣遠近)의 표현이 가능하다. 

그림12: 안개 - 안으로 들어갈수록  픽셀값을 뿌옇게 함으로써 공기의 원근 표현이 가능하다.



물론, α합성도 안개처리도 하지 않는경우는 그 픽셀색을 그대로 비디오메모리에 써내는 처리를 한다. 그리고 써낼때는, 그다음 이후의 다른 픽셀의 깊이테스트에 대비해서, 깊이값을 업데이트해 둔다. 

또한, 격자형 화소배열의 화면화소에서의 픽셀이 톱니같은 느낌(jaggy)을 주는 현상을 감소시키는 안티앨리어싱처리도 이 렌더백엔드 부분에서 이루어진다. 


'Game' 카테고리의 다른 글

액션게임의 타격감 향상시키기  (0) 2015.11.11
구글 플레이 게임 정책  (0) 2015.01.30