Game Dev/Article

Forward+ Rendering

AKer 2016. 10. 19. 07:57
반응형

개요

  • 전통적인 Forward Rendering의 확장!
    • 최종 Shading 전에 Light Culling 하나만 추가되었다.
  • 많은 수의 Light를 Culling하고 Pixel에 영향을 주는 것만 저장한다.
  • Light List를 구축하고 이를 Shader에서 사용하지만 메모리는 적게 쓴다.

Standard Forward, Standard Deferred

식상한 얘기들
  • Forward
    • Deferred에 비해서 Material 수는 큰 문제가 되지 않는다.
    • 다만 Light 개수가 많아지면 성능에 문제가 된다.
    • 또 Shader 조합이 폭발적으로 늘어날 수 있고 Light와 Geometry의 CPU 관리가 필요하다.
  • Deferred
    • Geometry 복잡도와 Light 복잡도를 분리하여 많은 Light를 그릴 수 있다.
    • Shader 조합이 비교적 관리할만한 수준이다.
    • 많은 수의 Material을 사용하기 힘들다.
    • 많은 Memory Bandwidth가 필요하다.
    • 투명한 물체 처리가 힘들고, HW MSAA가 어렵다.

하지만 이제 시대가 바뀌었다. ALU 성능이 늘어나고 GPU는 범용적인 용도로도 사용할 수 있도록 많이 유연해졌다. 조금만 고치면 Forward Rendering도 가능한 것이다!

구현

  1. Depth Pre-pass (Optional)
    • 옵션이지만 상대적으로 무거운 Final Shading의 부하를 줄이기 위해서 수행하는 것을 추천
  2. Light Culling
  3. Final Shading

Light Culling

  • Deferred Lighting의 Light Accumulation과 유사함
  • Pixel에 영향을 주는 Light Index를 List로 만든다.
  • 하지만 모든 Pixel에 대해 계산하는 것은 성능상 좋지 않기 때문에 화면을 Tile로 분할하여 Per-Tile 기준으로 수행한다.
    • 해당 Tile에 영향을 주는 Light를 선별하기 위해 Grid Frustum을 생성
    • Grid Frustum은 해상도가 변경될 때 1번만 계산하면 됨
  • Modern GPU에서는 모든 구현을 GPU 상에서 가능하다.



Shading

  • Light Culling 단계에서 Light List가 전달되었다.
  • Material 정보는 Linear Structured Buffer를 통해서 전달 가능하다.
  • 완벽한 Light, Material 정보를 하나의 단계에서 처리하기 때문에 Pixel Quality에 영향을 주는 문제가 모두 해결된다.

Light Culling

기존에 Forward Rendering을 사용하는 곳에서는 모두 사용이 가능하다.

  1. Gather approach
    1. 화면 해상도와 Depth의 min~max 범위에 따라 Tile을 나눈다.
    2. Tile 당 GPU Thread Group을 만들어서 병렬적으로 겹치는 Light에 대해서 체크한다.
    3. Light가 겹치면 TLS의 Local Register에 저장한다.
    4. 모든 Thread 작업이 끝나면 이를 Global Memory에 모은다.
      • Light 개수가 적을 때 사용하면 좋다.
  2. Scatter approach
    1. 어떤 Tile이 Light에 영향을 받는지 Light와, Tile Index를 Buffer에 기록한다.
      • Thread / Light
      • Light Index 순서로 정렬
    2. 1의 Data를 Radix Sort를 이용해 Tile Index로 정렬한다. (Tile 당 Light가 필요하기 때문에)
      • Light 개수가 많을 때 사용하면 좋다.

Thread Number in Thread Group

DirectX의 Compute Shader에서 사용되는 Thread 개수는 다음과 같은 제약이 있다.
  • 64의 배수여야 한다. (=8x8)
  • 1024개를 넘을 수 없다. (=32x32)
따라서 Thread Group의 Dimension은 8x8 ~ 32x32 사이로 결정하고 한 Thread가 NxN Pixel을 커버한다.
예를들어...
  • 해상도 : 1280x720 Pixel
  • Light Tile : 16x16 Pixel
    • 80x45개의 Tile이 생성됨
  • Thread Dimension : 16x16
    • 5x2.8개의 Group이 필요
    • 하지만 2.8개는 불가능하기 때문에 5x3개의 Group으로 계산

성능 비교 (vs Deferred)

  • Prepass, Lighting, Final Shading 3개의 파트로 나누어서 측정하였다.
  • Prepass, Lighting에서는 Forward+가 좋았고, Final Shading에서는 Deferred가 좋았다.
    • Forward+ : Shading 단계에서 Light Index를 모두 읽어야 함
    • Deferred : Shading 단계에서 Light Accumulation Buffer의 float4만 읽으면 됨
  • 결론적으로는 Forward+가 메모리를 더 적게 쓰고 빠르다.
    • 추후 GPU도 Memory Bandwidth를 늘리는 방향보다는 ALU 개수를 늘리는 방향으로 발전할 가능성이 많기 때문에 많은 메모리를 사용하는 Deferred 보다 Forward+가 더 적합할 것이다.

H는 고사양, L은 저사양을 나타내며 저사양 GPU에서 차이가 더 많이 난다.





모든 Rendering Technique이 Light 개수가 많아질수록 급격하게 느려진다.

Forward+는 큰 범위의 Light 보다는 작은 범위의 Light가 여러개 있을 때 좋다.


참고

https://takahiroharada.files.wordpress.com/2015/04/forward_plus.pdf

http://www.3dgep.com/forward-plus/


반응형

'Game Dev > Article' 카테고리의 다른 글

Tiled Rendering  (0) 2016.10.21
Premultiplied alpha  (0) 2016.10.14
Inferred Lighting  (0) 2016.10.13
Light Pre-Pass Renderering (=Deferred Lighting)  (0) 2016.10.09