Notice
Recent Posts
Recent Comments
Link
반응형
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

RenderLog

[번역]Light Indexed Deferred Lighting 본문

Graphics/참고자료

[번역]Light Indexed Deferred Lighting

scahp 2020. 3. 23. 09:40

개인 공부용으로 번역한 거라 잘못 번역된 내용이 있을 수 있습니다.

또한 원작자의 동의 없이 올려서 언제든 글이 내려갈 수 있습니다.

출처 : https://github.com/dtrebilco/lightindexed-deferredrender

 

출처에 있는 소스를 VS2019에서 컴파일 되도록 만든 소스

 

Source.7z
2.97MB

 

 

 

Light IndexeDeferred Lighting

By Damian Trebilco

dtrebilco@gmail.com

 

December 2007

(Revised January 2008)

 

 

 

 

Abstract

현재 Rasterization 기반 렌더러들은 라이팅을 위해 두가지 주요 기술을 활용합니다, 포워드 그리고 디퍼드 렌더링. 그러나 이 두가지 기술은 단점들이 있습니다. 포워드 렌더링은 복잡한 라이팅 장면을 확장하기 어렵고, 표준 디퍼드 렌더링은 높은 메모리 사용량과 투명 그리고 MSAA에 문제가 있습니다.

이 문서는 이 두 기술의 중간 정도 장점을 유지하면서 살펴보는 것을 목표로 합니다. 라이트 볼륨과 Scene이 교차하는 곳의 Light index 값을 저장하는 디퍼드 라이팅으로 이 장점들을 얻습니다.

 

Introduction

 

현대 Rasterization 기반 렌더러들은 라이팅을 위해 두가지 주요 기술을 활용합니다, 포워드 그리고 디퍼드 렌더링.

 

가장 기본적인 라이팅 기술은 포워드 렌더링입니다. 이 기술은 버택스 데이터의 Rasterization 동안 최종적으로 빛을 받은 표면을 색상을 계산하고 그립니다. 포워드 렌더링은 단순합니다, 간단한 Scene에서 효율적 메모리 그리고 빠릅니다. 그러나 여러개의 빛이 연관되어지면, 렌더러는 "Render X Pass 당 라이트 수(그리고 각각의 라이트 조합에 대한 쉐이더 코드)" 혹은 Render X 오브젝트 당 Pass(각각의 타입에 대한 라이팅) 로써 높은 Vertex 처리비용이 듭니다. 이 기술을 사용하는 렌더러는 역시 오브젝트와 라이트의 교차를 판단하기 위해서 연산을 해야만 합니다. - 아마 복잡한 Scene에 대해서 엄청나게 많은 시간을 들겁니다.

 

더 복잡한 라이팅 기술은 디퍼드 렌더링입니다. 그것은 각각의 Fragment에 대한 속성이 Vertex Data의 Rasterization 과정에서 저장됩니다. 이 속성들은 일반적으로 Fragment Position, normal vector 그리고 Material 속성들입니다. 그후 라이트 볼륨 지오메트리가 Scene에 렌더링됩니다. 이 과정에서 그것은 표면의 속성에 접근하고 Scene의 Ligting 처리를 합니다. 이 기술은 많은 라이트를 선형적으로 확장가능하고 단 한번의 Scene Vertex Data 의 Pass로 모든 표면에 동일한 Lighting 처리를 합니다. 그러나 이 기술은 표면의 속성을 저장해야 하기 때문에 높은 메모리 사용량이 필요하고 또한 MSAA와 투명처리를 함께 처리하는데 어려움이 있습니다. 다른 문제로는 모든 표면이 같은 방식으로 라이팅처리 되어지기 때문에 표면별로 커스텀 라이팅을 하는 것이 어렵습니다.

 

이 문서는 이 두가지 기술의 사이 중간정도를 주요 장점을 유지하면서 알아보는 것을 목표로 합니다. 

 

Rendering Concept

전형적인 디퍼드 렌더링은 Material 속성들을 각각의 Fragment에 저장하고 Fragment의 데이터에 접근하므로써 Lights를 렌더링합니다. 이 구현은 그 반대를 목표로 합니다. - Light 속성을 각각의 Fragment 마다 저장하고, 포워드 패스 렌더링을 사용한 메인 Scene 렌더링에서 속성에 접근합니다.

 

구현의 시작에서 각각의 Fragment의 위치에 각각의 라이트에 대해서, attenuated light vector를 계산하므로써 Lighting 기여도를 저장합니다. 이러한 벡터들은 View space (+x, -x, +y, -y, +z, -z)에서 6개의 축의 벡터들에 추가하여 합산합니다. 이 이론은 다음과 같습니다:

 

 

L1-L3은 라이팅 벡터들이고 N은 표면의 Normal 입니다. 

 

그러나 위의 식이 정확하긴 하지만 음수 라이팅 값은 0으로 고정되어야 합니다. 위의 식은 그것을 하지 않습니다. 이렇게 0으로 고정하게 되면 아래의 방정식은 더이상 성립하지 않습니다:

 

 

Light vector를 적절히 Clamp 하기 위해서, Fragment의 normal에 접근할 필요가 있습니다. 이것은 기본적으로 표준 디퍼드 렌더링 입니다. 이러한 접근은 또한 Specular 와 Light direction 을 요구하는 다른 라이팅 테크닉을 고려하지 않았습니다.(isotropic material 에만 적합)

 

다음 접근은 각 Fragment에서의 Color, attenuation 그리고 light direction을 저장하는 것과 관련있습니다. 그러나 이 접근은 각 Fragment 에 영향을 줄 수있는 라이트 수가 많이 제한됩니다. 이 제한은 렌더타겟과 버퍼 스토리지 공간의 제한때문입니다.

 

Light Indexed Deferred Rendering

이 새로운 접근은 Fragment 마다 모든 Light 나 Material 속성을 저장하기 보다 간단하게 각각의 라이트를 Unique Index에 할당하고, 이 Index 를 각각의 Light가 도달하는 Fragment 에 저장합니다. 이러한 Index 들은 Fragment shader에서 라이팅 속성 테이블로 부터 Fragment에 Light 처리를 하는 데이터를 검색하는데 사용될 수 있습니다.

 

이 기술은 3가지 기본 렌더패스로 분해할 수 있습니다:

  1. Depth only pre-pass 그리기
  2. Depth write 끄고(Depth test만 켬) Light index texture에 Light volume을 그립니다. 표준 디퍼드 라이팅 / 쉐도우 볼륨 기술은 어떤 Fragments가 각각의 Light volume에 의해 충돌되었는지를 알아내는데 사용되어질 수 있습니다.
  3. Geometry를 표준 포워드 렌더링으로 그립니다. - 라이팅은 각각의 쉐이더에서 라이팅 속성에 접근하기 위한 Light index texture를 사용하여 완료됩니다.

 

2번째와 3번째 스탭에서 Lights가 겹칠때 문제점이 발생합니다. 만약 Light volume이 서로 겹쳐지지 않는다면, 2번째 스탭은 간단하게 Light index를 텍스쳐에 기록 할 것입니다. 그리고 3번째 스탭에서 직접 접근되어 질 것입니다.

 

Fragment 당 여러 Light index들을 지원하기 위해서, 첫번째 index를 텍스쳐의 Red 채널에 저장하고, 두번째 라이트는 Blue 채널 등등... 과 같이 저장하는 것이 이상적일 것입니다. 이것을 하기 위해서, Light index packing 정책이 필요할 것입니다.

 

Light Index Packing CPU Sorting

Light index packing을 하기 위한 쉬운 CPU 기반 해결책은 Scene lights를 Light volume overlap에 따라 정렬하는 것입니다.

8 bit인 light indexes 와 RGBA8 light index texture를 가정해볼때, 4개의 light indexes overlap이 아래의 스탭에 따라 가능합니다: 

  • CPU에서 light volume data를 저장하기 위해 4개의 배열을 생성합니다. 그리고 각각의 Scene light에 대해, Light data array에 기존에 배열에 있던 라이트와 교차되지 않으면서 추가 가능한 Light data array를 찾습니다. (즉, 배열 1에 추가시도 한 후에 배열 2차에 추가 시도) 만약 라이트가 추가 될 수 없다면, 그것은 버려지거나 두번째 패스에서 처리되어지기 위해서 저장될 것입니다.
  • Light index color buffer를 0으로 초기화 합니다.
  • Red 채널만 쓰기 가능한 상태로 하고 light data array one에서 Light volume을 그립니다.
  • Green 채널만 쓰기 가능한 상태로 하고 light data array two에서 Light volume을 그립니다.
  • Blue 채널만 쓰기 가능한 상태로 하고 light data array three에서 Light volume을 그립니다.
  • Alpha 채널만 쓰기 가능한 상태로 하고 light data array four에서 Light volume을 그립니다.

이 방법의 장점은 다음과 같습니다:

  • Fragment shader에서 Unpacking 할 필요없음.

이 방법으로 light index를 packing하는 방법의 단점은 다음과 같습니다:

  • Scene의 라이트들을 CPU에서 정렬해야 함.

이 Packing 방법을 사용하면, 중요한 라이트를 먼저 정렬하여 라이트를 우선순위화 할 수 있습니다. 라이트의 overlap 횟수와 총 Scene light 수는 렌더타겟의 수와 렌더타겟의 bit-depth 에 따라서 다양하게 변화 가능합니다.

 

만약 Scene 이 일반적으로 Static lights를 만든다면, 빠른 런타임 접근을 위해서 이 라이트들은 light volume array를 선정렬 혹은 Intersection Tree를 생성할 수 있습니다.

 

 

Light Index Packing Multi-pass Max Blend Equation

Fragment 패스 기반으로 여러개의 index를 저장하는 GPU 기반 해결책은 Timothy Farrar에 의해서 제안되었으며, 더 나은 light indexed rendering의 지원을 위해서 수정되어 왔습니다. 

8 bit의 light indexes 와 RGBA8 light index texture를 가정하면, 4개의 light index overlap이 아래의 스탭을 사용하여 그려질 수 있습니다: 

  • Color 와 Stencil 버퍼를 0으로 초기화 합니다.
  • Blend equation 모드를 MAX로 설정합니다. (역주, 각 Source / Dest 컴포넌트 중에서 큰 값을 사용 하는 옵션)
  • Blue와 Alpha 채널의 쓰기를 제거합니다.
  • Stencil pass에서 stencil이 증가하도록 설정하고 stencil 비교 값을 값이 < 2 일때만 통과하도록 설정 합니다.(Fragment 당 최대 2번의 쓰기만 허용)
  • Light volume을 그리고, Red와 Green 채널에 (index, 1.0-index)를 출력합니다.
  • Red와 Green 채널의 쓰기를 제거하고 Blue와 Alpha 채널을 쓰기 가능하게 합니다.
  • Stencil pass에서 stencil이 감소하도록 설정하고 stencil 비교 값이 0과 같을 때만 통과하도록 설정합니다.
  • light volume을 그리고 Blue와 Alpha 채널에 index, (1.0-index)를 출력합니다.

각각의 light index에 대해 unpacking 하는 것은 아래처럼 처리 됩니다(Zero index는 라이트가 없다고 가정):

Index1 = Red channel

Index2 = 1.0 - Blue channel (Red 채널과 같다면 무시)

Index3 = Green

Index4 = 1.0 - Alpha channel. (Green 채널과 같다면 무시)

 

이 방법의 장점:

  • 간단한 Unpacking

 

이 방법으로 light index를 packing 했을때의 단점:

  • 최대 4개의 라이트 겹침이 지원됨
  • light index 4개를 위해 light volume 패스 2개가 요구됨
  • 스텐실 버퍼의 사용 – shadow volume 이나 light volume pass가 필요할지도 모릅니다. 만약 2개의 light index만 필요하다면, stencil 은 필요하지 않습니다.
  • 만약 1이나 3 라이트가 임의의 Fragment 에 도달하면, light index 1, 2, or 3, 4 가 같은 index 일 것입니다.

Packing 방식을 사용하여서, 라이트들이 가장 중요한 라이트과 중간 범위의 index인 두번째로 중요한 라이트들을 높고 낲은 Index 를 사용하여 우선순위화 되어질 수 있습니다.

 

이 방법은 위에서 본 CPU 정렬 기술과 조합되어져 Scene light를 2개의 light data array로 정렬하여 담을 수도 있습니다. 각각의 배열은 2개 이상의 라이트가 같은 Intersecting 공간을 공유하지 않는한 Intersecting light가 허용됩니다. 각각의 배열은 그리고 stencil buffer 없이 그려질 수 있습니다 (Red/Green pass 그리고 Blue/Alpha pass).

 

 

Light Index Packing Bit Shifting

Light index packing을 위한 다른 GPU 기반 해결책은 bit shifting 과 packing과 관련있습니다.

다시한번, 8 bit의 light index와 RGBA8 light index texture를 가정한다면, 4개의 light index 겹침이 아래의 스탭을 통해서 그려질 수 있습니다: 

  • Color 버퍼를 0으로 초기화
  • Blend mode를 ONE과 CONSTANT_COLOR(0.25)로 설정합니다. 이것은 이미 존재하는 Color bit를 2단계 아래로 이동 시키고( >> 2 = * 0.25) 2개의 새로운 비트를 숫자의 가장 앞쪽 비트에 추가해줍니다.
  • Light Volume을 그려주고 8 bit index value를 4개의 bit 값으로 분해합니다. 그리고 각각의 2비트 값을 RGBA 채널 상위 비트에 출력합니다. 즉. Red 채널 = (index & 0x3) << 6. 인덱스르 나누는 것은 Offline에서 수행될 수 있고 간단히 Light volume pass의 Color output으로 제공되어질 수 있습니다.

각 Light index unpacking은 쉐이더에서 bit-logic을 처리(Shader model 4)할 수 있거나 혹은 floating point 에뮬레이션 비디오카드를 필요로 합니다. 이어서 나오는 GLSL 코드는 각 light index를 0...1 범위로 unpacking 하는데 floating point math를 사용합니다. - 256 값의 light index texture를 찾는데 적합합니다.

#define NUM_LIGHTS 256.0

// Look up the bit planes texture
vec4 packedLight = texture2DProj(BitPlaneTexture, projectSpace);

// Unpack each lighting channel
vec4 unpackConst = vec4(4.0, 16.0, 64.0 , 256.0) / NUM_LIGHTS;

// Expand the packed light values to the 0.. 255 range
vec4 floorValues = ceil(packedLight * 254.5);

float lightIndex[4];

for(int i=0; i< 4; i++)
{
	packedLight = floorValues * 0.25; // Shift two bits down
	floorValues = floor(packedLight); // Remove shifted bits
	lightIndex[i] = dot((packedLight – floorValues), unpackConst);
}

 

Bit packing은 수많은 서로다른 light overlap counts 와 scene light count 조합을 가능케 합니다. 몇몇의 가능한 조합 리스트가 아래에 있습니다:

 

 

 

Bit packing 방법의 장점은 아래와 같습니다:

  • 얼마마 많은 scene light 그리고 Overalp 이 요구되냐에 따라 확장.
  • 추가 버퍼(즉, stencil) 없이 전통적인 싱글 패스 렌더.

Light index packing을 이 방법으로 했을때의 단점:

  • 복잡한 Unpacking
  • Requires hardware to be bit-precise in blending and floating point math.

* 주의할 점은 65535 의 light 가 사용되어 질때, Light index를 2개의 index로 분리하고 256x256 light data table에서 검색하도록 권장합니다.

 

Bit packing 방법을 사용하면, 가장 낮은 우선순위의 라이트가 먼저 그려지면서 라이트가 우선순위화 되어질 수 있습니다. 만약 Pack의 한계보다 더 많은 라이트가 있다면, 오래된 라이트는 Blending 할때 버려질 것입니다.

 

 

Light Index Geometry Lighting

Light index packing 기술이 선택되면, 다음 스탭은 각 표준 포워드 렌더링 쉐이더가 Light index를 사용하도록 변경하는 것입니다. 

 

만약 여러개의 light index를 사용한다면, 모든 라이팅 연산이 World 나 View 공간에서 계산되어지는 것을 추천합니다. 왜냐하면 라이팅 데이터는 World 나 View 공간 그리고 표면 데이터는 보통 Tangent or 모델 공간에서 제공되어지기 때문입니다. 일반적으로 표면데이터를 라이트 공간으로 변환하는 것이 각각의 라이트 데이터를 표면공간으로 변환하는 것 보다 더 효율적입니다. 

 

다음 스텝은 어떻게 light data look up table 을 Fragment shader에 제공할지와 어떤 데이터를 테이블에 포함해야만 하는지 입니다. 

 

Light 데이터 제공을 위한 확실한 방법은 하나 혹은 더 많은 포인트 샘플 된 텍스쳐 입니다. 텍스쳐는 지원범위가 넓다는 장점이 있지만, 텍스쳐에서 동적데이터를 업데이트 할때는 GPU 파이프라인이 Stall 되지 않도록 주의해야 합니다. 

현재 GPU에서 사용되고 있는 텍스쳐를 갱신하지 않도록 보장 하기 위해서 여러장의 텍스쳐들을 다른 프레임에서 사용할 수 있습니다. 만약 Scene이 World 공간에서 Static light를 만든다면, Frame 당 업데이트는 필요하지 않으며 텍스쳐는 한장만 필요합니다. Lighting data를 제공하는 다른 방법은 constant buffer를(Direct3D에서 사용가능) 사용하는 것입니다, 그러나 이 문서에서는 텍스쳐 접근법에 집중할 것입니다.

 

필요한 데이터 타입은 당신이 제공하려는 Lighting 타입에 따라 결정됩니다. 우리는 Point light에 집중할 것입니다. 왜냐하면 평행 광원은 Distant point light로 에뮬레이션 할 수 있고 spotlight는 Cone volume을 그려서 부분적으로 에뮬레이션 할 수 있습니다. Point light는 Position와 Attenuation 그리고 Light color 데이터가 필요합니다. 모든 이 데이터는 2D 텍스쳐 한장으로 제공될 수 있습니다. - Light index를 x축에 사용하고 light 속성를 y축에 사용하는 것. 그러나 더 실제로는 light 속성을 갱신빈도와 Format 요구사항에 따라 다른 텍스쳐로 분할합니다. 다른 Format과 분할 방식의 실험으로 타겟 하드웨어에서 어떤것이 가장 빠른지 결정합니다.

 

테스트 어플리케이션에서 우리는 Color가 덜 빈번하게 갱신될것이고 낮은 정밀도 만 필요하다고 결정되었습니다. 그러므로 Color는 1D RGBA8 텍스쳐로 제공됩니다. Position와 Attenuation light data는 더 많은 정밀도를 필요로 하며 1D 각 컴포넌트가 32bit 인 RGBA float point texture가 필요합니다. RGB 값은 light View space position 그리고 Alpha는 Attenuation(1/light radius)를 표시합니다. 8bit Light index(255 lights)인 light data textures의 총 크기는 5KB 입니다.

 

주목할 점은 Light index 0은 "none" 혹은 "NULL" 라이트를 표현하고 이 light index를 위한 light buffer 마지막 렌더링에 영향을 줄 수 없는 값으로 채워져야 합니다. (즉, 검은색 라이트 컬러)

 

Light index를 사용하여 light property를 찾으면, 표준 라이팅 방정식이 사용되어 질 수 있습니다. 현대 하드웨어에서 Light index 0일 때는 "early out" 처리가 이득일 수 있습니다.

 

 

Illustration 1: 테스트 어플리케이션의 스크린샷. ~40,000개의 삼각형과 255개의 라이트로 구성된 Scene. Nvidia Geforce 6800 GT의 1024x768 해상도에서, 이 어플리케이션은 Light Indexed Deferred Rendering(LIDR) 과 multi-pass forward rendering을 비교합니다. 측정된 Frame rates : 82 FPS with 1 overlap LIDR, 62 FPS with 2 overlap LIDR, 37FPS with 4 overlap LIDR and 12 FPS with multi-pass forward rendering.

 

Combining with other Rendering Techniques

많은 라이팅 기술은 독립적으로 잘 작동합니다. 그러나 한 어플리케이션에서 다른 기술과 혼합될때 실패합니다. 이 섹션은 Light Indexed Deferred Rendering과 흔한 렌더링 방식을 혼합하는 방법에 대해 이야기 할 것입니다. 

 

Multi-sample Anti Aliasing 

일반적으로 디퍼드 렌더링의 가장 큰 단점은 Multi-Sample Anti-Aliasing(MSAA)의 지원입니다. 다행이도 Light Indexed Deferred rendering에는 몇가지 해결방법이 있습니다.

 

MSAA Technique 1 - MSAA Texture Surfac

모든 렌더타겟(Depth/Stencil, light index texture, main scene)을 MSAA 표면에 그립니다. 그리고나서 포워드 렌더 패스를 할때, light index texture를 screen x, y 그리고 현재 샘플 index를 사용하여 샘플링합니다. MSAA 텍스쳐를 샘플링하는 것은 Direct3D 10 그리고 현대 콘솔 하드웨어에서 가능합니다.

 

반면에 전통적인 디퍼드 렌더링도 역시 이 기술을 MSAA를 위해 사용할 수 있습니다. - 이것은 2가지 주요 단점이 있습니다.

 

  1. 이미 큰 "fat buffers" 들이 2/4/8x 로 사이즈가 커집니다.
  2. 모든 샘플들(2/4/8x Framgment work)에 라이팅 계산이 실행되거나 혹은 샘플들은 보간되어져야 합니다. 이것은 근사된 라이팅을 줍니다. Light indexed deferred rendering은 edge에서 생성되어진 fragment에서 라이팅을 수행하는데만 필요합니다.

  

MSAA Technique 2 - Light Volume Front Faces

일반적으로 디퍼드 렌더링에서 Light volume 을 그릴때, 오직 light volume과 교차되는 표면에만 라이팅이 처리 됩니다. 이것은 일반적으로 후면에서 깊이가 더 큰 경우 stencil을 증가시키는 렌더링 "shadow volume like" 렌더링 기술을 통해서 얻어집니다. - 그리고 나서 앞면을 그리고 깊이가 더 적고 stencil이 0이 아닌 경우만 적용합니다. 깊이 값이 더작은 곳에서만 앞면을 렌더링하므로써, 포워드 렌더링 패스의 모든 fragments에서 발생하는 모든 이후의 lookups 은 Fragment에 영향을 준 모든 가능한 라이트를 얻을 것입니다.

 

이러한 front face 방식은 표준 non-MSAA 텍스쳐를 사용할 수 있다는 장점이 있습니다, 그러나 라이트 교차에는 문제가 있습니다. 이전에 표면을 빛을 비추는 라이트들에 대해서만 Light index의 bit packing에서 count 되어 졌습니다. 눈에서 부터 표면으로 향하는 반직선과 교차하는 모든 Light volume의 앞면은 bit packing count에 계산됩니다. 이것은 표면에 빛을 비추지 않는 라이트 처리에서 쉐이더의 cycle을 낭비하며 표면이 지원하는 라이트의 숫자를 쉽게 채울 수 있습니다.

 

이 기술을 사용하므로써, 표면 Light index count를 빠르게 채우는 비용에서 Depth pre-pass를 제거 가능합니다. (vertex limited scene 에서)  

 

MSAA Technique 3 - Nearest Fragment Edge

Non-MSAA 풀스크린 텍스쳐에서 Fragment 를 찾을 때, 그것은 일반적으로 texel/pixel의 중심을 샘플링하는 텍스쳐좌표를 사용합니다. 만약 이 중심 위치가 폴리곤에 의해서 덮히지 않는다면 - Light index는 제대로 얻어지지 않을 것입니다.

 

만약 당신이 폴리곤에서 근처 texel/pixel을 샘플링하기 위해서 텍스쳐 lookup 좌표계를 조정할수 있다면, 얻어진 light index는 1 픽셀의 정확도 범위 이내야만 합니다. 이 정확도는 아마 전경 오브젝트에 대해서 충분할 것입니다. 그러나 아마 거리가 있는 물체에는 문제가 있을것 입니다.

 

이런 상황이 발생했을때, Fragment를 둘러싼 주변을 검색하므로써 조정 할수 있습니다. 이러한 검색은 표면의 폴리곤 경사도를 계산하므로써 완료될 수 있을 것입니다. 그러나 만약 폴리곤에 sub-pixel fragment로 구성되어져 있다면, (길고 가느다란 폴리콘 케이스) 근처 fragment edge를 탐색하는 것은 실패할 것입니다. 이 기술의 복잡도와 가능한 오차 때문에, 이 기술은 실용적이지 못할 것입니다.

 

 

Transparency

투명도 역시 디퍼드 렌더링에서 주요 문제입니다. 그러나 Multi-sample Anti Aliasing 섹션의 Light volume front face 기술을 사용하므로써, 반투명 오브젝트들은 불투명 객체가 렌더링 된 후에 같은 Light index texture를 사용해 그려질 수 있습니다. 왜냐하면 모든 눈에서 불투명 표면으로 향하는 반적선과 교차하는 모든 light volume light index texture에 포함되어 질 것이기 때문입니다. 그러므로 반투명 객체를 그릴때, 포워드 렌더링 패스는 각 fragment를 충돌 할 수 있는 모든 라이트에 접근 가능할 것입니다.

 

Shadows

어떤 쉐도우가 사용되어지느냐에 따라 쉐도우와 Light indexed deferred rendering과 함께 사용할 수 있는 몇가지 방법이 있습니다.

 

No Combined Shadows

쉐도우를 사용하는 어플리케이션에 Light Indexed Deferred Rendering(LIDR)을 통합하는 가장 쉬운 방법은 그림자가 없는 조명에서만 LIDR을 사용하는 것입니다. LIDR은 라이트를 적용할 때, 표준 포워드 렌더링을 사용하기 때문에 가능합니다.

 

예를들어, 어떤 어플리케이션이 하나의 Primary directional light로 부터 쉐도우를 그리고 쉐도우를 만들지 않는 수많은 포인트 혹은 PFX 라이트를 가지고 있습니다. Directional light shadowing을 처리하는 각 쉐이더들은 LIDR 라이트에 접근하기 위해 간단히 갱신되어 집니다. 다른 옵션은 LIDR 라이트를 쉐도우 패스에서 분리된 패스로 하는 것입니다.

 

Shadow volumes

쉐도우 볼륨은 LIDR 라이트에서 잘 동작합니다. 쉐도우 볼륨은 어느 영역에 쉐도우가 있는지 가리키기 위해서 stencil buffer에 표시를 합니다. Light volume은 이 영역을 무시하도록 하여 그려질 수 있습니다. 그러나 이것은 MSAA / Transparency 지원을 위한 Light Volume Front Faces 기술을 사용하면 동작하지 않습니다.

 

 

Shadow Maps

쉐도우맵은 요구사항에 따라 몇가지 방식으로 지원가능 합니다.

  1. Pre-pass – Light volume을 그리는 패스에서 버퍼의 깊이 값에 접근하므로써, 쉐도우맵 텍스쳐는 Fragment가 쉐도우 안에 있는지 없는지 결정하기 위해 비교되어질 수 있습니다. 만약 Hard shadow 도 괜찮다면, 그림자 안에 있는경우 쉐이더에서 간단히 "discard"합니다. 만약 Soft shadow 생각한다면, "Shadow Intensity" 값을 별도의 렌더타겟에 출력해줄 수 있습니다. 이런 "Shadow intensity" 값은 light index 값과 같은 방식으로 bit-packed/unpacked 되어질 필요가 있습니다. 그리고 포워드 패스에서 라이트의 attenuate 를 위해서 접근하게 됩니다.
  2. Final pass – 쉐도우맵을 3D 텍스쳐 혹은 2D Texture array(DirectX 10)로 packing 하므로써, light index는 쉐도우 맵에 접근하고 포워드 렌더링 패스에서 쉐도우를 계산하는데 사용될 수 있습니다. 이것은 쉐도우맵 매트릭스에 추가적인 라이트 테이블 데이터를 필요로 하며 아마 Shadowed point light를 지원하기는 어려울 것입니다. (6개의 연속된 쉐도우맵 index로 사용가능) 만약 이 기술을 사용하면, 제한된 light index 범위만 쉐도우를 사용하길 권장해드립니다. 즉, Light index 0...3 만 쉐도우를 가짐

Constraining Lights to Surfaces

디퍼드 렌더링의 큰 장점 중 하나는 모든 표면이 동일한 라이트 처리를 하는 것입니다. - 불행히도 이것은 문제가 될 수 있습니다. 아티스트들은 때때로 특정 표면에만 라이트가 적용되기를 원합니다. 이것은 라이트 표면이 빛과 상호작용하는 것을 가리키는 또다른 index를 기록하므로써 전통적인 디퍼드렌더링으로 해결할 수 있습니다, 그러나 이것은 더 많은 버퍼 공간과 더 많은 로직이 Scene 라이팅 패스에서 요구됩니다.

 

그러나 LIDR을 사용하면, 그냥 다른 Light lookup table을 포워드 렌더링 패스에 제공하는 것이 전부입니다. 표면에 닿지 않아야하는 라이트들은 검은색이나 attenuation을 조정하여서 비워지게 할 수 있습니다.

 

만약 LIDR 이 아닌 Light가 표면에 닿는다면, 그 표면의 쉐이더는 LIDR 계산을 간단히 제거할 수 있습니다. 이런 흔한 케이스는 아마 Static geometry의 static light가 light map에 구워져서 들어가 있는 Scene일 것입니다. 이런 Static lights는 런타임에 Dynamic surface으로 접근하기 위해서 LIDR로 그려질 수 있습니다. Static geometry 의 표면은 모든 LIDR 라이트를 무시하거나 라이팅을 위해서 라이트맵을 사용할 수 있습니다.

 

Multi-Light Type Support

대부분은 Scene은 다른 종류의 라이트 타입이 조합되어 만들어 집니다. - 간단한 Directional light 부터 Projected texture를 사용하는 Spot light 까지. 불행히도 Scene 당 여러 라이트 타입은 LIDR과 쉽게 처리되지 않습니다. 몇몇의 해결방법은 아래와 같습니다:

  • 단일 라이트 타입을 LIDR과 사용. 예를들어 만약 Scene이 Directional light와 여러 Point light로 구성되어져있다면, Point light는 LIDR을 사용할 수 있는 반면에 Directional light는 표준 포워드 렌더링으로 그려집니다. Directional light는 역시 "faked" distant point light로 사용할 수 있습니다.
  • Light data와 함께 라이트 타입을 표시하는 플래그를 저장합니다. 이것은 복잡한 쉐이더 로직을 만들고 아마 많은 종류의 라이트 타입에는 실용적이지 못할 것입니다.
  • 각각의 라이트 타입에 대해서 서로 다른 light index buffer를 사용합니다. 이것은 라이트 타입이 고르게 분배되어있지 않는다면, index buffer를 낭비합니다. 이 기술은 복잡한 쉐이더 로직이나 각 라이트 타입에 대해 Scene geometry 가 여러개의 패스를 사용하는 것과 관련되어 있습니다. 

 

Lighting Technique Comparison

 

Light Indexed Deferred Rendering vs. Standard Deferred Rendering

LIDR 의 장점:

  • 포워드 렌더링을 사용하기 때문에 Normal/Position 타입 데이터를 "fat buffers"에 저장할 필요가 없다.
  • 기존 라이트 구조를 계층화 할 수 있다.
  • 버퍼 사이즈가 작다. (Fragment 당 얼마나 많은 라이트를 지원하느냐에 따라 다양함)
  • 반사벡터와 같은 라이트 계산을 한번만 함.
  • 적은 리소스로 MSAA 지원이 가능 함.
  • 투명이 지원됨.

LIDR 의 단점:

  • 특이한 라이트 타입은 지원하기 어렵다. (즉, projected texture light)
  • 각 Fragment 당 얼마나 많은 라이트가 사용될 수 있는지 설정이 필요 함. (현재 구현에는 16개가 최대)
  • Vertex geometry 를 2번 전달해야 한다. - Depth pre-pass 에서 한번, 포워드 패스에서 한번. 주목할 점은 Depth pre-pass는 LIDR에 중요하지는 않지만 많은 최적화가 가능합니다.
  • 그림자 지원이 더 어렵다.

Light Indexed Deferred Rendering vs. Multi-pass Forward Rendering

LIDR의 장점:

  • 라이트 당 Fragment 크기 비용으로 수많은 라이트를 그릴 수 있다.
  • Scene geometry 패스가 2개 뿐이다. – depth only pass 그리고 포워드 렌더 컬러 패스.
  • 개개별 라이트를 위해 Geometry를 여러 조각으로 분해할 필요가 없다. - 거대한 버택스 버퍼에 그릴 수 있다.
  • CPU에서 Object->Light 교차를 계산할 필요가 없음. (그럼자를 만들지 않는 라이트에 대해)
  • 반사 벡터와 같은 라이트 계산과 텍스쳐 lookup 그리고 필더링을 한번만 하면 된다.
  • "mesh-shaped" 라이트를 그릴 수 있습니다. - Sphere/cone 과 같은 라이트 볼륨 모양의 제한이 없다

LIDR 의 단점:

  • 특이한 라이트 타입은 지원하기 어렵다.(즉, projected texture light)
  • 각 Fragment 당 얼마나 많은 라이트가 사용될 수 있는지 설정이 필요 함. (현재 구현에는 16개가 최대)
  • Light index 데이터 저장을 위해서 풀스크린 버퍼가 필요하다.
  • LIDR 을 적용하기 위해서 모든 Scene의 쉐이더를 갱신해야 한다.
  • 적은 수의 오브젝트와 라이트가 있는 Scene에서는 더 느리다.
  • 쉐도우를 지원하기 어렵다.

 

Light Indexed Deferred Rendering vs. Multi-light Forward Rendering

LIDR 의 장점:

  • 라이트 당 Fragment 크기 비용으로 수많은 라이트를 그릴 수 있다.
  • 개개별 라이트를 위해 Geometry를 여러 조각으로 분해할 필요가 없다. - 거대한 버택스 버퍼에 그릴 수 있다.
  • CPU에서 Object->Light 교차를 계산할 필요가 없음. (그럼자를 만들지 않는 라이트에 대해)
  • "mesh-shaped" 라이트를 그릴 수 있습니다. - Sphere/cone 과 같은 라이트 볼륨 모양의 제한이 없다

LIDR 의 단점:

  • 특이한 라이트 타입은 지원하기 어렵다.(즉, projected texture light)
  • 각 Fragment 당 얼마나 많은 라이트가 사용될 수 있는지 설정이 필요 함. (현재 구현에는 16개가 최대)
  • Light index 데이터 저장을 위해서 풀스크린 버퍼가 필요하다.
  • Scene geometry 패스가 2개 뿐이다. – depth only pass 그리고 포워드 렌더 컬러 패스.
  • 적은 수의 오브젝트와 라이트가 있는 Scene에서는 더 느리다.
  • 쉐도우를 지원하기 어렵다.

 

Future Work

 

Fake Radiosity

LIDR을 사용하는 것은 "fake" one-bounce-radiosity 일 수 있습니다. 이것은 아래의 것으로 가능합니다:

  • Light projection 으로 부터 Scene을 그린 후 Depth 와 컬러 값을 저장합니다.
  • 수백개의 작은 Point light sphere를 LIDR 을 사용하고 있는 Scene에 그립니다. 버택스 쉐이더 안에서, 각 라이트를 위치를 설정하기 위해서 depth texture를 이전 light projection pass 에서 lookup 합니다. 라이트 컬러를 얻기 위해서, 컬러버퍼를 같은 light projection pass에서 Lookup 합니다.

이 기술을 사용하면, Radiosity light pass의 첫번째 bound를 흉내거나 splatting indirect illumination을 구현할 수 있을 것입니다.

 

 

Buffer Sharing

대부분의 LIDR 구현은 light index를 저장하기 위해서 분리되어진 버퍼가 필요할 것입니다. 그러나 몇몇 하드웨어에서는 최종 컬러버퍼를 light index texture로 재사용할 수 있을 수 있습니다. Index texture로 접근은 현재 fragment position에 대해서만 처리 되기 때문입니다.

 

Conclusion

이 문서에서 제시된 기술 "Light Indexed Deferred Rendering"(LIDR)의 효율은 표준 포워드 렌더링과 디퍼드 렌더링의 중간 정도에 있습니다. LIDR은 많은 라이트들을 선형적으로 확장 그리고 Scene vertex data 패스를 1개 혹은 2개의 패스로 처리합니다. LIDR은 또한 Index data를 저장하는 스크린 사이즈 크기 텍스쳐 한장과 Light data를 저장하는 작은 크기의 lookup table에 정도의 최소한의 메모리 요구사항을 가집니다. 아주 작은 변경으로, 디퍼드 렌더링에서는 힘든 MSAA와 투명처리도 LIDR이 처리 할 수 있습니다.

 

포워드 렌더링을 사용하는 기존 어플리케이션은 기존 라이팅 솔루션의 위에 LIDR을 계층화 하기 위해서 쉽게 변경되어 질 수 있습니다.

 

반응형