Unity 검색

GPU 라이트매퍼 자세히 살펴보기

2019년 5월 20일 테크놀로지 | 15 분 소요
공유

Is this article helpful for you?

Thank you for your feedback!

조명 팀에서는 반복 작업의 속도를 높이기 위해 프로그레시브 라이트매퍼(Progressive Lightmapper)를 설계했습니다. 유니티 조명 팀은 조명에 발생하는 모든 변경 사항에 빠른 피드백을 제공하는 것을 최우선 과제로 하고 있으며, 2018.3버전에서는 프로그레시브 라이트매퍼의 GPU 버전을 프리뷰로 공개했습니다. 이제 GPU 라이트매퍼는 CPU 라이트매퍼와 기능 및 시각적 품질 면에서 거의 동등하며 GPU 버전을 CPU 버전보다 10배 더 빠르게 만드는 것을 목표로 삼고 있습니다. 이를 통해 원활한 인터랙티브 라이트매핑이 가능해지면 팀의 생산성을 크게 높일 수 있습니다.

따라서 조명 팀에서는 AMD의 오픈 소스 레이트레이싱 라이브러리인 RadeonRays를 사용하여 GPU 라이트매퍼에 몇 가지 주요 기능 및 최적화(파워 샘플링, 레이 압축 및 커스텀 BVH 순회)를 구현했습니다.

GPU 라이트매퍼는 더 높은 성능을 달성하는 동시에 CPU 라이트매퍼와 동일한 기능을 제공하기 위해 설계되었습니다.

  • 편향되지 않은 인터랙티브 라이트매핑
  • CPU 및 GPU 백엔드 간 기능 동등성
  • 컴퓨팅 기반 솔루션
  • 최대 성능을 위한 Wavefront 경로 트레이싱

아티스트가 시각적 품질을 개선하고 창의성을 발휘하기 위한 핵심 요소는 바로 반복 시간입니다. 이를 해결하기 위해 인터랙티브 라이트매핑을 목표로 전반적인 베이킹 시간을 크게 줄이고 사용자 경험에서 즉각적인 피드백을 제공받고자 했습니다.

개발 과정에서 여러 흥미로운 과제를 해결했습니다. 이 글에서는 그 중 일부를 살펴보겠습니다.

프로그레시브 피드백

라이트매퍼에서 사용자에게 프로그레시브 업데이트를 제공하기 위해 설계를 몇 가지 수정했습니다.

사전 컴퓨팅 및 캐시된 데이터 없음

Unity에서는 직접 조명이 캐시되어 간접 조명에 재사용될 수 있지만 직접 조명에 복사 조도 또는 가시성을 캐시하지는 않습니다. 일반적으로 베이킹하는 동안에는 데이터를 캐시하지 않고 지연이 발생하지 않는 작은 계산 단계를 선호하며 프로그레시브 및 인터랙티브 디스플레이를 제공합니다.

이 콘텐츠는 Targeting Cookies 카테고리를 수락해야만 동영상을 시청할 수 있도록 허용하는 타사 제공업체에서 호스팅합니다. 이러한 제공업체의 비디오를 보려면 쿠키 환경 설정에서 Targeting Cookies 카테고리를 수락하시기 바랍니다.

GPU 라이트매퍼의 컨트롤 흐름 개요. 제작자/소비자 접근 방식을 통해 GPU 라이트매퍼가 비동기식으로 작동하는 동안 씬을 연속적으로 편집할 수 있습니다. 준비가 완료되면 결과가 에디터에 표시됩니다.

씬은 매우 클 수 있고 많은 라이트맵을 포함할 수도 있습니다. 가장 효율적으로 처리하려면 현재 보이는 영역부터 베이킹해야 합니다. 이를 위해 먼저 화면에서 가장 통합되지 않은 텍셀이 포함된 라이트맵을 감지한 다음 해당 라이트맵을 렌더링하면서 보이는 텍셀의 우선 순위를 지정합니다(화면에 보이지 않는 텍셀은 화면에 보이는 텍셀이 모두 통합된 후 베이크됩니다.).

텍셀의 가시성은 카메라 절두체에 위치하고 씬의 정적 지오메트리로 가려지지 않은 경우 보이는 것으로 정의합니다.

빠른 레이트레이싱을 활용하기 위해 GPU에서 컬링을 수행합니다. 아래는 컬링 작업도입니다.

컬링 작업을 통해 다음 두 가지 결과를 얻을 수 있습니다.

  • 라이트맵에서 각 텍셀의 가시성을 저장하는 컬링 맵 버퍼를 얻을 수 있습니다. 이 컬링 맵 버퍼는 렌더링 작업에 사용됩니다.
  • 현재 라이트맵에서 보이는 텍셀 수를 표시하는 정수를 얻을 수 있습니다. 이 정수는 향후 라이트맵 스케줄링을 조정하기 위해 CPU에서 비동기식으로 다시 읽어옵니다.

아래 동영상에서 컬링의 효과를 확인할 수 있습니다. 데모이므로 베이킹은 중간에 중단됩니다. 씬 뷰가 이동할 때 초기 카메라 위치 및 방향에서는 보이지 않았던 아직 베이크되지 않은 텍셀(검정색)을 볼 수 있습니다.

성능상의 이유로, 가시성 정보는 카메라의 상태가 ‘안정화’될 때만 업데이트됩니다. 또한 슈퍼샘플링은 고려하지 않습니다.

성능 및 효율성

GPU는 많은 양의 데이터를 수집하고 해당 데이터에 모두 동일한 작업을 수행하는 ‘처리량’에 최적화되어 있습니다. 또한 GPU는 이러한 가속화를 달성하는 동시에 많은 코어로 구성된 CPU보다 더 강력하고 비용 효율적입니다. 그러나 하드웨어 설계상 지연의 측면에서 보면 GPU는 CPU만큼 우수하지 않습니다. 그래서 GPU의 기본 특성인 병렬 계산을 최대한 활용하기 위해 CPU와 GPU 간 동기화가 되지 않는 데이터 기반 파이프라인을 사용합니다. 또한 성능을 개선하고 사용자 경험을 측정하기 위해 시간에 따른 시각적 영향을 나타내는 컨버전스 비율을 사용합니다. 더불어 효율적인 알고리즘도 필요합니다.

데이터 기반 파이프라인

GPU는 대용량 데이터 세트에 최적화되어 지연이 발생하는 대신 높은 처리량을 자랑합니다. 또한 일반적으로 CPU에서 미리 채워 둔 커맨드 대기열에 의해 작동합니다. 이러한 연속적인 대형 커맨드 스트림으로 GPU를 포화 상태로 만듭니다. 아래에서는 조명 팀에서 처리량을 최대화하고 기본 성능을 극대화하기 위해 사용 중인 방법을 살펴보겠습니다.

Unity 파이프라인

GPU 라이트매핑 데이터 파이프라인은 다음 원칙을 기반으로 합니다.

  1. 데이터는 번만 준비합니다.

이 시점에서 메모리 할당을 줄이기 위해 CPU와 GPU가 동기화될 수 있습니다.

  1. 베이킹이 시작되면 이상 CPU GPU 간의 동기화를 진행하지 않습니다.

CPU는 사전 정의된 워크로드를 GPU로 전송합니다. 이 워크로드는 경우에 따라 지나치게 적은 양일 수 있습니다(예를 들어 4번의 반사를 사용할 때 모든 간접 레이가 2번째 반사 후에 끝나 초반에 실행해야 할 커널이 대기열에 존재하는 경우).

  1. GPU 레이나 커널을 생성할 없습니다.

대신 빈 작업 또는 매우 작은 작업을 처리하게 됩니다. 이 과정을 효율적으로 처리하기 위해 데이터와 명령어의 일관성을 최대화하는 방식으로 커널을 작성해야 합니다. 이는 데이터 압축을 통해 처리되며 이 부분에 대해서는 나중에 자세히 다루겠습니다.

  1. 베이킹이 시작되면 CPU GPU 동기화뿐만 아니라 GPU 버블이 없도록 합니다.

예를 들어 일부 OpenCL 커맨드는 비동기 버전에서도 clEnqueueFillBuffer 또는 clEnqueueReadBuffer와 같은 작은 GPU 버블(GPU가 처리할 작업이 없는 순간)을 생성할 수 있으므로, 이러한 상황을 최대한 피해야 합니다. 또한 데이터는 가능한 오랫동안 즉, 렌더링과 합성이 완료될 때까지 GPU에 남아 처리되어야 합니다. 추가 처리를 위해 데이터를 CPU로 다시 가져와야 하는 경우 이를 비동기식으로 수행하며 다시 GPU로 데이터를 보내지 않습니다. 예를 들어 심 스티칭은 현재 CPU 포스트 프로세싱입니다.

  1. CPU 비동기 방식으로 GPU 로드를 조정합니다.

카메라 뷰가 변경되거나 라이트맵이 완전히 통합되었을 때 렌더링되는 라이트맵을 변경하면 약간의 지연이 발생합니다. CPU 스레드는 뮤텍스 경합을 피하기 위해 잠기지 않은 대기열을 사용하여 이러한 리드백(되읽기) 이벤트를 생성하고 처리합니다.

GPU에 적합한 작업 크기

GPU 아키텍처의 주요 기능 중 하나는 광범위한 SIMD 명령어입니다. SIMD는 단일 명령 다중 데이터(Single Instruction Multiple Data)를 의미합니다. 일련의 명령이 이른바 Warp 또는 Wavefront 내부에 있는 특정 분량의 데이터에 록스텝(Lockstep) 방식으로 순차적으로 실행됩니다. 이러한 Wavefront/Warp의 크기는 GPU 아키텍처에 따라 64, 32 또는 16의 값을 갖습니다. GPU에서는 단일 명령이 여러 데이터(단일 명령 다중 데이터)를 동일하게 변환하지만 유연성을 높이기 위해 SIMD 구현 시 분산된 코드 경로를 지원할 수 있습니다. 예를 들어 서브셋을 작업하는 동안 일부 스레드를 비활성화했다가 다시 합치는 작업을 수행할 수 있습니다. 이를 단일 명령 다중 스레드(Single instruction multiple threads, SIMT)라고 합니다. 그러나 Wavefront/Warp 내의 분산 코드 경로는 SIMD 유닛의 극히 일부만 사용한다는 단점이 있습니다. 더 자세한 내용은 이 블로그 게시글을 참조하시기 바랍니다.

 

마지막으로 SIMT 개념을 확장하면 GPU가 SIMD 코어당 많은 Warp/Wavefront를 유지할 수 있습니다. Wavefront/Warp가 느린 메모리 액세스를 대기 중인 경우, 스케줄러가 다른 Wavefront/Warp로 전환하고 작업을 계속 진행할 수 있습니다(대기 중인 작업이 충분한 경우). 그러나 이 방법이 실제로 효과가 있으려면 점유율(대기 중인 작업의 양)이 높아질 수 있도록 컨텍스트당 필요한 리소스의 양이 적어야 합니다.

요약하자면 다음을 목표로 해야 합니다.

  • 많은 양의 활성 스레드
  • 이상 분기 (Divergent Branches) 회피
  • 높은 점유율

커널 코드에서는 점유율을 높게 유지하는 게 가장 중요하지만 블로그에서 다루기에는 광범위한 주제이므로 참고할 만한 리소스를 소개하겠습니다.

일반적으로 로컬 리소스, 특히 벡터 레지스터 및 로컬 공유 메모리를 적게 사용하는 것이 목표입니다.

GPU에서 직접 조명을 베이크하는 데 어떤 워크플로를 사용할 수 있는지 살펴보겠습니다. 이 섹션은 주로 라이트맵을 다루지만, 광원 프로브도 가시성이나 데이터 점유율이 없다는 점을 제외하고 매우 유사한 방식으로 작동합니다.

참고: BVH(바운딩 볼륨 계층, Bounding Volume Hierarchy)는 레이/삼각형 교차를 위한 가속화 구조를 의미합니다.

여기에는 다음과 같은 문제가 몇 가지 있습니다.

 

  • 이 예에서 라이트맵 점유율은 44%(9개 중 4개 텍셀 점유)이므로 GPU 스레드의 44%만 실제로 사용되었습니다. 또한 메모리에 유용한 데이터가 부족하므로 점유되어 있지 않은 텍셀에도 대역폭을 할애합니다. 실제는 라이트맵 점유율이 50%에서 70% 사이일 때 가장 큰 효과를 볼 수 있습니다.
  • 데이터 세트가 너무 작습니다. 이 예제는 단순성을 위해 3×3 라이트맵을 사용하지만, 일반적인 경우인 512×512 라이트맵조차도 최신 GPU가 최고의 효율을 발휘하기에 부족합니다.
  • 이전 섹션에서는 뷰 우선 순위 지정 및 컬링 작업에 관해 설명했습니다. 점유된 일부 텍셀이 현재 씬 뷰에서 보이지 않아 베이크되지 않으므로 위의 두 가지 문제가 더욱 부각되어 점유율 및 전반적인 데이터 세트가 훨씬 낮아지게 됩니다.

 

이 문제를 해결하는 과정에 AMD와의 협업을 통해 레이 압축이 추가되었습니다. 레이 압축은 레이트레이싱 및 셰이딩 성능을 크게 향상시킵니다. 간단히 말해, 모든 레이 정의를 인접한 메모리에 생성하여 Warp/Wavefront의 모든 스레드가 자주 사용되는 데이터를 가지고 작업할 수 있도록 합니다.

또한 각 레이와 연관된 텍셀의 인덱스를 레이 페이로드에 저장하고 전역 압축 레이 수도 함께 저장합니다.

압축 워크플로는 아래와 같습니다.

레이를 트레이싱하는 커널과 셰이딩하는 커널 모두 이제 자주 사용되는 메모리에서만 실행 가능하여 코드 경로의 분산을 최소화할 수 있습니다.

다음 단계에서는 뷰 우선 순위 지정이 활성화된 경우 데이터 세트가 너무 작다는 문제를 해결해야 합니다. 먼저 지오메트리 버퍼 표현과 레이 생성이 서로 연관되지 않게 합니다. 단순히 생각하면 텍셀당 하나의 레이만 생성하는 겁니다. 하지만 실제로는 더 많은 레이가 필요하므로 텍셀당 여러 개의 레이를 생성하면  GPU가 처리할 양을 늘릴 수 있습니다. 워크플로는 아래와 같습니다.

압축 전에 텍셀당 여러 레이를 생성하는데 이를 확장이라고 합니다. 또한 수집 단계에서 올바른 대상 텍셀로 누적되는 메타 데이터도 생성합니다.

확장 및 수집 커널은 자주 실행되지 않습니다. 실제로는 모든 광원을 확장한 후 직접 조명은 셰이딩하고 간접 조명은 모든 반사를 처리하여 수집 단계는 한 번만 거칩니다.

이러한 기술을 통해 GPU를 포화시키기에 충분한 작업을 생성하고 중요한 텍셀에만 대역폭을 사용할 수 있습니다.

다음은 텍셀당 여러 레이를 쏠 때의 이점입니다.

  • 뷰 우선 순위 지정 모드에서도 활성 레이 세트는 항상 대형 데이터 세트가 됩니다.
  • 확장 커널이 연속 메모리에서 같은 텍셀을 대상으로 하는 레이를 생성하므로 준비, 트레이싱 및 셰이딩이 모두 매우 일관된 데이터에서 수행됩니다.
  • 확장 커널은 점유율 및 가시성을 처리하여 준비 커널을 크게 단순화하고 더 빠르게 만듭니다.
  • 확장되었거나 작업 중인 데이터 세트 버퍼의 크기는 라이트맵 크기와는 별개입니다.
  • 텍셀당 레이의 수는 적용되는 알고리즘에 따라 결정되며, 어댑티브 샘플링으로 확장될 수도 있습니다.

간접 조명은 더 복잡하지만 매우 유사한 개념을 사용합니다.

참고: 첫 번째로 반사되는 환경 레이는 직접 레이로 간주합니다.

간접광을 사용할 때는 여러 번의 빛 반사를 수행해야 하며, 각 반사는 임의의 레이를 버릴 수 있습니다. 따라서 자주 사용되는 데이터에 대한 작업을 계속하기 위해 반복적으로 압축합니다.

현재 사용하는 휴리스틱(Heuristic) 알고리즘에서는 텍셀당 동일한 양의 레이를 사용합니다. 목표는 매우 프로그레시브한 출력이므로 이를 자연스럽게 확장하면 어댑티브 샘플링을 사용해 휴리스틱을 개선하면 현재 결과에 노이즈가 많은 곳에 더 많은 레이를 쏠 수 있습니다. 또한 휴리스틱은 하드웨어의 Wavefront/Warp 크기를 인식하여 메모리와 스레드 그룹을 모두 더 일관되게 실행하는 것을 목표로 할 수 있습니다.

투명/반투명

GPU 라이트매퍼로 베이크된 ArchVizPRO 에셋

투명/반투명에 대한 사용 사례는 매우 많습니다. 투명 및 반투명을 처리하는 가장 일반적인 방법은 레이를 드리우고 교차점을 감지하고 머티리얼을 가져오고, 발생한 머티리얼이 반투명하거나 투명한 경우 새로운 레이를 예약하는 겁니다. 그러나 현재 이 경우에는 GPU가 성능상의 이유로 레이를 생성할 수 없습니다. 이에 대한 내용은 앞서 살펴본 데이터 기반 파이프라인 섹션을 참조하시기 바랍니다. 또한, 엄청난 성능 저하로 이어질 수 있으므로 최악의 경우 충분한 레이를 미리 예약하도록 CPU에 요청하는 것도 사실상 불가능합니다.

그래서 하이브리드 솔루션을 사용하여 반투명과 투명을 다음과 같이 다르게 처리했습니다.

투명(구멍이 있어 머티리얼이 불투명하지 않은 경우). 이러한 경우 레이가 확률 분포에 따라 머티리얼을 통과할 수도 있고, 반사되어 나올 수도 있습니다. 따라서 CPU가 사전에 준비한 워크로드를 변경할 필요가 없으며 씬에 독립적입니다.

반투명(머티리얼이 통과하는 광원을 필터링하는 경우). 이 경우 근삿값을 계산하고 굴절은 고려하지 않습니다. 다시 말해 머티리얼이 광원의 컬러는 바꿀 수 있어도 방향을 바꾸지는 않습니다. 이를 통해 BVH를 수행하는 동안 반투명을 처리합니다. 즉, 많은 컷아웃 머티리얼을 쉽게 처리하고 씬의 반투명 복잡도에 따라 매우 효과적으로 스케일이 가능합니다.

하지만 BVH 순회의 순서에 문제가 있습니다.

일단 레이를 따라 교차된 각 삼각형의 반투명 감쇠만 관심 대상이므로 오클루전 레이는 문제가 되지 않습니다. 곱셈은 순서가 달라도 같은 값이 나오므로, BVH 순회의 순서가 잘못되어도 문제가 없습니다.

그러나 교차 레이의 경우에는 삼각형에서 멈추고(삼각형이 투명한 경우 확률적 방식으로) 레이의 원점에서 도달점까지 각 삼각형의 반투명 감쇠를 수집해야 합니다. BVH 순회의 순서가 맞지 않기 때문에 우리가 선택한 솔루션은 먼저 교차만 실행하여 도달점을 찾고 반투명에 도달한 경우 레이를 표시하게 됩니다. 결국 교차 레이의 원점에서 교차 레이의 도달점까지 표시된 모든 레이에 추가로 오클루전 레이를 생성합니다. 효율성을 높이기 위해 오클루전 레이를 생성할 때 압축을 사용하고 교차 레이에 반투명 처리가 필요하다고 표시된 경우에만 추가 작업을 수행했습니다.

AMD와의 협력으로 RadeonRay의 오픈 소스를 유니티의 요구에 맞게 조정하고 커스터마이즈한 덕분에 이런 작업이 가능했습니다.

효율적인 알고리즘

지금까지 기본 성능과 관련해 유니티가 진행한 내용을 살펴보았습니다. 그러나 이는 시작에 불과합니다. 초당 높은 샘플링도 중요하지만 결국 가장 중요한 것은 베이크 시간입니다. 조명팀은 캐스트한 모든 레이에서 최고의 결과를 얻기 위해 지난 수십 년간 연구를 진행해 왔습니다. 참고용 리소스는 다음과 같습니다.

1주 안에 레이트레이싱하기

레이트레이싱: 2주차

레이트레이싱: 평생의 연구 과제

Unity GPU 라이트매퍼는 순수 확산 라이트매퍼입니다. 이는 광원과 머티리얼의 상호작용을 크게 간소화하고 반짝임과 노이즈 감쇠를 지원합니다. 그러나 컨버전스 비율 개선이라는 과제가 남았습니다. 다음은 유니티 조명 팀에서 사용한 기술의 종류입니다.

러시안룰렛

각 빛의 반사마다 누적된 알베도를 기반으로 경로를 확률적으로 제거했습니다. 이에 대한 자세한 설명은 Eric Veach의 논문(67페이지)에서 확인하실 수 있습니다.

환경 중요도 기반 멀티 샘플링(MIS)

분산이 높은 HDR 환경은 출력에 상당한 양의 노이즈를 일으킬 수 있으며, 만족스러운 결과를 얻기 위해서는 엄청난 수의 샘플이 필요합니다. 그래서 환경을 먼저 분석하고 중요한 영역에 따라 샘플링하여 환경을 평가하기 위한 특수한 샘플링 전략 조합을 적용했습니다. 중요도 기반 멀티 샘플링이라고 불리는 이 접근 방식은 환경 샘플링에만 국한되지 않으며 Eric Veach의 논문(252페이지)에서 처음으로 제안되었습니다. 이 작업은 그르노블에 있는 Unity Labs와의 협업을 통해 진행되었습니다.

많은 광원

각 빛의 반사마다 확률적으로 하나의 직접광을 선택하고 공간 그리드 구조로 표면에 영향을 주는 광원의 수를 제한했습니다. 이 작업은 AMD와의 협업을 통해 진행되었습니다. 광원 샘플링은 품질에 매우 중요한 역할을 하므로 조명 팀은 현재 광원 문제에 대해 더 깊이 살펴보고 있습니다.

GPU 라이트매퍼와 HDRP를 사용해 렌더링된 유니티 런던 지사. 한 씬에 광원이 많이 포함되어 있다.

노이즈 제거

노이즈 제거에는 경로 트레이서의 출력에 대해 훈련된 AI Denoiser를 사용했습니다. Jesper Mortensen의 유니티 GDC 2019 발표를 참고하시기 바랍니다.

글을 마무리하며

지금까지 데이터 기반 파이프라인, 기본 성능에 대한 관심 및 효율적인 알고리즘이 어떻게 결합되어 GPU 라이트매퍼를 통한 인터랙티브 라이트매핑 환경을 제공하는지 살펴봤습니다. GPU 라이트매퍼는 현재 개발 중이며 지속적으로 개선되고 있습니다.

GPU 라이트매퍼와 관련된 여러분의 의견을 들려주세요!

 

이 게시글을 흥미롭게 보셨고 새로운 도전에 관심이 있다면 코펜하겐 지사의 조명 개발자 포지션에 지원해 보세요!

---

Unity에서 그래픽스를 최적화하는 방법은 튜토리얼을 참고하시기 바랍니다.

2019년 5월 20일 테크놀로지 | 15 분 소요

Is this article helpful for you?

Thank you for your feedback!