TECHARTNOMAD | MAZELINE.TECH

TECH.ART.FLOW.IO

번역[GDC2025 Core Concept] Decoding Light: Neural Compression of Global Illumination.

jplee 2026. 5. 8. 03:34

역자의 말: 바이트덴스 누버스 스튜디오에 근무 할 때 항저우 브렌치에 기술교류 출장을 갔던 적이 있습니다. 저도 넷이즈 항저우에 근무 했던 경험이 있고 강남 스튜디오 브렌치 총경리는 저랑 넷이즈에서 같이 근무 했던 후티엔레이가 맡고 있었죠. 같이 기술 교류를 했던 친구중의 한명 같은데 언제 또 텐센트로 이직을 했더군요. 아마 바이트덴스가 게임사업부 인원축소 할  때 쯤인것 같네요. 요즘 GI 쪽에 대한 여러 기술적인 접근과 활용등이 발표 되고 있기에 관심있게 보다가 슬쩍 공유해 봅니다.


저자: 포야(浦夜)

지난달 GDC에서 발표한 내용인데, GDC Vault에 이미 올라와 있길래 이쪽에도 공유합니다.

제 발표는 신경망의 사전 베이킹 글로벌 일루미네이션 데이터 적용에 관한 것입니다. 먼저 제 Advisor인 Amy Ackermann에게 감사드립니다. 더 적절한 제목을 붙여 주었거든요 — 《Decoding Light: 신경망으로 글로벌 일루미네이션 압축하기》.

게임 개발을 한 지 10년이 넘었고, 주로 렌더링을 담당해 왔습니다. 넷이즈와 바이트댄스에서도 근무했으며, 《연운십육성(Where Winds Meet)》과 《Earth: Revival》 등의 프로젝트에 참여했습니다. 최근에는 주로 GI를 맡고 있습니다.

제 부서는 광자기술개발부로, 중앙 기술 조직이기 때문에 제 솔루션은 이미 여러 프로젝트에 적용되었습니다. 그중 한 프로젝트의 영상을 공유하겠습니다.

이번 발표의 핵심은 신경망으로 Irradiance Volume을 압축하여 TOD(Time of Day) 글로벌 일루미네이션을 구현하는 것입니다.

요즘 AI 관련 내용이 정말 많죠. 이미 식상하게 느끼실 수도 있겠습니다. 하지만 신경망의 실제 적용 가능성을 탐색하는 것은 여전히 가치 있는 일입니다. 제 작업이 실무에 영감을 줄 수 있기를 바랍니다.

안정성과 런타임 소모 절감을 위해 사전 베이킹 GI 방식을 채택했습니다. 흔히 사용되는 Lightmap에 비해 Irradiance Volume은 패키지 크기와 메모리 점유가 더 적고, 정적·동적 모델 모두에 적용할 수 있습니다. 워크플로 효율 면에서도 Probe 기반 베이킹이 훨씬 빠릅니다. 게다가 Lightmap은 별도의 2UV가 필요하지만, Probe 기반 방식은 그럴 필요가 없습니다.

최종 릴라이팅 단계에 대형 3D 텍스처를 사용합니다. 텍스처 크기는 플랫폼에 따라 달라지는데, 예를 들어 PC에서는 한 변 256픽셀의 3D 텍스처로 플레이어 중심 한 변 512미터 영역을 커버합니다. 모바일에서는 더 작은 3D 텍스처로 전환합니다.

스트리밍 구현을 위해 《파 크라이 3》에서 소개된 Clipmap 기법을 채택하여, 플레이어가 이동할 때 가장 먼 행의 섹터만 갱신합니다.

또한 UE 엔진의 Volumetric Lightmap 방식처럼 Virtual Texture로 희소 Probe 데이터를 관리하고 메모리를 줄이는 시도도 했습니다. 그러나 완전히 펼쳐진 3D Texture를 사용하는 쪽이 더 낫다고 판단했습니다. 펼쳐진 텍스처는 삼선형 보간으로 이음새 문제를 간단히 처리할 수 있고, 별도의 인덱스 샘플러도 필요 없기 때문입니다.

Probe GI 데이터의 저장 포맷은 매우 효율적으로, Probe당 8바이트만 필요합니다. 보통 2차 구면 조화 함수의 RGB에는 12개의 Float가 필요하지만, 저희는 Indirect Diffuse에 6바이트만 사용합니다. 0차 SH의 RGB에 3바이트, 1차 SH의 3개 계수는 휘도만 저장하여 각 1바이트, 이렇게 Irradiance 저장에 총 6바이트입니다. 추가로 1바이트는 Sky Visibility 전용으로, 아티스트가 날씨에 따라 앨비언트 라이트를 조절할 수 있게 했습니다. 마지막 1바이트는 기타 용도입니다.

오른쪽 위 이미지의 씨에서, 왼쪽 아래는 구면 조화 간접광의 베이킹 결과입니다. 오른쪽 아래는 Sky Visibility의 베이킹 결과인데, AO에 비해 더 부드러운 전환을 보여줍니다.

최종 Irradiance 계산 공식은: Irradiance SH + Sky Visibility × 앨비언트 라이트입니다.

Indirect SH와 Sky Visibility는 모두 사전 베이킹되며, 앨비언트 라이트는 아티스트가 조절할 수 있습니다. 환경 분위기에 맞춰 앨비언트 라이트를 수정하는 방식은 이미 여러 프로젝트에서 라이브 서비스 중입니다. 그러나 하루 중 시간대(TOD) 변화에 따른 광원 방향, 강도, 색상 변화는 사전 베이킹 데이터의 정확성을 떨어뜨릴 수 있습니다.

이를 해결하기 위해 서로 다른 시간대의 GI를 여러 세트로 베이킹할 수 있습니다. 반면, 데이터량은 베이킹 시점 수에 비례해 증가합니다.

먼저 유손실·무손실을 포함한 전통적 압축 방식을 시도해 봤지만, 압축률이 기대에 미치지 못했습니다.

저희의 요구사항은 사실 하나의 함수로 볼 수 있습니다: 입력은 Probe 정보, 출력은 Irradiance. 자연스럽게 신경망을 이 함수로 사용하자는 아이디어가 떠올랐습니다.

MLP의 입력은 네 가지 값입니다: Probe 위치의 x, y, z 좌표와 선형 Weather Time, 모두 [0, 1] 범위로 정규화됩니다. 출력은 6바이트로, SH 0차의 RGB 값과 1차의 세 가지 휘도를 포함합니다. 64개 노드의 히든 레이어를 사용하며, 섹터 복잡도에 따라 히든 레이어 수를 다르게 설정할 수 있습니다. 활성화 함수 ReLU와 Sin을 비교한 결과, SIREN의 오차가 더 작았습니다.

압축률이 매우 이상적입니다. 예를 들어, 10개 시간대의 1.23MB 베이킹 데이터는 이미 무손실 런렛스 압축이 적용된 결과인데, 뉴럴 압축을 적용하면 70KB까지 줄일 수 있었습니다.

MLP 레이어 수는 섹터의 씨 복잡도나 유효 Probe 수에 따라 조절할 수 있습니다. 보통 1km × 1km 씨 기준으로 패키지 내 GI 데이터를 10MB 이하로 유지하면서도 오차를 매우 작게 만들 수 있습니다.

이 그래프는 이터레이션 횟수가 증가함에 따라 훈련 오차가 점진적으로 감소하는 과정을 보여줍니다. 이터레이션 횟수를 10,000회로 설정하면 RGB 오차를 2% 미만으로 달성할 수 있습니다.

왼쪽은 Irradiance 베이킹 결과, 오른쪽은 추론 결과입니다. 거의 동일하게 보이지만, 자세히 살펴보면 아치 부분에서 약간의 차이를 발견할 수 있습니다.

최종 라이팅 결과를 비교하면 오차는 더욱 눈에 띄지 않습니다.

압축률과 오차가 기대에 부합한 뒤, 섹터 간 이음새 문제를 만나게 됩니다.

인코딩과 디코딩 모두 섹터 단위로 진행되기 때문에, 오차가 작더라도 섹터 간 이음새는 불가피합니다. 추가 런타임 비용을 피하기 위해 훈련 시 주변 섹터 데이터를 함께 도입하여, 주변 섹터의 2개 레이어 Probe로 이음새를 방지했습니다. 위 이미지는 주변 섹터를 사용하지 않았을 때의 이음새 효과, 아래 이미지는 해결 후의 결과입니다.

많은 문제를 해결했지만, 가장 많이 받는 질문은 역시 "기기에서 신경망을 어떻게 돌리느냐"입니다. MLP가 매우 작긴 하지만, 여전히 성능 부담이 있습니다.

먼저 Compute Shader로 추론을 구현했습니다. Probe 기준으로 스레드를 분할하여, 각 Probe가 5회의 행렬 곱셈을 포함한 전체 추론 과정을 처리합니다.

구현해 보니 RTX 3090에서도 섹터당 10ms가 걸렸습니다. 앞서 스트리밍 페이지에서 언급했듯이 근거리 GI 3D 텍스처를 채우려면 64개 섹터가 필요한데, GPU 온도가 80도까지 치솔았습니다... 이대로는 안 됩니다.,

먼저 Probe 분포를 관찰해 보니, 공간 내 모든 Probe가 아니라 모델 표면의 유효 Probe만 추론하면 된다는 것을 알 수 있었습니다. Valid Mask를 도입하여 필터링한 결과, 섹터당 추론 시간이 3ms로 줄었지만 여전히 많습니다.

다음으로 스크린 스페이스 GBuffer Depth를 활용하여 화면 깊이 근처의 Probe만 추론 대상으로 삼았습니다. 뷰 프러스텀 밖이거나 가려진 Probe(엄밀히는 GBuffer Depth가 먼 Probe로, 엄격한 오클루전 커링은 아닙니다)는 현재 프레임에서 처리하지 않아도 됩니다. 중간의 Valid Mask가 바로 해당 프레임에서 처리할 Probe인데, 이전보다 훨씬 적습니다. 나머지 Probe는 분산 프레임으로 갱신하면 되므로, 우선순위 정렬 후 추론 시간이 프레임당 1ms로 줄었습니다.

경험 많은 엔지니어라면 다이나믹 브랜칭 문제를 이미 눈치채셨을 겁니다. SMAA 구현을 참고하여 처리할 Probe를 큐로 정리하는 방식을 적용한 결과, 섹터당 추론 시간이 0.7ms까지 줄었습니다.

다음은 스레드 분할 방식입니다. 신경망 추론에서 가장 많은 연산은 행렬 곱셈인데, 처음에는 Probe당 한 스레드를 할당했지만 행렬 곱셈의 병렬 처리 특성을 충분히 활용하지 못했습니다. 이후 행렬 원소의 행·열 곱셈마다 한 스레드를 할당하고, 각 행렬 곱셈마다 Dispatch를 발행하는 방식으로 변경했습니다. Dispatch 수는 늘었지만, 총 소요 시간은 0.3ms로 감소했습니다.

NVIDIA Tensor Core 같은 연산 가속 유닛이 있는 머신에서는 CUDA를 활용해 추론 성능을 더 높일 수 있습니다. 로딩과 추론을 DLL로 구현한 뒤 엔진의 Texture로 리맵합니다. 성능이 향상되면 다시 섹터 전체의 모든 Probe를 한 번에 추론할 수 있게 되고, 추론 및 리맵 빈도도 줄일 수 있습니다. 전체 프로세스는 비동기로 진행되며, 날씨 변화 후 두 프레임 안에 전체 3D Texture를 갱신할 수 있습니다.

모바일에서는 3D Texture 크기와 프레임당 추론 Probe 수를 줄입니다. 모바일 GPU 성능에 따라 Probe 추론 수를 조절하여 프레임당 총 추론 시간을 1.5ms 이하로 유지합니다. Tensor Core나 NPU가 탑재된 기기에서는 한 번에 더 많은 Probe를 처리할 수 있습니다.

결과를 몇 가지 보여드리겠습니다. 이 예시에서는 모든 간접광과 일부 직접광 변화가 NeuralGI로 처리됩니다. SunLight를 제외하면 실시간 라이팅 계산이 없습니다.

또 다른 예시입니다. 신상(神像) 위의 조명 변화도 사전 베이킹된 GI에서 나오며, 훈련과 추론을 거쳐 픽셀 단위의 실시간 라이팅 계산이 필요 없습니다.

사전 베이킹된 Irradiance Volume의 효과가 좋지만, Probe GI 솔루션은 대부분 샘플링 시 라이트 리키지 문제에 시달립니다. 이는 주로 하드웨어 삼선형 보간이 주변 Probe를 끌어오기 때문입니다.

먼저 각 픽셀 샘플링 시 노말 방향으로 약간 오프셋하여 뒷면 Probe의 영향을 줄일 수 있습니다. 이 방법은 지금까지 계속 사용하고 있지만, 모든 문제를 해결하지는 못합니다. 특히 벽 모서리의 픽셀에서요.

Interior Volume을 사용해 실내와 실외를 구분하는 시도도 했습니다.

하지만 아트 에셋의 공간 분할은 단순한 실내외 구분보다 훨씬 복잡합니다.

DDGI 방식을 참고하여 Probe 주변의 GBuffer Depth를 기록합니다. 보간 계산 시 Depth와 Probe-픽셀 간 거리를 비교하여 Probe 가시성을 얻고, 보간 가중치를 조절합니다. DDGI 방식은 인기가 있지만, GBuffer 기록에 대량의 공간이 필요합니다. 구면 조화나 구면 가우시안으로 GBuffer를 압축해 봤지만, 모서리 부분의 깊이 피팅이 잘 되지 않았습니다.

신경망의 피팅 장점을 고려하여, 기존 신경망을 수정해 깊이 피팅 능력을 강화해 보았습니다. 수정된 모델은 여전히 작아서 섹터당 추가 17KB의 공간만 필요하고, 피팅 결과도 괜찮았습니다. 하지만 Irradiance 훈련이 섹터당 1분이면 되는 반면, 깊이 피팅 훈련을 추가하면 3090에서도 40분이 걸려 워크플로에 매우 불리했습니다.

결국 Screen Space Probe Gather 방식으로 돌아왔습니다. 각 Screen Probe가 8×8 픽셀을 담당하며, 트레이스 결과를 작은 radiance map에 기록합니다. 스크린 스페이스 트레이스 시 각 픽셀에서 8개 Probe까지의 도달 가능 여부를 확인하여 보간 가중치를 조절합니다.

Screen Probe Gather를 통해 스크린 스페이스 트레이스와 사전 베이킹 Irradiance Volume을 결합했습니다. SSGI의 고주파 정보와 Irradiance Volume의 저주파 정보를 모두 확보하면서, 스크린 스페이스 방식에서 흔히 발생하는 고스팅 문제도 해결했습니다. Probe 가시성 기반 가중치 조절로 대부분의 라이트 리키지 문제도 해결되었습니다.

왼쪽은 Screen Space Probe Gather를 끄 상태, 오른쪽은 켠 상태의 결과입니다. 라이트 리키지 문제가 대부분 해결된 것을 확인할 수 있습니다.

다음으로 TOD GI의 원경 처리가 필요합니다.

512m × 512m 범위 내에서는 Irradiance Volume을 사용하고, 그 밖의 원경에는 2D GlobalGIMap을 사용합니다.

글로벌 GIMap으로 원경을 처리하는 것은 일반적인 접근입니다. RGB 채널은 탑다운 시점의 간접광 색상을, 알파 채널은 SkyVisibility를 저장합니다. 하지만 앞서 소개한 TOD 변화처럼, GlobalGIMap도 여러 시간대의 색상 변화를 처리해야 하므로 역시 압축이 필요합니다.

BC7과 ASTC로 텍스처를 한 차례 압축할 수는 있지만, 씨과 GlobalGIMap 수가 늘어남에 따라 더 높은 압축비를 원하게 되었습니다. 그래서 CNN을 시도했습니다.

CNN의 압축률은 괜찮았지만, 런타임 추론 속도가 확연한 병목이었고 정밀도도 부족하여 이 방식은 포기했습니다.

텍스처 변화에 연속성이 있다는 점에 착안하여, 비디오 기반 압축을 고려하기 시작했습니다. 텍스처를 비디오 프레임으로 취급하면 시간적 연속성을 충분히 활용하여 이상적인 압축률과 품질을 달성할 수 있습니다.

10개 시간대의 TOD GlobalGIMap을 예로 들면, ffmpeg로 1024×1024 크기의 텍스처 10장을 YUV420p 포맷으로 압축한 결과, 717KB에 불과합니다.

디코딩 과정도 자연스럽습니다. 예를 들어 Unity에서는 네이티브 VideoPlayer를 사용해 시간대별로 텍스처를 디코딩할 수 있습니다. 시간이 변할 때 디코딩은 비동기로 1프레임 내에 완료되어 메인 스레드를 블로킹하지 않습니다.

런타임에서는 훈련·압축된 데이터로 3D Texture를 추론 디코딩한 뒤 스크린 스페이스 Gather를 수행합니다. 비디오에서 GIMap을 추출하고, Deferred 파이프라인에서 Screen Space Gather를 완료합니다.

프로덕션 워크플로에서는 씨의 여러 시간대 베이킹과 훈련·압축 과정을 거쳐야 하며, 이 과정은 최대한 빨라야 합니다.

하드웨어 레이 트레이싱 베이커와 MLP 훈련 워크플로를 자세히 살펴보겠습니다.

시중의 베이커 대부분은 Lightmap 기반으로 개발되어 별도의 2UV 전개 과정이 필요합니다. 저희는 Probe 베이킹이므로 이 과정을 생략할 수 있고, 자체 개발한 DXR 기반 HWRT Baker로 속도를 높였습니다. 각 Probe에서 고정 방향으로 1024개 등의 레이를 발사하고, 여러 차례 반사 결과를 기록하여 SH에 투영합니다.

AO 계산은 일반적으로 주변 레이가 장애물에 히트하는 비율을 기록하는 방식입니다. 저희는 Probe에서 발사한 레이의 충돌 비율뿐 아니라, 오브젝트에 히트한 뒤 이차 반사의 히트 비율까지 고려하여 Sky Visibility의 전환을 더 자연스럽게 만들었습니다.

대부분의 베이커처럼 Directional Light, Point Light, Spot Light, Area Light를 지원합니다. 간접광만 베이킹하는 모드와 직접광까지 GI에 포함하는 모드를 모두 지원합니다.

HWRT 베이커를 통해 섹터당 베이킹 시간을 1분에서 3초 이하로 단축했을 뿐 아니라, 베이킹 과정을 다양하게 커스터마이즈할 수 있게 되었습니다. 예를 들어 GBuffer Depth, SDF 등 특수 데이터를 지원하고, 머티리얼별로 투과율을 다르게 설정할 수 있습니다. 식생의 투과율을 높게 설정하면 SkyVisibility 베이킹 결과가 더 투명하게 나옵니다.

레이 트레이싱 API 개발에는 많은 버그가 따릅니다. 엔진 크래시, DX12 Device Remove 문제 등이 대표적입니다. NSight 같은 디버깅 도구가 필요하고, 저희도 자체 도구를 만들었습니다. 예를 들어 각 Probe 근처의 레이 분포를 시각화하여 어떤 머티리얼이나 불명 오브젝트가 베이킹 에러를 일으키는지 확인할 수 있습니다.

베이킹이 완료되면 다음 단계는 훈련을 통한 압축입니다. PyTorch로 훈련 파이프라인을 구축했지만, 다양한 머신에 배포하기가 쉽지 않았습니다. 저처럼 신경망 초보자에게 환경 구성은 정말 골치 아픈 문제입니다. 제 머신을 세팅한 뒤에도 아티스트들의 다른 머신 환경 문제까지 도와줘야 했는데, 매우 시간이 많이 걸렸습니다. 그래서 Docker 이미지를 도입하여 환경 구성을 간소화했습니다.

전체 프로세스가 사용하기 쉬운 씨 에디터에 통합되어 있습니다. 아티스트가 씨을 수정한 뒤 몇 개 섹터를 선택하고 버튼 하나만 누르면, 베이킹과 훈련 전체 과정이 병렬로 시작됩니다. 64m × 64m 섹터 하나를 처리하는 데 1분이면 충분합니다. 즉, 1km × 1km 씨을 RTX 3090 한 대로 5시간 이내에 처리할 수 있습니다. POI가 적은 씨(단순한 해면이나 지형 등)은 소요 시간을 더 줄일 수 있습니다.

작업을 여러 머신에 분산하면 소요 시간을 몇 배 더 단축할 수 있습니다. 또한 CI 작업을 통해 리소스가 수정될 때 원격 베이킹이나 예약 베이킹을 편리하게 시작할 수 있습니다. Probe와 원경 베이킹, MLP 레이어 수 선택, 후처리, 정기 저장 등의 작업은 아티스트에게 투명하게 처리됩니다. 최종 결과에 문제가 발견되면, 베이킹 결과와 추론 결과를 각각 확인하여 어느 단계에서 문제가 발생했는지 파악할 수 있습니다.

요약하면, 신경망 압축을 통해 GI 저장 요구량을 크게 줄이고 각 플랫폼 간 일관성을 달성했습니다. 원경 텍스처는 비디오 압축 방식도 효과가 좋았습니다.

워크플로 최적화에는 알고리즘보다 훨씬 많은 노력을 쏟았습니다. 혁신의 실제 적용이 혁신 자체보다 더 중요하다고 믿기 때문입니다.

향후 작업에서는 모델 구조를 계속 최적화하고 분산 전략을 개선하여 워크플로를 가속화할 것입니다. GI 압축률과 품질도 계속 향상시킬 것입니다. 더 중요하게는, NPU 가속 추론 프레임워크를 더 많은 디바이스, 특히 모바일 디바이스로 확장하여 GPU와 NPU의 협업을 실현하고자 합니다. 또한 신경망을 게임 개발과 데이터 압축의 다른 영역에도 적용할 계획입니다.

이것들은 제 작업과 관련된 참고 문헌으로, 큰 도움이 되었습니다.

Director와 함께 협업한 동료분들께도 감사드립니다. 지양(吉洋)은 신경망에 더 깊은 이해가 있고, 하이거(海哥)는 비디오 기반 압축을 탐색하라고 조언해 주었습니다. 그리고 아티스트의 작업이 화면 효과에 미치는 영향은 기술보다 훨씬 큽니다.

시청해 주셔서 감사합니다! 교류를 환영합니다!