TECHARTNOMAD | TECHARTFLOWIO.COM

TECH.ART.FLOW.IO

[간단정리?]Voxelizations of Shadow for mobile. Case Study.

jplee 2023. 9. 27. 01:33

정리하면서....
2020년인가??? 신동네트웍스에서 테크아트팀 팀장으로 근무 할 때 유니티 코리아에서 개발하고 있던 복셀기반 쉐도우 처리 발표영상을 보고 팀원들에게 정리 해 주기 위해 정말 유투브에 올라온 영상을 죽어라 구간반복(단기기억력이 좋지 않아서요!) 해서 요약 했던 기억이 참....
 

心动

加入心动 不签竞业、不强制996、无限假期、业内标杆的工作环境!你将与匠人们一起,在开放自由的文化中打造不同凡响的游戏。

www.xd.com

돌아와서...
유니티 코리아 스팟라이트팀의 시니어 그래픽스 프로그래머로 계시던?(지금 있는지 모름) 김성대 님 발표 자료는 매우 도움이 된다고 생각 합니다.
어쨌거나~  간략히 켑처 된 발표 자료와 짧게 제가 정리 한 코멘터리 정도로 요약본을 올려보니다. 

신동네트웍스 기술센터의 유니티 엔진 담당 팀장이랑 이것에 대해서 대화 했었습니다... 그때 그 친구가 복셀라이징은 용량때문에 모바일에서 못쓸꺼라고... 그러다군요. 그래서 제가 DAG 압축 부분도 함께 참고해서 봐라고 했습니다.... 그때 유니티 엔진쪽 팀장이 DAG 가 뭐냐고 ... 뭔가 되게 공격적으로 반응했던 기억은 잊혀지지 않습니다. 개인적으로 컴퓨터 그래픽스에 역사가 있는 좋은 교수님 아래에서 전공 석사 이상하고 업계 이력 다 밟고 연구에 매진 하는 사람들만 엔진 담당 엔지니어가 되야 할 것 같다는 생각을 그때 더 확실히 알게 되었.... 쿨럭...

 


이 글은 2019년 유니티 스포트라이트 팀에서 발표한 복셀 기반 그림자 처리 연구 발표를 듣고 제 나름대로 번역한 글입니다. (원래 중국어로 정리 했던거에요)
저는 중국에 거주하면서 두 개의 모바일 MMO RPG(완전 오픈 월드 모바일 MMORPG) 개발에 참여했습니다.
실시간 섀도 처리는 매우 어려운 최적화 난이도입니다.
최신 기술을 사용하진 않았지만 한 씬에서 소량의 드로우 콜만 소모되도록 최적화했습니다.
저는 이를 섀도 프록시 볼륨이라고 부릅니다.
나중에 자세히 소개하겠습니다.
하지만 이 방법은 일관성을 보장하지 않습니다.
또한 실시간 섀도는 저해상도 섀도 맵이지만 Unity 기본 섀도 알고리즘보다 시각적으로 개선된 가변 섀도 기법을 사용하여 처리했습니다.
복셀 기반 섀도잉 기법이 매우 흥미로워서 내용을 요약해서 공유해 보겠습니다.
Unity 스포트라이트 팀에 감사드립니다.

섀도 맵을 압축하여 큰 씬에 조명을 적용해 보겠습니다.
프레젠테이션: 발표자: 김성대 유니티 스포트라이트 팀 그래픽스 프로그래머.
 

  • 요약.
    • 기존 사례 소개.
    • 아이디어 소개.
    • 구현 설명 및 렌더링.
    • 결과 및 성과.

일반적인 캐스케이드 섀도 방식.
장점은 있지만 여러 번의 드로우 콜이 필요합니다. 
 

섀도 마스크 메서드.
또한 표면에 미리 베이크된 라이트맵이 필요합니다.

서명된 디스턴스 필드 섀도(사전 베이크)
더 나은 퀄리티의 섀도를 얻을 수 있지만 표면에 미리 베이크된 라이트맵이 필요합니다.

오늘 소개할 복셀라이즈드 섀도 메서드입니다.
UV가 필요하지 않으므로 전체 씬에 동일한 밀도와 일관성을 보장할 수 있습니다.

그림자 정보는 이미 옥트리 공간에 구워져 있으므로 동적 오브젝트를 추가해도 상관없습니다.
볼류메트릭 라이팅을 사용하는 경우에도 추가 섀도잉이 필요하지 않습니다.

아이디어 소개.
- 그림자 맵 복셀화.
-SVO로 표현.
- DAG 구조로 압축.
 

이해를 돕기 위해 위의 장면을 사용했습니다.

빨간색은 깊이입니다.
일반적인 그림자 처리 방법에서는 위와 같이 깊이를 사용하여 그림자가 있는지 여부를 결정하여 그림자를 그릴 수 있습니다.

복셀 그림자를 구현할 때 공간을 균일한 격자로 분할하여 저장했습니다.
이때 0 또는 1 형식(이진 1비트)으로 저장됩니다.

복셀화 시 4K * 4K * 4K * 1비트에 8GB 용량이 필요합니다.

균일한 그리드를 사용할 경우 비효율적이기 때문에 SVO 구조를 사용합니다.

윤곽선 부분만 처리됩니다.
불필요한 자식은 더 이상 구성되지 않습니다.

부모는 최대 8개의 자식을 소유할 수 있습니다.
먼저 리프 노드가 비트 세트를 소유합니다.
실루엣에 따라 비트 집합을 처리합니다.
상위 노드는 포인팅만 있고 별도의 데이터를 가지고 있지 않습니다.
포인팅은 하지만 해당 정보를 사용하여 정확한 데이터 정보를 추론합니다.
 

8GB에서 3.8메가로 최적화(1단계)
SVO 레벨에서만 사용할 수 있는 구조가 아닙니다.

DAG 구조 압축

DAG 구조를 설명하기 전에 먼저 SVO 단계를 요약해 보겠습니다.
트리 구조를 xyz로 나눕니다.
최대 8개의 자식.
리프 노드만 비트 세트를 소유합니다.
다른 하나는 루트를 포함한 포인팅만 사용합니다.
이해를 돕기 위해 2D 구조로 예제를 설명합니다.
실제로는 옥트리 구조입니다.

DAG 구조에 대한 간략한 설명.
직접 비순환 그래프. https://en.wikipedia.org/wiki/Directed_acyclic_graph 
자식을 공유하는 비순환 구조입니다.
부모는 복제된 자식을 의미합니다.
위 그림을 보면 노드가 겹쳐져 있는 것을 볼 수 있습니다.
이렇게 겹치는 것들을 공유하는 방식으로 압축합니다.

Directed acyclic graph - Wikipedia

From Wikipedia, the free encyclopedia Directed graph with no directed cycles Example of a directed acyclic graph In mathematics, particularly graph theory, and computer science, a directed acyclic graph (DAG) is a directed graph with no directed cycles. Th

en.wikipedia.org

이 그림은 단순화를 위해 2차원 그래프를 사용합니다.
이 예에서는 2의 8제곱에 해당하는 복셀 루프입니다.
총 케이스 수는 256개의 케이스 수를 생성합니다.

리프 노드에는 비트 세트에 대한 정보가 있으며 정렬할 수 있습니다.
정렬 후 가까운 노드를 기준으로 정렬하여 중복을 검색합니다.
중복된 항목은 즉시 삭제합니다.
부모 노드를 다시 업데이트하여 리프 노드의 DAG 단계를 완료합니다.
부모 노드 정렬.
부모 노드를 정렬하여 중복 노드를 검색합니다.
DAG 처리 후 결과는 압축되지만 동일한 결과가 출력됩니다.

  • 압축 가능한 이유: 이진 데이터.
    • 이진 데이터는 케이스 수가 적습니다.
    • Leaf가 가장 작은 경우의 수입니다.
    • 레벨이 올라갈수록 증가하는 케이스 수입니다.
    • 피라미드 형태에서 다이아몬드 형태로의 변환.

DAG 백서를 참조하세요.
압축률 비교.
 

처음 8기가바이트에서 3.8메가바이트로 1차 최적화.
DAG 압축 후 0.2메가로 최적화.

구현 설명 및 렌더링.

  • SVO/DAG 데이터 구조.
  • Build pipeline.
  • SVO/DAG 빌드.
  • 렌더링(트리 탐색).

SVO/DAG Data Structure.
컴퓨트 버퍼에 UInt로 저장합니다.

  • Stride : 4
  • Count : Variable

2*2*2 divided.
노드 크기는 가변적입니다.
컴퓨트 셰이더에서 구현됩니다.

  • Node Header
    • Unused:16bit.
    • Childmask: 16bit.
  • Childmask
    • Use 2bit per child.
    • 0x0001 : lit
    • 0x0002 : shadowed
    • 0x0003 : intersected
      • Child exists.
  • Child pointer#
    • 32bit
    • Stride index(not byte index)

자식 마스크의 구조에 따라 노드의 크기가 달라지는 자식 포인터가 있습니다.
자식이 없으면 헤더만 존재하고 끝납니다. 헤더는 처음 16비트가 사용되지 않습니다.
하위 마스크는 16비트를 사용합니다. 자식당 2비트를 사용합니다.
첫 번째 경우에는 어린이가 빛을 받습니다. 두 번째 경우에는 어린이가 빛을 받습니다.
세 번째 경우에는 교차, 즉 아직 알려지지 않은 것으로 판단합니다. 자식 포인터는 32 비트로 저장됩니다.
그 안에는 보폭 (4 바이트) 인덱스가 있습니다.

  • 리프는 비트셋만 표현합니다.
    • 리프는 2x2x2가 아닌 8x8x1입니다.
    • 리프당 64비트로 표현됩니다.
    • 28케이스가 아닌 264케이스
    • 약 16Exa, 16,777,216Tera
  • 실제로는 케이스 수가 많지 않습니다.

리프가 8개가 되면 8x8x8이 됩니다.
PCF 9x9.
 
 

빌드 파이프라인.

2019년 당시 CPU를 기반으로 구축되었습니다.

GPU에서 최소 최대 단위로 계산됩니다.

이해를 돕기 위한 간단한 코드입니다.
다음 방법을 통해 압축합니다.

8K

16K

루트 노드를 만듭니다.

루핑 중 하위 노드 처리에 대해 자세히 알아보세요.

복셀 바운스와 섀도 밉맵을 비교할 수 있습니다.각 단계마다 CPU에 최소값과 최대값이 있습니다.복셀 상태를 확인할 수 있습니다.

교차된 상태에서는 자식 포인터가 추가됩니다.

헤드 노드에 존재하는 자식 비트마스크 영역의 차별 루프입니다.
판별 과정의 시각화.

여기까지는 SVO 빌드이며, 바깥쪽 부분이 집중적으로 계산되는 것을 알 수 있습니다.

렌더링 결과.

복셀다그 섀도 기능.
3D 데이터 옥트리 → 트리 검색.

hlsl로 구성된 셰이더를 계산합니다.
트리를 검색하면서 루프를 내려갑니다.
원하는 점의 상태를 결정합니다.

Rendering (Tree searching)
비트 연산으로 이동할 자식 위치를 결정합니다.
P를 UInt로 변환합니다. (P는 월드 스페이스라고 가정합니다.)
월드 스페이스 포인트의 첫 번째 공간 변환이 수행될 때 해상도에 따라 UInt로 변환하면 비트로 표시됩니다.
이 비트의 형태로 각 레벨마다 교체되는 비트 표시를 통해 어느 자식으로 이동해야 하는지 바로 알 수 있습니다.
별도의 변환은 복잡하지 않습니다.
월드-공간 → 라이트-공간
위치 → NDC → [0...Res]

어느 위치로 이동해야 할지 아이의 위치를 결정합니다.

  • 자식이 어떤 복셀인지 결정합니다.
    • Lit
    • Shadowed
    • Intersected
  • 라이팅되면 1
  • 그렇지 않으면 0
  • 그렇지 않으면 계속 내려갑니다.

차일드 마스크 세트.
헤더 이동하려는 어린이가 빛을 받고 있는지 여부를 결정합니다.
빛을 받으면 1, 그렇지 않으면 0입니다.
위의 두 가지가 아닌 경우 루프에서 계속 내려갑니다.
 
 

리프 노드 검색.
다른 방법으로 사용되는 검색.
남은 Z 값으로 어느 리프 노드로 이동할지 결정합니다.
단순화를 위해 목록에 가까운 샘플링(2선형 샘플링 등)을 사용합니다.

시각화하여 설명하세요.
이를 조기 종료로 정의했습니다.
이미지 차트에서 왼쪽은 실루엣 영역에서 멀리 떨어져 있으므로 루프가 빨리 끝나고 빠르게 결정됩니다.

이 결과는 다양한 각도에서 계산된 모든 정보를 컴퓨팅 버퍼에 삽입하여 시각화합니다.
일반적으로 그림자를 구울 때는 동적 오브젝트가 그림자 영역에 있을 때 라이트 프로브를 사용하여 동적 오브젝트가 약간 어두워지도록 처리합니다.
하지만 복셀화된 그림자는 이미 그림자 정보를 옥트리 공간에 구워두었기 때문에 동적 오브젝트에 동일한 그림자 효과를 적용할 수 있고 드로우 콜이 별도로 증가하지 않는다는 장점이 있습니다.

결과 및 성능 ( 2019년 개발 버전 기준 )

메모리 사용량 비교.
섀도우맵 16비트(기본 그림자 유형) 4K(거리): 32메가
복셀섀도우 4K(거리): 1.6메가
섀도맵 16비트(기본 섀도 타입) 16K(거리) : 512메가
복셀섀도우 16K(거리): 8.6메가
최적화 1과 2는 부록 파일로 제공됩니다.

  • 장점
    • 고해상도 그림자 조명 사용 가능.
    • 정적 오브젝트 그림자 솔루션.
      • 추가 드로우 콜 없이 동적 오브젝트에 그림자를 드리울 수 있습니다.
      • 볼륨 조명 지원.
      • 원 패스 드로우 콜
  • 단점
    • 사전 계산 필요
    • 트리 검색만 가능.

향후 개선 사항.

  • 렌더링 최적화(트리 탐색).
  • 멀티 씬에서 빌딩
  • 시간 조명
    • 스팟 또는 포인트 라이트
  • 차원 확장(시간이 지남에 따라 일관된 특성이 하나로 압축됨)
  • 완전한 GPU 빌드.
    • 병렬 알고리즘 필요.
  • 닫힌 형태
    • 닫힌 오브젝트에서 오브젝트 표면에 노드를 구성할 필요가 없습니다.
  • 장점
    • 노드 수량 감소
    • 빌드 시간 단축
    • 성능향상
    • 셰도잉 결과 동일성 보장.
  • 섀도 맵 2개 필요.
    • 전면/후면 컬링 렌더링
    • 빠른 깊이 진출
      • 뒷면 컬링 기준
    • 가장 가까운 진입 깊이
      • 앞면 컬링 기준
  • 깊이 값은 섀도 캐스터.
  • 노드에 섀도 캐스터가 없기 떄문에 겹칠 가능성이 있음.
  • 이전 노드가 존재하면 복사 됨.
    • 포인터만 가져오면 된다.
    • 더 이상 서브트리 구성이 불필요.

End of contents.