반응형
개요
- 현재의 렌더링 방식은 크게 2가지인데 각각 단점이 있다.
- Forward : Light가 많을 때 문제
- Deferred : Bandwidth 사용량, 반투명, MSAA
- 각각의 장점만 취할 수 없을까?
- Light Volume의 Index를 저장해서 해결해보자.
- 결론부터 말하자면 Lighting은 Deferred, Geometry는 Forward
구현
- Render depth only pre-pass
- Light Index Texture에 Light Volume을 그림
- 이 때 Light Index만 기록하면 됨
- Depth Write Off, Depth Test On
- Forward Rendering으로 Geometry를 그림
- 2에서 기록한 Light Index Texture를 참고하면서 Lighting
문제점
- 한 Fragment에 여러 Light가 겹치는 경우에는?
- 기본적인 해결책으로는 Light Index Texture의 여러 채널에 나누어 쓴다.
- R 채널에는 1번째 Light, G 채널에는 2번째, B 채널에 3번째, A 채널에 4번째, ...
Light Overlap 해결책
CPU Sorting
구현
- CPU에서 array 4개를 만든다.
- R8G8B8A8 Texture를 사용한다고 가정
- array[N] * 4
- 모든 Light에 대해서 겹치지 않고 추가될 수 있는 array를 찾아 넣는다.
- 찾을 수 없다면 버리거나 다음 Pass에 그린다.
- arrays[0]의 모든 Light Index를 R 채널에 그린다.
- arrays[1~3]도 GBA 채널에 그린다.
for (l in lights)
for (a in arrays)
if l is not intersect with all of a's lights
add l to a
장점
- Fragment Shader에서 Unpacking을 할 필요가 없음
- 중요한 Light부터 정렬할 수 있음 (비교할 때 조건을 두면 됨)
- Static Light가 많다면 arrays를 미리 만들 수 있음
단점
- CPU에서 정렬 해야함
Light Index Packing - Multi-pass Max Blend Equation
Packing
- Color, Stencil Buffer를 0으로 지움
- Blending 모드를 MAX로 설정
- GL : GL_MAX, DX : D3D10_BLEND_OP_MAX
- Red, Green Channel
- Stencil 값이 2보다 작을 때만 통과하도록 설정 (최대 2번 덮어쓸 수 있음)
- Stencil 테스트가 성공하면 Stencil 값을 증가하도록 설정
- Red : Index 값을 씀 (여기서의 Index는 당연히 256으로 나눈 0~1 사이의 값)
- Green : 1 - Index 값을 씀 (MAX Blending을 하기 때문에 1에서 빼면 낮은 Index의 값이 써짐)
- Blue, Alpha Channel
- Stencil 값이 0일 때만 성공하도록 설정
- Stencil 테스트가 실패하면 Stencil 값을 감소하도록 설정
- Blue : Index 값을 씀
- Alpha : 1 - Index 값을 씀
Unpacking
- Index 1 : Red channel
- Index 2 : 1.0f - Blue channel
- Index 3 : Green channel
- Index 4 : 1.0f - Alpha channel
장점
- 간단한 Unpack
- Light 우선 순위 설정이 가능 (중요한 Light의 Index 값을 크게 하면 MAX Equation에 의해서 덮어써지니까)
- CPU Sorting과 같이 사용하면 Stencil Buffer 사용하지 않을 수 있음
단점
- 4개의 중첩 Light만 처리 가능
- 2 Pass 렌더링이 필요함 (R,G 이후 B,A)
- Stencil Buffer 사용
- 1개나 3개의 Light 중첩이 발생하면 1,2나 3,4는 같은 Index를 가짐
Light Index Packing - Bit Shift
구현
- Color Buffer를 0으로 지운다.
- Blending 모드를 변경한다.
- ONE
- CONTANT_COLOR (0.25로 설정)
- Destination(=즉 기존에 있던 색)은 0.25가 곱해짐
- 0.25를 곱하는 것은 Bit를 오른쪽으로 2번 Shift 하는 것과 같음
- Light Volume을 그린다.
- 이 때 8bit Index를 4개의 2Bit로 쪼개고 RGBA 채널의 최상위 Bbit에 설정한다.
- R : (index & 0x03) << 6
- G : (index & 0x0C) << 6
- B : (index & 0x30) << 6
- A : (index & 0xC0) << 6
- 이렇게 N번째 그리는 Light가 7~8번째 Bit에 그려지고 N+1번째 Light를 그릴 때는 Destination에 0.25를 곱하면서 N번째 Light Index는 5~6번째 Bit로 밀려난다.
장점
- 얼마나 많은 Light 개수, 중첩이 필요한지 조정이 가능함
- Stencil 등 별도의 Buffer 없이 1-Pass 렌더링으로 끝남
- Light의 우선순위 설정이 가능 (낮은 우선순위부터 그리면 됨)
단점
- Unpacking이 복잡함
- HW가 Bit 연산을 정확하게 수행할 수 있어야 함
- Shader4 이후부터 Shader에서 Bit 연산이 가능
Geometry Lighting
개요
- Geometry는 이제 Forward Rendering
- Lighting 계산은 World or View Space 하는 것이 좋다.
- 보통 Light 정보는 World or View Space, Surface는 Tangent Space
- Light * N을 Tangent Space로 변환하는 것보다 Surface 하나를 World 혹은 View Space로 변환하는 것이 싸기 때문
- Light Index가 참조할 테이블을 어떻게 Fragment Shader에 넘겨줄지 정해야 한다.
- Texture?
- Constant Buffer?
Point Light 구현 예
- Lookup Texture 구성
- Position, Attenuation : 1D RGBA32
- RGB : Position
- A : Attenuation
- Color : 1D RGBA8
- 255개의 Light를 사용할 때 모든 Light Data Texture의 크기는 5KB
- 0번째 Light는 "조명 없음"으로 사용
성능
원문을 보면 Overlap Light가 많을 수록 FPS가 급격히 떨어짐
1 : 82 FPS
2 : 62 FPS
4 : 37 FPS
MSAA 혹은 HW 제조사에 따라서도 다른 결과를 보임
MSAA
MSAA Texture Surface
구현
- Render all targets (depth/stencil, light index texture, main scene) to MSAA surfaces
- Forward Rendering Pass에서 screen.xy를 이용해 Light Index Texture와 Sample Texture 접근
- DX 10 이상부터 가능
Deferred Shading의 MSAA와 비교한 장점
- 2/4/8x의 큰 Buffer가 필요함
- Lighting이 2/4/8x Sample에 대해 이뤄져야 하거나 보간이 필요함
Light Volume Front Face
- 일반적인 Deferred Rendering에서 Light Volume과 Fragment가 만나는 곳만 Lighting이 이루어짐
- 대부분 구현은 Shadow Volume을 그릴 때 처럼
- Back Face Rendering (DepthFunc GreaterThan, Stencil Increment)
- Front Face Rendering (DepthFucn LessThan, Stencil NotZero)
- Light Volume을 그릴 때 Front Face만 그린다면 Fragment에 영향을 미치는 모든 Light에 대해서 접근이 가능함
- 장점
- 기존의 non-MSAA Texture를 그대로 이용할 수 있음
- 단점
- 기존에는 Surface에 영향을 미치는 Light만 Packing되었는데, 이제는 eye에서 Surface까지 모든 Light가 Packing에 영향을 미침
Nearest Fragment Edge
- 보통 non-MSAA Texture를 Sampling 할 때 텍셀이나 픽셀의 중점을 사용함
- 하지만 그 중점이 렌더링 되는 Polygon 영역이 아니라면 얻어온 Light Index는 잘못된 값임
- 가장 가까운 Polygon의 것을 Sampling 할 수 있다면 꽤 정확한 값을 얻어올 수 있음 (다만 멀리 있는 물체에 대해서는 부정확함)
- 주변 Polygon을 찾을 때는 Polygon들의 경사도를 이용할 수 있으나 Polygon이 길고 얇은 경우에는 실패할 가능성이 높음
- 그래서 실제로 이 기법은 쓸모 없다고 함 -_-;;
Transparency
- 이전 MSAA에서 소개한 Front Face 기법을 이용하면 불투명 물체를 그린 이후 투명한 물체를 잘 그릴 수 있음
- 같은 Light Index Texture를 사용할 수 있음
- 왜냐하면 eye에서 물체까지의 모든 Light 정보가 Light Index Texture에 들어있으니
Shadow
No combined Shadow
- Shadow가 필요없는 Lighting에만 사용하라!
- 이게 무슨 소리냐!?
- 아니면 Shadow Pass 이후에 LIDR을 사용하라!
- ......
Shadow volumes
- LIDR과 가장 연동하기 좋은 방식
- 하지만 MSAA나 Transparency를 위해 Front Face 방식을 사용한다면 이 역시 사용할 수 없음
Shadow Maps
다음의 조건만 만족한다면 사용할 수 있음
- Pre-Pass
- Light Volume을 그릴 때 Depth를 접근하여 현재 Pixel이 Shadow 영역인지를 판단한다.
- Hard-Shadow도 괜찮다면 Shadow 영역일 때 Light Volume을 그리지 않는다.
- Soft-Shadow를 원한다면 별도의 Render Target에 Shadow Intensity를 기록한다.
- Final-Pass
- Shadow Map을 3D Texure 혹은 2D Texture Array에 기록한다.
- LIDR의 Forward Rendering Pass에서 Shadow Map을 접근한다.
- Shadow Map 행렬에 대해서 별도의 Light Table이 필요하고 Shadowed-Point-Light에는 사용이 어렵다.
- Point Light는 빛이 사방으로 퍼지는데 3D Texture를 사용한다면 6개까지만 가능
Multi-Light Type Support
결론은 힘들다.
역시 우회하는 방법은 있지만...
- 한가지 Light Type에만 사용하라.
- Point Light는 LIDR로, Directional Light는 일반 Forward Rendering으로...
- 혹은 Directional Light를 아주 아주 멀리 있는 Point Light로 처리해도 된다.
- Light Data에 Light Type도 같이 넣어라.
- 나중에 참조할 때 Light Type에 따라서 다른 분기의 Shader를 태우면 된다.
- Light Type마다 별도의 Light Index Texture를 만들어라.
- 대신에 얻게될 것은 Multi-Pass, 별도의 메모리, 복잡한 Shader...
Comparison (vs Other Rendering Technique)
vs Standard Deferred Rendering
장점
- Deferred Rendering 처럼 많은 Render Target이 필요하지 않다.
- Reflection vector 같은 Light 계산이 한번만 이뤄지면 된다.
- MSAA를 지원하기 쉽다.
- Transparency 지원이 가능하다.
단점
- Projected Texture Light 같은 Light Type 지원이 힘들다.
- 중첩된 Light 개수에 제한이 있다.
- Geometry Pass가 2번 필요하다. (Depth Pre-pass + Forward Rendering)
- Shadow 지원이 어렵다.
vs Multi-pass or Multi-light Forward Rendering
장점
- 훨씬 저렴한 비용으로 많은 개수의 Light 지원이 가능하다.
- 단 2번의 Geometry Pass만 있으면 된다.
- Lighting을 위해 Geometry를 여러개로 쪼개서 그리지 않아도 된다.
- 모양이 있는 Light를 그릴 수 있다.
단점
- Shader가 LIDR을 지원해야 한다.
- Object나 Light 개수가 적을 때는 더 느리다.
참고
https://github.com/dtrebilco/lightindexed-deferredrender
https://mynameismjp.wordpress.com/2012/03/31/light-indexed-deferred-rendering/
http://bpeers.com/blog/?itemid=525
반응형
'Game Dev > Article' 카테고리의 다른 글
Inferred Lighting (0) | 2016.10.13 |
---|---|
Light Pre-Pass Renderering (=Deferred Lighting) (0) | 2016.10.09 |
CppCon2014 : C++ in Huge AAA Games (0) | 2016.10.08 |
C4265 Warning을 켜자! (0) | 2016.09.22 |