13장 중급 그래픽스 - ③ 지연 셰이딩
더욱 현실감 있는 그래픽 구현을 위해서는 다양한 그래픽 기술들이 사용된다. 여기에서는 몇 가지 중급 그래픽스 개념을 다룬다. 텍스처 품질을 향상하거나 텍스처로 렌더링 하는 방법. 그리고 지연 셰이딩같이 장면을 라이팅 하는 방법을 살펴본다.
💡 13장의 목차
- 텍스처 샘플링 기법
- 텍스처로 렌더링
- 지연 셰이딩
지연 셰이딩
앞서 우리는 렌더링 결과를 텍스처에 저장하는 방법을 다뤘다. 렌더링 결과를 텍스처에 저장하는 방법은 그래픽렌더링 성능을 향상하기 위한 기술로 많이 사용된다. 이번에는 그중 많이 사용되는 지연 셰이딩에 대해서 다뤄본다.
지연 셰이딩의 원리
앞서 6장 퐁 셰이딩에서 렌더링을 진행할때 광원 연산에 대한 의사 코드는 다음과 같았다.
foreach Mesh m in Scene
foreach Pixel p to draw from m
if p passes depth test
foreach Light li that effects
color = Compute lighting equation(li, p)
Write color to framebuffer
지금까지 우리가 진행한 이런 방식을 순방향 셰이딩(forward shading) 또는 순방향 렌더링 (forward rendering)이라고 한다. 이 의사 코드의 시간 복잡도는 \(O(m \cdot p \cdot li)\)이다. 이 방식은 단일 광원에서는 잘 동작하지만 광원이 많은 야경과 같은 장면에서는 계산량이 크게 늘어난다.
대안으로 등장한 방식이 지연 셰이딩(deferred shading) 또는 지연 렌더링(deferred rendering)이다. 지연 셰이딩은 렌더링하는 방식을 대폭 변경하여 이러한 문제를 극복하는 것을 목표로 한다.
지연 셰이딩의 렌더링 과정을 두 단계(패스라고 표현함)로 나눠서 렌더링한다. 첫 번째 패스에서는 물체의 기하 정보(위치, 법선, 분산광, 반사 지수)를 렌더링 한다. 그리고 렌더링 결과를 G버퍼라 불리는 텍스처의 집합에 저장한다. 두 번째 패스에서는 G버퍼에 저장된 기하학 정보를 사용하여 각 조각에 대한 장면의 조명을 계산한다. 이 계산을 모든 픽셀 단위로 반복한다.
// 첫 번째 패스에서 계산 결과 G-Buffer에 저장
foreach Mesh m in Scene
foreach Pixel p1 to draw from m
if p1 passes depth test
Write surface properties of p1 to G-buffer
// G-Buffer에 저장된 값을 활용하여 두 번째 패스 진행. 최종 셰이딩 결과 얻음
foreach Light li in the scene
foreach Pixel p2 affected by li
s = surface properties from the G-buffer at p2
color = Compute lighting equation (l, s)
Write color to framebuffer
이 2개의 패스 접근법의 시간 복잡도는 \(O(m \cdot p_1 + li \cdot p_2)\)로 성능이 많이 개선되었음을 확인할 수 있다.
지연 셰이딩을 구현하기 위해서 아래와 같은 단계가 필요하다.
- 다중 출력 텍스처를 지원하는 프레임 버퍼 개체를 설정해야한다. (G버퍼)
- GEOMETRY정보를 렌더링 하고 G버퍼에 쓸 프래그먼트 셰이더를 작성해야 한다.
- G버퍼에 저장된 샘플(텍스처들)을 다룰 수 있는 사각형을 그려서 전역 조명의 결과를 출력할 수 있어야 한다.
- 비 전역 광원(점광이나 스포트라이트 같은)에 대한 조명을 계산하여 최종으로 기본 프레임 버퍼에서 쓸 수 있어야 한다.
G버퍼의 구현은 코드의 양이 많고 복잡하여 여기서는 다루지 않는다. 전체 소스코드와 교재를 참고하자.
지연 셰이딩의 단점
지연 셰이딩은 수많은 현대의 게임에서 사용하는 매우 강력한 렌더링 테크닉이지만 아래와 같은 단점이 존재한다.
- 텍스처에 상대적으로 많은 양의 장면 데이터를 저장해 야하기 때문에 메모리 소모를 많이 한다. 특히 위치 벡터와 같은 장면 데이터에는 높은 정밀도가 필요하기 때문에 메모리 소모가 더 커진다.
- 알파 블렌딩을 지원할 수 없어서 창문과 같은 부분적으로 투명한 물체를 다룰 수 없다. 이는 투명 오브젝트를 제외한 장면을 먼저 그린 후 별도의 패스로 투명 오브젝트를 그리는 방법으로 해결할 수 있다.
- 게임이 주로 대낮을 무대로 진행되거나 매우 적은 수의 광원만 가지는 게임에서는 G 버퍼를 설정하고 복수 렌더 타깃으로 렌더링 하는 오버헤드 때문에 잘 사용되지 않는다.
- 매우 큰 점광을 생성했는데 광원 내부에 카메라를 놓는다면 그 조명 효과가 사라지는 문제가 있다.
출처:
에이콘 출판 <Game Programming in C++>
Learn OpenGL: https://learnopengl.com/Advanced-Lighting/Deferred-Shading