TECHARTNOMAD | TECHARTFLOWIO.COM

TECH.ART.FLOW.IO

[번역][역자각주]Nubis3: Methods (and madness) to model and render immersive real-time

jplee 2023. 9. 12. 14:28

역자의 말.
차세대 게임이라는 단어가 산업계 전반에 대두되었을 때 아티스트와 엔지니어들이 가장 먼저 구현하고자 했던 몇 가지 요소들이 있습니다.
그 중에 거의 필수적으로 들어가는 것이 있는데 바로 채적 구름 효과겠죠. 볼류메트릭 클라우드 효과를 말합니다.
보통 차세대 라고 하면 콘솔 디바이스 또는 PC 하드웨어 기반일 것입니다. 특히 이러한 정의 즉 차세대기 라는 말은 주로 콘솔 산업계에서 먼저 대두 됩니다. 차세대 콘솔 하드웨어 라는 말도 많이 들어 봤을겁니다. 이제 벌써 PS6 가 나올 시기를 기다리는 때가 되었고 PC 에서는 RTX4 번 대가 사용화 되었으며 RTX5 번대에 대한 루머가 돌기 시작합니다. 무어의 법칙보다 더 가파른 로그 곡선을 그릴만큼 하드웨어 발전은 매우 빠르며 MPU 까지 등장 한 시점에서 앞으로 어떤 그래픽 처리가 차세대에 속하게 될지 기대가 되는 시대에 살게 되었습니다.


 
https://advances.realtimerendering.com/s2023/index.html#Nubis3

SIGGRAPH 2023 Advances in Real-Time Rendering in Games course

Jorge Jimenez is the Director of Creative Engineering & General Manager at Striking Distance Studios Spain, where he pushes the boundaries of what is possible at the intersection of art and technology. Jorge is a passionate real-time graphics researcher wi

advances.realtimerendering.com

Abstract: 
우리는 6개월도 채 되지 않아 매우 상세하고 몰입형 복셀 기반 클라우드 렌더러 및 모델링 접근법을 개발했습니다. 누비스 복셀 클라우드는 지상에서 볼 때 하루의 시간 주기를 지원하는 전통적인 체적 스카이박스 역할을 하며, 하늘의 날아다니는 마운트 후면에서 탐색할 때 높은 프레임 레이트와 대기 게임 플레이를 지원합니다. 이러한 목표를 달성하기 위해 압축 된 signed distance fields 를 사용한 ray march 가속, 유체 시뮬레이션 기반 클라우드 모델링, 메모리 액세스 병목 현상을 방지하는 방법, ray 샘플링 가속, 어두운 가장자리 및 내부 광채와 같은 클라우드 고유 조명 기능을 근사화하는 새로운 방법 등의 솔루션을 통해 몰입형 체적 클라우드 렌더링의 몇 가지 미해결 문제를 해결하거나 완화했습니다. 이번 강연에서는 이러한 주제를 자세히 다루며 복셀 기반 클라우드가 제시하는 이점과 기회를 선호하여 2015년과 2017년 실시간 렌더링 교육 과정의 발전에서 소개한 실시간 체적 클라우드 렌더링의 2.5D 방법을 뒤로하고 있는 추론을 살펴볼 수 있습니다.

 
Speaker Bio: 

Andrew Schneider는 Guerrilla의 Atmospherics Lead입니다. 그는 Nubis 볼륨 클라우드 시스템을 개발하고 Horizon 프랜차이즈 게임의 모든 Atmospherics를 위한 응집력 있는 솔루션을 개발하는 데 시간을 보내고 있습니다. 그는 Advances in Real Time Rendering Course에서 실시간 클라우드 렌더링에 대한 세 번의 강연과 장편 애니메이션을 위한 볼륨 렌더링에 대한 SIGGRAPH에 대한 세 번의 강연을 진행했습니다.
 
 

감사합니다. 
50회 시그라프에서 게릴라를 대표하여 이 강좌의 일부로 다시 참여하게 되어 영광입니다. 
시그래프와 이 강좌는 개인적으로 매우 중요한 의미를 지니고 있습니다. 대학 졸업 후 첫 직장을 시그라프에서 얻었거든요. 이곳에서 많은 친구들을 사귀었고, 놀라운 작품들이 세상에 소개되는 것을 지켜볼 수 있었습니다. 아이디어를 공유할 수 있는 아티스트와 엔지니어 커뮤니티를 발견한 것은 행운이었어요. 2011년부터 이 컨퍼런스에서 클라우드 강연을 한 것이 이번이 여섯 번째인 것 같습니다. 제가 좀 강박증이 있거든요. 특히 이번 강연은 오프라인 렌더링 기술이 AAA급 실시간 게임에 적용된다는 점에서 저에게 특별한 의미가 있습니다. 우리 모두가 알고 있었지만 아직 적절한 열쇠를 찾지 못해 잠
겨 있던 문을 열 수 있기를 바랍니다.
 
 

한계를 뛰어넘는다는 말은 테스트 파일럿과 엔지니어가 비행기를 작동 범위의 가장자리까지 운전하는 행위를 설명할 때 사용하지만, 더 넓게는 일반적인 한계를 넘어 급진적이거나 위험하다고 여겨지는 새롭고 다른 아이디어를 시도하는 것을 의미할 수 있습니다. 
실시간 렌더링이 가장 빠른 비행기를 설계하고 테스트하는 것만큼 멋지지는 않겠지만, 우리의 볼류메트릭 클라우드 렌더러인 누비스를 단 몇 달 만에 DLC 확장을 위해 재설계하기로 한 결정도 같은 의미로 해석할 수 있습니다. 오픈 월드 게임을 위한 몰입도 높은 고해상도 실시간 복셀 기반 클라우드 렌더링은 최신 그래픽 하드웨어에서도 거의 불가능한 메모리 및 성능 문제로 인해 파일럿 테스트의 장벽과도 같았습니다. 장편 애니메이션과 VFX 제작사는 여전히 한 프레임이 렌더링될 때까지 몇 분에서 몇 시간씩 기다려야 합니다.  오늘은 저희의 방법과 약간의 광기가 어떻게 '누비스를 큐브화'하고 이 장벽을 넘을 수 있었는지에 대한 이야기를 들려드리려고 합니다. 이 이야기는 10여 년 전 다른 업계에서 예상치 못한 곳에서 시작되었습니다. 

 
 

2011년에 저는 시그라프에서 블루 스카이 스튜디오의 애니메이션 영화 '리오'에서 유체 시뮬레이션을 사용하여 구름을 모델링하고 CGI 스튜디오라는 독점적인 레이 트레이서로 렌더링한 방법에 대해 강연한 적이 있습니다. 스모그복스라는 볼류메트릭 렌더링 기술은 제 동료인 트레버 톰슨이 개발했습니다. 저는 시뮬레이션된 '복셀 구름' 라이브러리를 만든 다음 동료 매트 윌슨(Matt Wilson)과 함께 '프랑켄클라우드스케이프'라고 부르는 것으로 조각을 조립했습니다. 리오가 입체 영화였기 때문에 기존의 빌보드 기반 구름으로는 충분하지 않았기 때문에 이 모든 과정이 필요했습니다.  결과는 만족스러웠지만 렌더링 시간은 단일 1920x1080 프레임에 대해 하나의 코어에서 10분에서 4시간까지 걸렸습니다. 장편 애니메이션에서는 시간과 렌더 팜만 있으면 해결할 수 있는 문제였기 때문에 문제가 되지 않았습니다. 

각주
스모그복스: 다중 산란은 볼륨을 통과하는 광선의 여러 굴절을 계산해야 하므로 리소스 집약적이지만, 구름 특유의 따뜻한 내부 빛을 내기 위해서는 필수적입니다. 따라서 우리는 자체 레이트레이서인 CGI 스튜디오의 볼륨 렌더러인 스모그복스에서 제로 비용으로 다중 산란을 근사화할 수 있는 방법을 개발했습니다. 이를 통해 후디니 씬에서 원근 카메라와 조명을 익스포트하고 SmogVox를 사용하여 다중 산란 근사치로 클라우드를 렌더링함으로써 테스트를 빠르게 렌더링할 수 있었습니다.

 

저는 2014년 게릴라에서 호라이즌 프로젝트에 합류하여 이전 킬존 게임에서 사용했던 2D 스카이박스 기술을 개선하는 작업을 맡았습니다. 
 
 

오프라인 렌더링에 익숙하지 않았던 저는 모든 문제를 해결할 수 있는 솔루션으로 복셀 기반 클라우드 스케이프에 즉시 끌렸습니다. 
 

저는 마이크로 솔버라는 것을 사용하여 Houdini에서 Aero라는 이름의 커스텀 유체 솔버를 만들었습니다.  
마이크로 솔버는 유체 시뮬레이션의 한 프레임에 대해 풀기 위한 단계를 모듈화하여 사용자가 순서를 바꾸고, 수정하고, 새로운 솔브를 도입하는 등 사용자가 생각할 수 있는 거의 모든 작업을 수행할 수 있도록 하는 Houdini의 방식입니다.
 
 
 

널리 사용되는 연소 스타일 솔버와 달리 Aero는 여기에서 볼 수 있듯이 클라우드 성장만 시뮬레이션하도록 간소화 및 개선되었습니다.
 
 

2014년에 여러 가지 구름 모양을 시뮬레이션하고 다양한 렌더링 방법을 시도했지만, 그 어떤 것도 PlayStation 4에 적합하거나 시리즈 첫 번째 게임의 요구 사항을 충족하지 못했습니다: 호라이즌 제로 던의 요구 사항을 충족하지 못했습니다. 
아직 시간이 없었기 때문에 작업을 보류하고 개발을 계속 진행했습니다.
 
 

3D 클라우드가 3D 복셀 데이터로 저장되지 않고 2D 텍스처 데이터로 저장되기 때문에 2.5D 클라우드라고 할 수 있는 여러 가지 모델링 및 렌더링 방법을 사용했습니다. 우리는 이 클라우드 시스템을 누비스라고 불렀습니다.  이 시스템이 어떻게 작동하는지 살펴보겠습니다.
 
 

오늘 다룰 새로운 자료가 많기 때문에 이 간략한 연혁이 너무 짧게 느껴진다면 이 자료들을 다시 한 번 살펴보고 자세한 정보를 찾아보시기 바랍니다. 가능한 경우 왼쪽 하단에 관련 강연을 나열했습니다.
 
 
 

누비스가 게임에서 성공할 수 있었던 이유는 볼류메트릭 레이 마칭이라는 렌더링 기법을 미세 조정하면서 클라우드 스케이프를 압축하고 렌더링 시점에 클라우드 특유의 밀도와 라이팅 디테일을 추가하는 방식 덕분이었습니다. 이를 통해 구름의 진화와 시간 주기를 모델링할 수 있었으며, 멀리 있는 구름을 생성하는 것 외에도 붉은 조명이 있는 슈퍼폭풍과 플레이어가 비행할 수 있는 낮은 고도의 안개와 같은 구름을 생성할 수 있었습니다. 우리의 레이 마칭 방식은 압축된 클라우드 모델을 멀리서 보거나 구름을 뚫고 지나가는 경우에 따라 0.2초에서 5밀리초 사이에 렌더링된 구름 프레임으로 전환했습니다. 
이러한 모델링 및 렌더링 방법이 어떻게 작동했는지 간단히 살펴보겠습니다.
 
 

레이 마치 안에서 픽셀에 구름이 포함되어 있다는 것을 알고 나면 다음과 같이 하면 됩니다.
구름 레이어의 상단과 하단을 교차하는 광선 부분을 따라 스텝을 밟습니다. 
그리고 구름을 포함하는 각 단계에서 밀도를 샘플링하고 조명을 샘플링하여 빛을 향해 2차적으로 상당히 비싼 레이마칭을 한 다음 이러한 데이터를 이전 단계의 결과와 통합하여 각 픽셀에 대한 색상 및 불투명도 데이터를 생성하는 방식으로 통합합니다. 
이 과정은 전체 불투명도에 도달할 때까지 반복되며, 전체 렌더링을 생성하기 위해 모든 픽셀에 대해서도 이 과정이 수행됩니다.  

 
 

이 프로세스를 살펴보고 각 단계에서 사용한 방법을 살펴보겠습니다, 
광선의 핵심인 밀도 샘플러부터 시작하겠습니다.

 
 

오늘은 클라우드 렌더링 방법 중 두 가지를 중점적으로 살펴보려고 하는데, 두 가지 방법 모두 복셀 기반 클라우드에 대한 접근 방식에 직접적인 영향을 미쳤기 때문입니다:
첫 번째는 플레이어가 도달할 수 없는 높은 고도의 대류권 구름에 사용된 수직 프로파일 메서드입니다.
두 번째는 플레이어가 비행할 수 있는 오로그래픽 구름에 사용된 엔벨로프 메서드입니다. 
각각의 밀도 샘플링 방식은 다르지만 설계상 몇 가지 유사점을 공유합니다. 



Nubis 데이터 필드 또는 NDF라고 하는 2D 데이터 필드에는 클라우드 렌더링 컴퓨팅 셰이더가
가 생성된 2D 데이터에서 3D 클라우드를 "압축 해제"하는 데 사용하는 명령어를 포함합니다.


 

이 2D 데이터 필드는 월드의 16km 영역에 매핑되어 렌더링 시점에 샘플링되어 3D 클라우드 풍경을 구성하는 데 필요한 데이터를 얻었습니다. 
 
 

레이마칭 동안 밀도를 샘플링할 때 두 방법 모두 여기에서 볼 수 있듯이 저해상도 치수 프로파일에서 
저해상도 치수 프로파일을 생성했습니다.
비슷해 보이지만 치수 프로파일을 구성하는 방식은 방법마다 다릅니다.
 
 

수직 프로파일 방법의 경우, 5개의 2D NDF로 시작했습니다.
클라우드 최소 및 최대 높이로 클라우드의 수직 범위를 정의했습니다.
상단 유형과 하단 유형을 사용하여 수직 프로파일 룩업 텍스처를 샘플링하고 그 결과를 
를 곱하여 수직 프로파일이라고 부르는 것을 만들었습니다.  
그런 다음 클라우드 커버리지 데이터에 따라 이 수직 프로파일의 스케일을 조정하여 클라우드가 형성되는 위치를 제어했습니다. 



엔벨로프 프로파일 방법의 경우, 4개의 2D NDF로 시작했습니다.
최소 및 최대 높이를 사용하여 구름을 렌더링할 수 있는 엔벨로프를 정의했습니다.  
치수 프로파일은 3개의 그라데이션의 곱이었습니다: 
아래쪽을 향해 값이 증가하는 상단 그라데이션
위로 갈수록 값이 증가하는 하단 그라데이션
그리고 측면에서 안으로 갈수록 값이 증가하는 가장자리 그라데이션. 

각주
치수(measure, dimension size, 寸數): 물체 또는 건축물에서 2점간의 거리를 어느 단위에 따라서 표현한 값이다. ... 각주 달고 보니 10년간 한국어를 많이 사용하지 않아서... 뭔가 했습니다....  그 치수 맞습니다.

 

엔벨로프 프로파일 방법의 경우, 4개의 2D NDF로 시작했습니다.
최소 및 최대 높이를 사용하여 구름을 렌더링할 수 있는 엔벨로프를 정의했습니다.   
치수 프로파일은 3개의 그라데이션의 곱이었습니다.  아래쪽을 향해 값이 증가하는 상단 그라데이션 위로 갈수록 값이 증가하는 하단 그라데이션 그리고 측면에서 안으로 갈수록 값이 증가하는 가장자리 그라데이션. 



3D 노이즈를 사용하여 치수 프로파일을 침식하고 여기에 표시된 세부 결과물로 "업 레즈"했습니다.


 

우리는 구름의 물결 모양의 디테일과 기복이 심한 디테일의 혼합을 시뮬레이션하기 위해 펄린-월리 노이즈라고 부르는 노이즈를 개발했고, 구름에서 보이는 기복이 심한 디테일을 시뮬레이션하기 위해 월리 노이즈를 사용했습니다. 
이러한 노이즈를 모델별로 다양한 방식으로 결합하여 샘플 위치에서 잠재적인 위스피 및 빌로우 디테일을 위한 노이즈 합성물을 만들었습니다.



그런 다음 샘플 위치의 유형 데이터를 사용하여 위스피에서 빌로우 노이즈 컴포지트까지 블렌딩했습니다. 



침식은 노이즈 합성물에서 치수 프로파일의 치수 프로파일의 역수를 빼는 것으로 모델링했습니다. 
 
 
 

"두 모델 모두 바람 방향의 노이즈에 애니메이션을 적용하여 'Pseudomotion'을 시뮬레이션했습니다. 



밀도를 샘플링한 후 빛 에너지를 샘플링합니다.
 
 
 

두 방법 모두 빛 에너지를 세 가지 구성 요소의 합으로 정의합니다:
직접 산란 - 태양으로부터 오는 모든 빛 에너지를 나타냅니다. 
하늘과 주변 구름에서 나오는 빛 에너지를 나타내는 주변 산란.
그리고 2차 산란은 번개와 같은 광원에서 나오는 빛 에너지를 나타냅니다. 
각 방법마다 이러한 에너지를 계산하는 방식에는 약간의 차이가 있습니다. 자세한 설명은 이 강좌의 가장 최근 강연인 2022년 강연을 참조하시기 바랍니다. 간결함을 위해 요약해 보겠습니다. 
 
 
 

클라우드의 특정 샘플 지점에서의 직접 산란을 세 가지 확률의 함수로 정의했습니다:
투과율, 산란 위상, 다중 산란입니다. 
투과율은 광학 매체에서 주어진 깊이에서 빛의 양을 측정한 값입니다. 그래프는 클라우드의 깊이에 따라 더 많은 빛이 흡수됨에 따라 빛의 강도가 어떻게 감소하는지를 나타냅니다. 이 깊이를 수집하려면 빛을 향해 두 번째 (그리고 매우 비싼) 광선 행진을 수행해야 합니다.  
산란 단계는 뷰 벡터와 빛 벡터가 주어졌을 때 샘플 위치에 도달한 에너지가 우리 눈을 향해 얼마나 산란하는지를 측정한 값입니다. 빛은 작은 물방울이나 얼음을 통해 굴절되면서 구름 속에서 산란합니다. 구름에서 빛은 원래 경로를 따라 더 많이 산란하는 경향이 있으므로 여러 헨니 그린스타인 위상 함수를 사용하여 아트 디렉팅이 가능하고 어느 정도 물리적인 기반이 있는 효과를 생성합니다. 
다중 산란은 빛이 물 분자와 여러 번 만나 굴절된 후 뷰 벡터로 흩어지는 것을 설명합니다. 구름이 깊어질수록 광자가 원래 경로에서 산란될 가능성이 높아지므로, 우리는 차원 프로파일을 확률 필드의 기초로 사용하여 내부 산란이 더 자주 발생하는 위치를 설명합니다. 여러 번 산란된 빛은 더 많이 산란될수록 결국 흡수되기 때문에 다른 비어-램버트 감쇠 곡선을 사용하여 이 필드를 감쇠시킵니다. 또한 아래에서 구름으로 산란되는 빛의 양이 적다는 사실을 고려하여 높이에 따라 감쇠시킵니다. 
마지막으로 위상 함수로 스케일을 조정하여 방향성을 갖도록 합니다.

 
 

다중 산란 근사법을 사용하지 않은 클라우드의 모습은 다음과 같습니다.
그리고 이 근사법을 사용하면 다음과 같습니다.
 
 

광선이 많은 광원으로 이동하지 않고 사방에서 구름으로 들어오는 주변광의 효과를 근사화하기 위해 이를 다시 한 번 확률 필드로 기하학적으로 모델링했습니다. 
대부분의 빛은 위와 주변에서 들어와 구름 표면만 투과합니다. 
치수 프로파일은 이미 클라우드 외부에서 내부로의 그라데이션을 제공했습니다.  차원 프로파일의 역을 사용하여 빛이 구름 내부의 특정 지점에 도달한 후 우리 눈에 산란될 확률을 나타내는 그라데이션을 만들었습니다.  
 
 

이 주변 구성 요소만 있는 구름의 모습은 다음과 같습니다.
그리고 직접광 에너지만 있는 경우.
그리고 직접광과 주변광 에너지가 모두 있는 경우입니다. 
 
 

이제 2차 광원 에너지입니다. 일반적으로 볼류메트릭 효과 내부에 배치된 광원의 경우 
샘플을 채취할 때마다 값비싼 2차 광선 행진을 수행해야 합니다. 추적하는 사람들에게는 3번의 광선 행진을 의미하므로 이상적인 접근 방식은 아닙니다.

 
 

다시 한 번 조명을 기하학적 확률 문제로 살펴보았습니다. 
1차 광선 행진에서 광원 주변에서 채취한 각 샘플에 존재하는 빛 에너지의 근사치에 해당하는 빛의 양을 모델링했습니다. 여기에는 조금 더 자세한 내용이 있지만 나중에 복셀 클라우드에 대해 이 작업을 수행한 방법을 살펴보면서 설명하겠습니다. 

 
 

호라이즌 포비든 웨스트의 슈퍼스톰은 이렇게 생겼습니다.
 
 

다음 단계(말장난 의도)는 다음 샘플을 광선을 따라 배치할 수 있도록 행진의 스텝 크기를 결정하는 것입니다. 

 
 

수직 프로파일 방법은 카메라로부터의 거리에 따라 스텝 크기가 증가하는 적응형 스텝 크기를 사용했습니다. 그 결과 정밀도가 덜 필요한 카메라에서 멀리 떨어진 샘플의 수가 줄어들었습니다.  
 
 

하늘이 부분적으로 보이는 경우의 시간은 0.5밀리초에서 2.2밀리초까지 다양했습니다, 
하늘이 완전히 보이는 경우 2.2밀리초까지 다양합니다.  
 
 

하지만 엔벨로프 방식으로 생성된 구름을 통과하는 비행을 지원하려면 대부분 카메라 근처에 많은 샘플이 필요했기 때문에 빈 공간에 많은 샘플을 배치하지 않기 위해 약간의 창의력을 발휘해야 했습니다. 
우리는 
콘 스텝 매핑과 거리 스텝 매핑이라는 두 가지 방법을 결합하여 높이 데이터만 사용하면서 최대한 효율적으로 샘플을 배치할 수 있었습니다.


레이마칭을 할 때 표면 교차점에서 생성된 레이와 원뿔 사이의 교차 테스트를 수행하여 구름에 부딪히기 전에 취할 수 있는 가장 큰 단계를 결정했습니다. 거리 스텝 매핑은 이러한 원뿔 외부와 구름 외부 영역에 대해 너무 많은 샘플을 채취하지 않도록 하기 위해 사용되었습니다. 
엔벨로프 내부에서 샘플링을 시작하면서 더 작은 광선 행진 단계를 수행하여 실제 구름 밀도를 샘플링하기 시작합니다. 

 
 

이 이미지에서 빨간색 점으로 표시된 것처럼 샘플이 구름 주위에 집중되어 있고 빈 공간에는 빈번하지 않아 분포가 더 좋은 것을 볼 수 있습니다.
 
 

그렇다면 얼마나 절약할 수 있을까요? 이전 비용은 다음과 같습니다... 4.2ms
 
 

그리고 더 안정적인 1,3ms
 
 

이러한 시스템을 최대한 고성능으로 만드는 또 다른 방법은 모든 픽셀에 레이마치를 수행하는 것 이상의 작업을 수행하는 것이었습니다. 클라우드에 대한 두 가지 접근 방식 모두 약간의 렌더링 가속을 사용했습니다.
 
 

수직 프로필 구름은 16프레임에 걸쳐 렌더링 비용을 상각하는 템포럴 업스케일링을 사용했습니다.  
이는 20ms 렌더링을 2ms 렌더링으로 만드는 효과가 있으며 멀리 있는 구름에는 효과가 있었지만 카메라가 빠르게 움직일 때 이미지가 제시간에 해결되지 않기 때문에 가까운 구름에는 효과가 없었습니다. 


 

따라서 몰입형 엔벨로프 클라우드의 경우 대신 렌더링을 두 패스로 분할합니다:  렌더링 비용이 더 많이 드는 카메라 근처에서는 해상도를 낮추고, 에일리어싱을 방지하려는 멀리 떨어진 곳에서는 해상도를 높입니다.  
 
 

지금까지 클라우드 모델링 및 렌더링에 대한 이전 방법을 살펴봤습니다.  어떻게 비교되는지 살펴보겠습니다:

원뿔과 거리 필드를 즉석에서 생성할 수 있는 예산이 없었기 때문에 엔벨로프 메서드에서는 에볼루션을 지원하지 않습니다.
두 가지 방법 모두 시간에 따라 작동합니다.
라이트닝은 수직 프로파일 메서드에서만 구현되었습니다.
둘 다 높은 프레임 속도를 지원합니다.
엔벨로프 클라우드만 비행이 가능합니다.
안타깝게도 이 두 가지 방법 중 어느 것도 시각적으로 직관적으로 작성하고 작업할 수 없습니다. 솔직히 말씀드리자면요.  이러한 방법으로 작업해 본 분들은 무슨 말인지 아시겠죠. 

이 두 가지 솔루션은 제가 게릴라에서 일하기 전에 스튜디오 아트 디렉터 JB가 제게 준 목표, 즉 '제대로 된 구름을 통과하는 비행'을 달성하는 데 걸림돌이 되었습니다. 

그래서 포비든 웨스트가 끝난 후 좌절감을 느끼고 우리가 할 수 있는 것을 증명하고 싶어서 프로토타입을 개발하기 시작했습니다. 이 프로토타입 실시간 복셀 클라우드 렌더러를 살짝 보여주면서 작년 이 강좌의 강연을 마무리했습니다.

 
 

구름의 위치를 알 수 없는 레이마치 샘플을 배치하는 대신 바운딩 박스와 내부 부호 거리 필드를 결합하여 소스에 구애받지 않는 단계 배치 방법을 지원했습니다.
 
 

샘플 배치와 밀도 및 라이트 샘플링 모두에서 볼륨이 겹칩니다. 3개의 복셀 클라우드가 겹쳐진 렌더링 결과를 확인할 수 있습니다.
 
 

여기에서 이 하이브리드 샘플 배치 방법을 사용하여 흰색으로 채취한 샘플을 볼 수 있습니다. 
혼자서 더 이상 더 빠르게 만들 수 없을 때까지 이 작업을 반복하고 또 반복했습니다.
 
 

좋은 소식은 구름 몇 개를 합쳐서 그 속으로 날아갈 수 있고, 그 결과 Playstation 5에서 960x540 해상도에서 4ms가 소요된다는 것이었습니다. 
안 좋은 소식은 스튜디오 아트 디렉터인 얀 바트 반 비크에게 이걸 보여주는 실수를 저질렀다는 것이었지만, 궁극적으로 다행스러운 일이었습니다. 얀-바트는 제가 게릴라에 합류하기 전부터 이런 종류의 작업을 목표로 삼고 있었다는 사실을 기억하세요.
얼마 지나지 않아 곧 출시될 DLC 트레일러에 관한 회의에 불려갔습니다. 
예고편에 구름이 등장할 거라는 얘기를 들었죠.  


 

다음은 우리에게 보여진 애니메이션 영상입니다. 보시다시피 흰색 타원체로 표시된 것처럼 거의 모든 것이 구름이며, 단순한 구름이 아니라 전경과 배경 사이에 펼쳐진 세밀한 구름을 통해 고속으로 비행하는 모습을 눈으로 볼 수 있습니다. 

 

회의가 끝난 후 프로토타입이 어떤 조건에서 어떻게 작동하는지 확인하기 위해 테스트를 실행했는데 약간 우려스러웠습니다. 성능은 형편없었고 우리가 필요로 하는 디테일이 부족했습니다. 
다행히도 불타는 해안 DLC가 PS5 전용으로 출시된다는 사실을 알게 되어 성능에 대한 많은 제약에서 벗어날 수 있었습니다. 

 

그런 다음 수석 기술 프로그래머인 Jeroen Krebbers와 수석 기술 프로그래머인 Nathan Vos와 함께 프로토타입을 트레일러용 솔루션으로 출시할 수 있는 방법에 대해 논의했습니다. 긴장감 넘치는 토론이었다고만 말할 수 있습니다.  그럴 만한 이유가 있었죠. 

 
 

회의에서 나온 결론 중 하나는 트레일러에서 이걸 해냈다면 게임에서도 해내야 한다는 것이었습니다. 
만약 성공하지 못하면 프로젝트 전체를 폐기해야 한다는 데도 동의했습니다. 테크 코드의 콜라주 팀원, 네이선 보스, 제임스 맥라렌, 휴 말란의 도움으로 트레일러에서 보여준 모든 것을 품질 저하 없이 실제 DLC에 최적화할 수 있었습니다. 그 결과물은 다음과 같습니다:

 
 
 

트레일러

 

그 경험에서 몇 가지 교훈을 얻었습니다: 이 장면에서 이 정도로 다양한 구름 풍경을 연출할 수 있었을 때, 어떤 식으로든 작동할 수 있는 시스템이 있다는 것을 깨달았습니다.  
 
 

1킬로미터 상공에서 촬영해야 하는 이 장면을 검토하고 있을 때 누군가 구름이 있는 200미터 산 정상을 숨겨야 하냐고 물었죠.  아트 디렉터는 "아니요, 그냥 두세요."라고 말했고, 그때 깨달은 것은 우리의 원래 의도가 중요한 것이 아니라 무언가를 표현하는 것이 중요하다는 것이었습니다. 아트 디렉션이 구름을 깊이감을 주기 위한 전경 도구 중 하나로 사용하는 데 익숙해졌다는 것을 깨달았죠.

또한 트레일러에 대한 반응 영상을 보면 구름에 대한 언급은 있었지만 게임플레이에 어떤 의미가 있는지에 대한 이야기는 전혀 없었는데, 이는 이 영상을 본 많은 사람이 그저 시네마틱 트레일러일 거라고 예상했다는 뜻이기도 합니다. 

흥미진진하면서도 약간 무섭기도 했죠.
 
 
 
 

버닝 쇼어 DLC의 배경은 폐허가 된 로스앤젤레스의 화산 군도입니다. 
할리우드에 걸맞게 이 프로젝트의 목표는 현실보다 더 큰 경험을 제공하는 것이었습니다. 
그래서 자연스럽게 새로운 기술을 개발하면서 하늘을 탐험할 수 있도록 개방하고, 원래 계획했던 대로 기본 게임과 DLC 사이의 과도기적 시네마틱으로 경험을 제한하지 않고 싶었습니다. 
즉, 구름은 디테일한 볼류메트릭 스카이박스로서 전통적인 역할을 해야 할 뿐만 아니라 플레이어가 다음과 같은 기능을 수행할 수 있어야 했습니다. 
지상에서 매우 디테일한 물보라 구름 속으로 날아간 다음 물속으로 잠수할 수 있어야 했습니다.  
나중에 알게 된 사실이지만, 이보다 더 어려운 과제가 있었습니다. 나중에 자세히 설명하겠습니다.  
 
 

단 몇 달 만에 빠르게 개발해야 했죠, 
하지만 우리는 해냈습니다. 그래서 더 이상 지체하지 않고
몰입형 리얼타임 복셀 클라우드를 위한 우리의 솔루션을 소개합니다. 
 
 

우리의 레이마칭 절차를 기억합니다. 우리는 구름이 어디에 있는지 잘 알지 못한 채 마칭을 수행하죠.
클라우드에 도달하면 밀도를 샘플링합니다. 직접 광선 에너지 – 성가시게 비싼 2차 레이마칭이 필요합니다, 
그러면 주변의 빛 에너지는, 그리고 마지막으로 번개와 같은 2차적인 에너지원에서 나오는 빛 에너지. 그런 다음 완전한 불투명에 도달하여 마칭을 중단할 때까지 다른 정보가 없는 모든 단계에서 이를 반복합니다. 
이러한 작업들은 대단히 효율적이거나 복셀과 효율적으로 작동하도록 설계된 것은 없지만, 우리의 복셀 클라우드 렌더링 접근 방식은 거의 모든 작업을 더 빠른 복셀 기반 방법론으로 대체하고 최적화합니다.
이 작업을 처음부터 시작하겠습니다.
 
 

효율적인 렌더링 및 조명 솔루션을 개발하는 것은 AAA 실시간 그래픽의 경우 사용자가 여기서 결정하는 대부분의 작업과 필요한 메모리의 양이 성능에 도움이 되거나 손상을 입을 수 있기 때문에 견고한 모델링 기반에 달려 있습니다.
 
 

2.5D 클라우드에 대한 밀도 샘플러는 2D 모델링 데이터로부터 3D 클라우드 볼륨을 먼저 생성한 후, 이를 업-리징(up-rezeing)하는 방법으로, 이 모든 값비싼 작업은 쉐이더 밖으로 옮겨질 수 있지만, 이것은 당신이 필요하다는 것을 의미합니다.  복셀 구름을 모델링하는 방법을 개발합니다, 그것을 저장해 둔 다음 샘플러에서 효율적으로 접근할 수 있습니다.
 
 
 

손으로 복셀 클라우드를 모델링하는 데는 지루하고 비현실적인 많은 방법이 있지만, 제가 발견한 가장 유망한 접근법은 다음과 같습니다 
과거에는 클라우드를 "성장"하기 위해 유체 시뮬레이션을 사용했습니다.
 
 
 

2014년의 유체 시뮬레이션 실험을 공개하지 않는 것처럼 보였습니다. 데이터 저장기를 사용하기 때문에 몇 가지 "프랑켄클라우드링"을 하는 것이 버닝 쇼어스의 복셀 클라우드스케이프 모델링을 시작하기에 가장 좋은 방법이 될 것 같습니다. 하지만 클라우드스케이프를 구축하기 위해서는 이 데이터를 활용할 방법이 필요했습니다.
 
 

우리는 후디니에서 아틀라스(Atlas)라고 부르는 저작 도구 세트를 개발했습니다. 아틀라스는 복셀 데이터를 위한 합성 파이프라인으로, 데이터를 생성하거나 소싱하고 조작하는 다양한 방법을 가지고 있습니다.
 
 

예를 들어, Bryan Adams라는 이름의 대기학 아티스트 중 한 명이 게임 모델에서 큰 목 구름을 만들었습니다.
 
 

우리가 일찍 배운 것 중 하나는 여러 개의 복셀 격자 상자가 비유적으로 그리고 문자 그대로 우리를 경계시켰다는 것입니다. 그것들을 기억에 유지하고 광선 행진의 내부 루프에서 그것들 사이를 전환하는 것은 이상적이지 않았고 느린 성능으로 이어졌습니다.
 
 

다음 아이디어는 전체 클라우드스케이프를 구축한 후 전 세계의 모든 세제곱미터 공간에 대한 클라우드 밀도 정보를 포함하는 촘촘한 복셀 그리드를 작성하는 것이었습니다.
 
 

이를 통해 복셀 클라우드 모델링의 첫 번째 과제를 해결하고 2.5D 클라우드 밀도 샘플러의 값비싼 부분을 완전히 대체할 수 있습니다. 
 
 
 

우리가 스카이 스케일의 복셀 그리드에 직면할 것이라는 것을 알고 있었던 첫 번째 문제. 메모리 병목 현상.
 
 

제가 설명을 해드릴게요. DLC 지역은 가로 4km, 세로 4km 정도였고 저희는 지상과 공중에서 작동하는 구름을 배치하기 위해 약 500미터의 수직 공간이 필요했습니다. 
또한 플레이어가 구름을 통해 날아갈 때와 같은 미세한 세부 사항을 포착할 수 있는 충분한 해상도가 복셀 그리드에 필요했습니다.
 
 

예상치를 한 단계 낮추고 계산을 한다면 복셀 그리드가 필요할 것입니다.
2048x2048x256은 2미터의 정밀도를 얻기 위해서입니다. Flying mounts는 약 2미터의 날개폭을 가지고 있기 때문에 우리의 예상으로는 이것으로 충분하지 않았습니다.
 
 

이러한 밀도의 그리드는 많은 메모리를 필요로 하며, 따라서 대역폭 요구량이 기하급수적으로 증가하기 때문에 광선 행진의 샘플에 접근하는 데 더 오랜 시간이 걸립니다. 초기의 실험은 이것이 단순히 우리에게 효과가 없다는 것을 보여주었습니다. 
격자 안에 무언가가 있는 영역에서만 더 높은 정밀도의 데이터를 저장하는 다양한 희소 복셀 형식이 있지만, 이를 위해서는 간접적인 단계가 필요하며, 이는 각 샘플을 채취하는 비용을 증가시킵니다. 광선 행진의 경우 이러한 집중적인 성능이 매우 중요합니다. 이미 알려진 문제가 엄청나게 많기 때문에, 우리는 이 알려지지 않은 것을 더미에 추가하지 않기로 결정했습니다. 하지만 이 영역은 미래에 탐색하기에 좋은 영역이 될 수 있습니다. 
BC4 압축, 8미터 정밀도로 시작하기로 했습니다.
 
 
 

복셀 클라우드 모델링에 대한 접근 방식에 대해 알고 있는 내용을 요약해 보겠습니다. 
Houdini의 Aero라는 맞춤형 클라우드 솔루션을 사용하여 클라우드를 시뮬레이션합니다
그런 다음 편집하고 혼합하여 프랭크 클라우드스케이프와 
그런 다음 저해상도 복셀 그리드에 저장하여 렌더링 시간에 샘플링합니다. 우리의 목표에 대한 해결책은 아닌 것 같지만…
 
 

과거 경험을 통해 컴퓨팅 쉐이더에서 메모리 액세스와 명령 간의 작업 균형을 조정하면 GPU에서 더 나은 성능을 얻을 수 있다는 것을 알 수 있었기 때문에 과거에 했던 것처럼 밀도 샘플러 자체에서 이 문제를 해결하기로 결정했습니다.
 
 

이전 접근 방식을 기억합니다 
그들의 2D NDF 모델링 데이터를 차원 프로파일로 압축 해제했습니다. 또한 기억하십시오 
그런 다음 더 높은 주파수 잡음으로 차원 프로파일을 침식시켜 상향 조정했습니다. 질문은 분명합니다. 복셀 데이터를 체적 광선 행진에서 샘플링할 때 상향 조정 접근 방식이 작동할 수 있을까요? 대답은 '예'입니다.
 
 

고해상도로 모든 복셀에 클라우드 밀도를 명시적으로 정의하기 보다는, 
우리의 접근 방식은 새로운 세부 노이즈를 사용하여 저해상도 복셀 기반 차원 프로파일을 상향 조정하는 것입니다. 이를 통해 2.5D 모델 클라우드에서와 같이 상향 조정 명령으로 작업을 전환하여 메모리 병목 현상을 방지할 수 있습니다.
 
 

제 말의 의미를 미리 설명해 드리자면, 다음은 우리가 그것을 렌더링했을 때의 차원 프로파일의 모습입니다
그리고 이것이 업레스의 결과입니다.
 
 
 

차원 프로파일은 클라우드의 부호화된 거리 필드(SDF)에서 생성되어 외부에서 내부로 기울기를 얻을 수 있습니다.
 
 
 

Atlas 도구를 사용하면 Nubis Volume Data Fields, 즉 NVDF를 모델링할 수 있습니다. 차원 프로파일 외에 NVDF를 추가로 만들 수 있습니다: 
Detail Type(디테일 유형) - 위스피(wispy)에서 위스피(billowy) 노이즈 세부 정보를 혼합하는 데 사용됩니다
그리고 잡음이 업-레즈에서 인가된 후 밀도를 감소시키기 위해 사용되는 밀도 스케일(Density Scale). 

이들은 BC6 Compression을 사용하여 압축되었으며, 텍셀당 1바이트의 채널을 3개 허용했습니다.

복셀 클라우드 샘플러 기능이 어떻게 우리의 up-rez 방식으로 구현되었는지 살펴보고, 새로운 3D Noise의 맛에 대해 알아보겠습니다.
 
 

밀도 샘플러에서 우리가 하는 첫번째 일입니다
치수 프로파일 NVDF를 샘플링합니다. 우리가 먼저 하는 이유는 결과가 0이면, 
그러면 우리는 클라우드를 샘플링하지 않고 이 샘플에 대한 더 이상의 작업을 방지할 수 있습니다.  
차원 프로파일이 0이 아닌 경우 모델링 데이터를 up-rez 함수로 전달합니다.  

up-rez 기능을 검토하기 전에 사용하는 새로운 3D 클라우드 세부 소음을 살펴봐야 합니다.
 
 

주의:

NVDF 모델링 데이터를 저장할 구조를 정의하는 것은 특별한 기능을 가지고 있기 때문입니다. 채워지는 구조를 함수로 전달할 수 있으며 접근하지 못한 구성원은 VGPR 사용에 기여하지 않습니다. (주의할 경우)

우리는 밀도 샘플 구조에 두 개의 밀도 샘플을 저장합니다. 전체 샘플은 정밀도가 필요한 밀도 및 조명 계산을 위한 것이고 프로파일 샘플은 광산란 볼륨을 구성하려고 시도하는 조명 계산을 위한 것입니다. 이에 대한 자세한 내용은 나중에 확인하십시오.
 
 
 

먼저 이 타임랩스를 한번 보겠습니다. 삼각대를 사용했어야 하는데, 요점이 전달이 됩니다. 여러분들은 이런 영상을 본 적이 있지만, 저는 Fluids를 정말 이해하기 위해서는 다른 관점에서 보는 것이 가장 좋다고 생각합니다.
 
 
 
 

특히 거꾸로. 직장에서 제 책상 옆을 지나가면 이상한 각도로 모니터를 보고 있는 제 모습을 볼 수 있을 것입니다. 이 관점에서 보면 실제 구름이라기보다는 파란 배경의 물탱크에 우유를 붓는 것처럼 보입니다. 이 관점에서 보면 수증기가 원하는 곳에 도달하기 위해 싸우고 있는 압력을 쉽게 상상할 수 있습니다. 이 투쟁은 구름의 세부사항을 모델링하는 방법을 이해하는 데 핵심입니다.
 
 
 

클라우드 세부 정보는 빌로우(billow)와 위스(wisp)로 분류합니다. 
증기가 한 지역으로 이동하고 더 차가운 공기와 마주칠 때, 증기는 효과적으로 원래 있던 곳으로 밀려납니다. 
우리가 볼 수 있는 이 물결은 팽창하는 증기가 이 압착력의 약한 부분을 뚫고 지나가는 결과입니다.
 
 

자, 이것을 여러분의 마음속으로 뒤집어 보면 여러분은 좀 더 위스피한 형태의 가리비 모양을 갖게 될 것입니다. 
이것은 주변의 공기 자체가 수증기로 뿜어져 나오면서 발생하는 현상입니다. 수증기가 증발합니다.
 
 
 

여기 또 다른 타임랩스가 있습니다. 거의 거미줄처럼 보입니다. 
이러한 상황에서 공기 중의 난류는 모양에 추가적인 왜곡을 더하는 데 더 큰 역할을 합니다.
 
 

정리하자면, 
겹겹이 쌓인 위스프와 곱슬곱슬한 왜곡은 밀도가 감소하는 곳에서 형성되고 겹겹이 부풀어 오르는 현상은 밀도가 증가하는 곳에서 발생합니다.
 
 

예전에, 우리는 거꾸로 된 Wolley 노이즈를 사용하여 볼록한 세부사항을 만들었지만, 꽉 찬 구체가 아닌 구름처럼 보이는 무언가를 얻기 위해 꽤 많은 층을 쌓고 샘플러에서 작업해야 했습니다.  
이번에 우리는 후디니의 악어 소음을 사용하기로 결정했는데, 이 소음은 우리의 겹겹이 쌓인 월리 소음과 매우 비슷하지만 이러한 구조에 더 적절한 구름과 같은 열공성을 가지고 있습니다. 
위스피한 디테일을 위해 우리는 이전에 펄린-월리 노이즈라고 불리는 것을 사용했습니다. 그러나 이것은 이 업-레즈 절차에서 사용될 만큼 순수하게 위스피하지 않았습니다.  
대신에, 우리는 모양처럼 멋진 거미줄을 만드는 반전 악어 소음으로 시작해서, 컬 소음을 이용해서 왜곡했습니다.  
우리는 이것을 컬리 앨리게이터 소음이라고 부릅니다.
 
 
 

우리가 생성하는 3D 텍스처는 128x128x128 복셀의 4채널을 가지고 있습니다. 
처음 두 채널은 저주파 및 고주파 Curly-Alligator 노이즈입니다 
마지막 두 가지는 Low and High Frequency 악어의 소음입니다. 이 3D 소음을 발생시키는 데 도움이 되는 것에 대해서는 토크가 끝날 때까지 채널을 맞추세요.  
SideFX Software에서 친절하게 제공하는 앨리게이터 노이즈 소스 코드에 직접 링크를 공유하게 되어 기쁩니다.  

이제 차원 프로파일을 상향 조정하기 위해 사용하는 새로운 3D 노이즈를 이해했으므로 상향 조정 기능 자체의 구현을 살펴볼 수 있습니다.
 
 
 

// Apply wind offset 
inSamplePosition -= float3(cCloudWindOffset.x, cCloudWindOffset.y, 0.0) * voxel_cloud_animation_speed;

첫 번째 단계는 2.5D 구름에서처럼 세부 소음이 스크롤되도록 하는 것입니다. 이 작업은 소음 샘플 위치에 바람 오프셋을 추가하여 수행합니다.
 
 
 
 

// Sample noise
float mipmap_level = log2(1.0 + abs(inRaymarchInfo.mDistance * cVoxelFineDetailMipMapDistanceScale));
float4 noise = Cloud3DNoiseTextureC.SampleLOD(Cloud3DNoiseSamplerC, inSamplePosition * 0.01, mipmap_level);

다음에 저희가 카메라에서 멀어질수록 MIP 레벨을 증가시키면서 3D 노이즈 텍스처를 샘플링합니다. 
이것은 고정된 MIP 레벨보다 성능을 약 15% 향상시키고 결과에 눈에 띄게 영향을 미치지 않습니다. 샘플러가 많이 호출되므로 작은 부분 하나하나가 도움이 된다는 것을 기억하십시오. 이제 우리가 위스피(wispy)와 위스피(billowy) 디테일 노이즈(detail noise)를 어떻게 정의하는지 알아보겠습니다.
 
 

먼저, 이 흐린 구름 사진을 보세요.  
밀도가 낮은 지역에서 매우 높은 주파수 세부 정보가 어떻게 존재하는지 주목합니다 
그리고 주파수가 더 밀집된 지역에서는 주파수가 더 낮은 위스피 구조를 가질 수 있습니다. 마치 저주파 구조를 이용하여 가장자리의 고주파 구조에 구조를 제공해야 할 것 같습니다.

우리는 이러한 관계를 우리의 상향식 잡음 합성물과 모방하고자 합니다.
 
 

우리의 치수 프로파일 NVDF를 떠올려 보세요. 보시다시피, 
이것은 이미 우리에게 클라우드 외부에서 내부로의 기울기를 제공합니다.
 
 

이를 모방하기 위해 차원 프로파일을 통해 저주파 위스피 노이즈에서 고주파 위스피 노이즈로 간단히 혼합할 수 있습니다.
 
 

이것이 치수 프로파일에 적용되었을 때의 모습입니다. 여기 보시는 것은 오직 고주파 소음입니다, 
그리고 이제 저주파 잡음 그리고 이제 둘 다 차원 프로파일 위에 혼합물을 가지고 있습니다.
 
 

이제 구름 모양의 세부 정보를 담은 이 사진을 보세요.  
중심부 근처에 저주파 물결 모양이 있다는 것을 주목하십시오 
그리고 더 좋은 단어가 없기 때문에 표면 근처에 더 높은 주파수의 볼록한 모양이 있습니다.
 
 

물결 모양의 경우, 우리는 가장자리가 코어보다 더 둥근 구조를 가지기를 원합니다. 그렇지 않으면 가장자리의 모든 곳에서 고주파 노이즈가 발생하여 차원 프로파일을 통해 저주파에서 고주파로 혼합됩니다.
 
 

다음은 이 소음 복합기의 예로, 오직 고주파수로 시작합니다. 저주파 및 이제 둘 다 차원 프로파일 위에 혼합물이 있습니다.
 
 

샘플 위치에 대한 두 개의 노이즈 합성물이 있으면 2.5D 클라우드에서와 마찬가지로 위스피 노이즈에서 위스피 노이즈로 혼합됩니다.
 
 
 

자, 여기 위스피 노이즈가 있는 구름이 있습니다,
약간의 노이징 현상이 있습니다. 그리고 이제 두 가지 모두 세부 유형 데이터에 혼합됩니다.
 
 

 

이것은 멀리서도 잘 작동하지만, 구름 가까이에서 비행할 때는 여전히 우리가 원하는 방식으로 확장되지 않았습니다. 가까이에서 세부적인 정보가 극적으로 부족하기 때문에, 우리는 고주파 노이즈 중 일부를 재사용하여 더 높은 주파수 노이즈를 생성하고 샘플러에서 많은 비용을 추가할 수 있는 3d 노이즈를 다시 샘플링하지 않는 방법을 고안했습니다.
 
 
 

우리는 소음을 -1~1 범위로 확대하고 abs 기능을 이용해서 0으로 2번 접어서 소위 2번 접는 소음을 만들어 냈습니다. 보시는 것처럼 주파수가 더 높은 것 같습니다. 우리는 소음 종류별로 이렇게 합니다.
 

 

 

그런 다음, 하위 주파수 노이즈 합성물과 같이 세부 유형 데이터를 사용하여 혼합합니다. 
마지막으로, 우리는 원래의 소음 합성으로부터 이 근처의 더 높은 주파수의 소음 카메라를 혼합했습니다.
 
 

이 기능이 향상되지 않은 상태에서 가까운 곳에 있는 모습입니다 
그 다음에는 향상된 기능이 있습니다.
 
 
 
 

그리고 구름 속으로 날아갈 때의 모습은 이렇습니다. 세부적인 소음이 효과를 발휘합니다. 보시다시피 효과는 매끄럽습니다.
 
 

이제 소음이 어떻게 작동하는지 이해했으니 소음을 적용해 보겠습니다…

2.5D 클라우드와 마찬가지로 재맵 기능과 노이즈 합성을 사용하여 차원 프로파일을 침식합니다.
그런 다음 밀도 척도 데이터에 상향 조정된 밀도를 곱합니다.
마지막으로 우리가 하는 한 가지 방법은 파워 함수를 사용하여 결과를 개선하는 것입니다. 밀도가 낮은 영역에서 더 많은 작업을 수행하여 밀도가 낮은 영역에서 정의를 도출하는 것입니다. 
그런 다음 상향 조정된 결과를 반환합니다.
 
 

요약하자면:
그 결과, 샘플러 기능을 단순화하는 지상 및 근접에서 클라우드를 원활하게 렌더링할 수 있습니다.  
메모리 및 액세스 시간 병목 현상을 방지하는 동시에 높은 수준의 세부 정보를 제공합니다. 
Up-rez 절차는 저해상도 차원 프로파일의 8미터 정밀도에서 0.5미터 정밀도를 제공합니다.  
여기서 핵심은 메모리 사용량과 사용 지침의 균형을 맞추는 것으로, 가능한 최상의 성능을 제공하는 것이었습니다. 
최악의 경우 960x540 픽셀 렌더의 경우 다른 많은 콘텐츠와 예산을 공유하는 지면에서 볼 때 10ms의 비용이 들기 때문에 이 개편 작업이 끝나려면 멀었습니다. 장편 영화 VFX나 애니메이션에서 합류하신 분들께는 좋을 것 같지만, AAA 게임 실시간은 아닙니다. 
하지만 지금까지는 콘솔의 물리적 메모리에서 실행해야 하는 세부 정보를 얻기 위해 고군분투하고 있었습니다. 계속 진행해 보겠습니다.
 
 

//
// Function to up-rez a Dimensional Profile NVDF using noise Detail Type and Density Scale NVDF’s
//
float GetUprezzedVoxelCloudDensity(CloudRenderingRaymarchInfo inRaymarchInfo, float3 inSamplePosition, float inDimensionalProfile, float inType, float inDensityScale, float inMipLevel, bool inHFDetails)
{
    // Apply wind offset 
    inSamplePosition -= float3(cCloudWindOffset.x, cCloudWindOffset.y, 0.0) * voxel_cloud_animation_speed;
    
    // Sample noise
    float mipmap_level = GetVoxelCloudMipLevel(inRaymarchInfo, inMipLevel);
    float4 noise = Cloud3DNoiseTextureC.SampleLOD(Cloud3DNoiseSamplerC, inSamplePosition * 0.01, mipmap_level);
    // Define wispy noise
    float wispy_noise = lerp(noise.r, noise.g, inDimensionalProfile);
    // Define billowy noise 
    float billowy_type_gradient = pow(inDimensionalProfile, 0.25);
    float billowy_noise = lerp(noise.b * 0.3, noise.a * 0.3, billowy_type_gradient);
    // Define Noise composite - blend to wispy as the density scale decreases.
    float noise_composite = lerp(wispy_noise, billowy_noise, inType);
    // Get the hf noise which is to be applied nearby - First, get the distance from the sample to camera and only do the work within a distance of 150 meters. 
    if (inHFDetails)
    {
        // Get the hf noise by folding the highest frequency billowy noise. 
        float hhf_noise = saturate(lerp(1.0 - pow(abs(abs(noise.g * 2.0 - 1.0) * 2.0 - 1.0), 4.0), pow(abs(abs(noise.a * 2.0 - 1.0) * 2.0 - 1.0), 2.0), inType));
    
        // Apply the HF nosie near camera.
        float hhf_noise_distance_range_blender = ValueRemap(inRaymarchInfo.mDistance, 50.0, 150.0, 0.9, 1.0);
        noise_composite = lerp(hhf_noise, noise_composite, hhf_noise_distance_range_blender);
    }
    // Composote Noises and use as a Value Erosion
    float uprezzed_density = ValueErosion(inDimensionalProfile, noise_composite);
    // Modify User density scale
    float powered_density_scale = pow(saturate(inDensityScale), 4.0);
    // Apply User Density Scale Data to Result
    uprezzed_density *= powered_density_scale; 
        
    // Sharpen result and lower Density close to camera to both add details and reduce undersampling noise
    uprezzed_density = pow(uprezzed_density, lerp(0.3, 0.6, max(EPSILON, powered_density_scale)));
    if (inHFDetails)
    {
        float distance_range_blender = GetFractionFromValue(inRaymarchInfo.mDistance, 50.0, 150.0);
        uprezzed_density = pow(uprezzed_density, lerp(0.5, 1.0, distance_range_blender)) * lerp(0.666, 1.0, distance_range_blender);
    }
    // Return result with softened edges
    return uprezzed_density;
}

 
 

조명은 역설적이게도 체적 광선 행진에서 가장 무거운 부분 중 하나입니다. 우리의 임무는 그 표면상 불가능했습니다. 엄청난 성능을 절감하는 동시에 결과의 품질을 향상시켜 구름을 통과하는 비행을 지원하는 것이었습니다.
 
 

우리는 번개와 같은 직접 산란, 주변 산란 및 2차 광원에 대한 광 에너지 성분을 계산하고, 직접 산란 계산은 내가 경멸하며 계속 언급하는 빛을 향한 2차 광선 행진 때문에 꽤 비싸다는 것을 기억하십시오. 우리는 수년 동안 그 2차 광선 행진을 가속화하거나 대체하기 위해 모든 종류의 것을 시도해 왔습니다.
 
 

복셀로 생각하기 시작하고 나서야 몇 년 전 우리가 가졌던 더 높은 아이디어 중 하나가 더 제정신이 된 것처럼 보였습니다. 
수석 테크 프로그래머인 Nathan Vos는 오랫동안 광선 행진을 가시광선에서 분리하기를 원했지만 복셀 작업을 하기 전까지는 복잡성 때문에 가치가 없었습니다.
 
 

우리의 새로운 접근방식에서, 
처음 두 샘플은 우리가 전에 했던 것과 같이 채취됩니다 
그러나 나머지 샘플은 별도의 패스로 미리 저장되고 256x256x32 복셀 그리드에 저장되며, 이는 우리가 처음 두 샘플 후에 샘플을 채취하는 것입니다.  
이것은 구름 표면 근처의 세부 정보를 보존하고 그 이후에 더 확산된 결과를 제공합니다. 분명히 말하자면, 이 사전 계산은 광선 행진의 외부에서 발생합니다.
 
 

다음은 복셀의 작동 방식입니다. 만약 복셀 안에 구름이 있다면, 광선이 복셀 사이로 이동하고 각 단계에서 복셀 그리드와 밀도의 경계가 축적됩니다. 
이를 통해 렌더 시간이 약 40% 감소하는 것을 확인했습니다. 하루 중 시간과 태양을 향한 광선의 길이에 따라 작업 자체의 비용은 0.1에서 0.2ms 사이입니다. 비용 자체는 8프레임에 걸쳐 상각됩니다.
 
 

비용 절감 외에도 마침내 클라우드 간의 먼 거리 그림자를 렌더링할 수 있었습니다. 
여기 우리가 전에 했던 것처럼 256미터, 10개의 샘플 광선 행진이 있을 때의 모습이 있습니다.
그리고 여기에 복셀을 이용한 조명이 있습니다.

최적화를 통해 훨씬 더 나은 결과를 얻을 수 있는 드문 경우였습니다.
 
 

복셀로의 전환은 구름의 내부 빛과 어두운 가장자리를 담당하는 다중 산란 효과를 모형화하는 방법을 공고히 할 수 있게 해주었습니다. 조금 더 깊이 들여다 봅시다.
 
 

텍사스 주 휴스턴에 있는 자연사 박물관의 아주 미세한 결정 성장을 보세요.
각각의 결정 사이의 작은 틈들을 통해 중심부를 깊이 들여다 볼 수 있기 때문에 흥미롭습니다. 눈을 흐리게 하면, 표면 아래에서 더 밝은 물질 덩어리처럼 보이는 것을 거의 볼 수 있습니다.
 
 

유사점에 주목하지 않는 것은 어렵습니다. 구름의 표면 경계 근처에는 입사광을 더 무작위한 방향으로 산란시킬 물질이 적어 대부분의 광선이 정상적으로 진행되어 우리 눈에 도달하지 못합니다. 당신은 구름 내부의 확률장을 상상할 수 있는데, 산란 방향이 더 등방성 또는 전방향성이 될수록 당신이 관찰자를 향해 내산란할 가능성이 증가합니다. 이 광량은 두꺼운 면 뒤의 손전등처럼 표면을 통해 비춥니다.
 
 

또는 이러한 결정 형성 내부의 빛나는 빛의 부피. 결과 산란 전위로서 기하학적으로 이 효과를 살펴보면 실시간 목적으로 모델링하기가 훨씬 쉽습니다.
 
 

다행히도 이 볼륨은 있는데 이 클라우드를 예로 들어보겠습니다.   이것이 치수 프로파일입니다.
이것은 우리의 확률 분야의 기초가 됩니다.
 
 
 

이 클라우드를 다시 살펴보겠지만, 이제 클라우드가 정확히 우리와 태양 사이에 있도록 정렬해 보겠습니다. 
우리는 이 각도에서 구름을 잘라내는 이 구름의 한 조각을 살펴볼 것입니다.

 
 

여기 우리가 시작했던 것과 같은 사이드 뷰의 슬라이스가 있습니다. 
그리고 빛의 에너지를 모두 끄도록 합시다.

 
 
 

ms_volume = dimensional_profile;
cloud_distance = GetVoxelCloudDistance(inSamplePosition);
ms_volume *= exp(-inSunLightSummedDensitySamples * Remap(sun_dot, 0.0, 0.9, 0.25, ValueRemap(cloud_distance, -128.0, 0.0, 0.05, 0.25)));

만약 우리가 차원 프로파일을 산란으로 사용한다면 다음과 같습니다.  
빛이 이런 식으로 산란하기 때문에 결국은 흡수되기 때문에, 우리는 a를 적용합니다 
맥주 맛이 나는 감쇄 곡선을 운동장에 적용했습니다.
이 곡선은 내부 광채 효과를 내기 위해 태양 근처와 구름 내부 근처의 벡터에 대해 완화됩니다.
게임과 함께 배송한 클라우드를 살펴보도록 하겠습니다.
 
 

다중 산란 근사치가 없는 태양이 우리 앞에 있는 모습입니다.
그리고 근사치로.
 
 

그리고 여기 태양이 우리 뒤에 있는 모습이 근사치 없이 보입니다.
그리고 근사치로.
 
 

2.5D 구름과 마찬가지로 차원 프로파일을 확률 필드로 사용하여 주변 광전위를 계산합니다.

float ambient_scattering = pow(1.0 – dimensional_profile, 0.5);

 
 

주변광이 없는 상태입니다, 
그리고 앰비언트 라이트 기여도를 적용한 모습입니다.
라이팅을 미리 계산하고 광선 행진에서 샘플링하는 것이 매우 효과적이어서 이 기법을 사용하면 방향성을 추가하여 앰비언트 라이트 기여도를 최종적으로 개선할 수 있을 것이라고 생각했습니다.

 
 

하늘을 향한 광선을 따라 합산된 밀도를 미리 계산하면 샘플 위치 위의 구름에 의해 하늘이 가려진 영역에서 주변 산란이 덜 강해지도록 스케일링하는 데 사용할 수 있는 결과를 얻을 수 있습니다.
하늘을 향해 합산된 밀도로부터 계산된 감쇠 신호로 원래 방법의 스케일을 조정하면 됩니다.

float ambient_scattering = pow(1.0 – dimensional_profile, 0.5) * exp(-summed_ambient_density);




 
 

앰비언트 스캐터링 컴포넌트에 방향성이 없는 모습은 다음과 같습니다.
그리고 복셀 기반 라이팅으로 약간의 방향성을 제공한 모습입니다. 
 
 
 
 

조명과 같은 보조 광원은 다시 한 번 확률 필드로 근사화했습니다.
먼저 구형 감쇠를 사용하여 플래시의 포텐셜 에너지를 정의하고 
여기에 비균질 밀도의 효과를 가짜로 만들 수 있는 무언가를 곱했습니다.

potential_energy = pow( 1.0 – (d1 / radius), 12.0);
pseudo_attenuation = (1.0 – saturate(density * 5.0)); 
glow_energy = potential_energy * pseudo_attenuation;

 
 
 

게임 내 모습은 다음과 같습니다. 
게임입니다. 앞으로 나올 기능의 미리 보기라고 생각하시면 됩니다.
 
 
 

이는 매우 큰 개편이므로 시간대별 주기가 여전히 의도한 대로 작동하는지 확인하는 것이 중요합니다. 그래서 여기에 필수 TOD 주기가 있습니다.
 
 
 
 

원경 그림자는 상당한 차이를 만들어냅니다.
 
 
 
 

간단히 말해 광선 행진을 분리하고 복셀 그리드에서 합산된 밀도를 미리 계산하여 저장했습니다.
이를 통해 프레임 비용을 40% 절감하는 동시에 장거리 그림자를 추가하여 결과물의 품질을 실제로 개선했습니다. 
또한 이 접근 방식을 사용하여 주변 조명을 개선했습니다.
또한 일부 조명 근사치는 치수 프로파일을 조회하는 것만으로 더 간단하게 계산할 수 있었습니다.
때로는 새롭고 효율적인 접근 방식을 채택하면 다른 문도 열립니다.


 
 

바로 이어서, 클라우드를 통한 비행을 지원하려면 성능 절감을 위해.... 광선 행진도 살펴봐야 합니다. 

 
 
 

우리의 딜레마를 떠올려봅시다. 각 샘플 위치를 기준으로 구름이 어디에 있는지 알지 못하면 광선을 따라 연속적인 샘플을 효율적으로 배치할 수 없습니다. 
 
 
 

엔벨로프 방식 클라우드의 경우 2D 제작 데이터에서 생성된 클라우드 주위에 샘플을 더 잘 배치할 수 있도록 콘 스텝 매핑과 거리 스텝 매핑을 결합한 것을 기억하세요. 이제 우리의 문제는 처음부터 진정한 3차원 문제입니다. 다행히도 오랫동안 사용하길 기다려온 기술이 있습니다.
 
 
 

구체 추적은 가장 가까운 암시적 표면까지의 3차원 거리를 사용하여 스텝 크기를 결정하는 기술입니다. 부호화된 거리 필드는 공간의 모든 지점에 대해 이러한 거리의 측정값을 저장합니다. 양수 값은 오브젝트 외부에 있고 음수 값은 내부에 있습니다.
볼류메트릭 클라우드 사이를 광선 행진할 때 이 거리를 사용하여 가능한 한 가장 큰 스텝을 취하고 광선을 따라 작업량을 줄일 수 있습니다. 

 
 
 

부호화된 거리 필드를 사용하는 방법에 대한 정보를 얻을 수 있는 두 가지 훌륭한 출처가 있습니다. 첫 번째는 이니고 퀼레즈의 웹사이트로, 여러 원시 도형에서 부호화된 거리를 계산하는 방법을 찾을 수 있습니다.  두 번째는 세바스티앙 안톨넨의 클레이북 강연 슬라이드입니다. -네, 오늘 제 바로 앞에서 강연한 세바스찬입니다. 여기에서 고성능 사례에서 효과적으로 사용하는 방법을 확인할 수 있습니다.  
 
 
 
 

아티스트가 프랑켄클라우드 스케이프 빌드를 완료하면 
3D 부호화된 거리 필드를 생성하고 이를 별도의 NVDF로 기록합니다. 데이터의 원래 범위는 -256에서 4096이지만 0에서 1 범위로 스케일링합니다. 중앙 이미지에서 왼쪽 이미지의 구름에서 멀어질수록 값이 증가하는 것을 볼 수 있습니다.  
 
 
 

모든 단계에서 이 데이터를 조회할 것이고 이유 없이 메모리 병목 현상을 일으키고 싶지 않기 때문에 모델링 데이터 NVDF와 별도로 보관합니다. 병목현상에 대해 말하자면, 이 필드의 압축이 중요합니다.

 
 
 

다음은 세계적으로 유명한 네이선 리드의 BC 테이블을 통해 이를 설명합니다.
이 SDF는 샘플링이 많으므로 압축하면 텍셀당 비트 수가 줄어들고 결과적으로 메모리 대역폭 요구량이 줄어들어 성능이 향상될 것입니다.  
1채널 BC4가 이상적으로 보입니다. 단, 텍셀당 비트 수가 적다는 것은 정밀도가 낮아진다는 것을 의미하며 이는 
큰 문제입니다. 압축 해제 값이 너무 낮으면 레이마치가 필요 이상으로 많은 단계를 거치게 되고, 너무 높으면 렌더링 오류가 발생합니다.
이 문제를 어떻게 해결할 수 있을까요? 미술 학위를 가진 사람들은 렌더링 엔지니어를 블랙 매직이라고 부릅니다. 


 
 

수석 수석 기술 프로그래머 휴 말란이 개발한 커스텀 BC1 컴프레서를 사용하여 다음과 같이 압축했습니다. 
16비트에 가까운 정밀도를 제공하는 방식으로 SDF를 압축했습니다.
이 슬라이드 바로 뒤에 휴의 작업에 대한 자세한 설명이 담긴 보너스 슬라이드가 있지만, 요약하자면 데이터를 세 개의 채널로 분할한 다음 레이-마치 셰이더에서 SDF를 샘플링한 후 재조립하는 방식입니다. 

이 접근 방식은 SDF 데이터가 부드럽기 때문에 효과가 있었습니다.
광선의 길이에 따라 가장 자주 액세스하는 복셀 데이터의 메모리 병목 현상을 줄여 압축되지 않은 텍스처보다 10~30% 절약되는 것으로 나타났습니다. 

// Decompress BC1
dot(sampled_color.rgb, float3(1.0, 0.03529415, 0.00069204))

 
 
 

float3 sampled_color = EncodedScalarTexture.Sample(BilinearSampler, uv).rgb;
float result = dot(sampled_color, float3(1.0, 0.03529415, 0.00069204));

DXT1의 경우 각 4x4 블록에는 두 개의 16비트 엔드포인트 컬러가 있고, 각 텍셀에는 이 두 컬러 간의 블렌드를 지정하는 두 비트가 있습니다.  오류를 최소화하는 커스텀 압축기를 작성했습니다.  구체 추적을 위해 SDF를 압축하기 때문에 인코딩된 근사값이 고정밀 입력값보다 작도록 해야 합니다.  근사값이 이상적인 값보다 크면 구체 트레이스가 너무 멀리 떨어져서 아이소서피스 안쪽이나 바깥쪽으로 끝날 수 있습니다.

기본 아이디어는 16비트 값을 565 RGB 색상으로 표현하는 것입니다. 즉, 빨간색 채널은 가장 중요한 5비트, G는 그 다음 6비트, B는 가장 중요하지 않은 5비트를 갖습니다.  런타임에 셰이더는 텍스처의 이중선형 샘플을 수행하고 float3(1.0, 1.0/32, 1.0/(32*64))의 도트 프로덕트를 사용하여 RGB를 스칼라로 변환합니다.

참고: 그라디언트를 부드럽게 증가시키면 파란색 채널이 최대로 증가하다가 최소로 떨어지고 녹색 채널이 증가하는 텍스처가 생성됩니다.  (녹색과 빨간색도 마찬가지입니다.) 도트 프로덕트 결과가 이러한 전환에 대해 올바르게 작동합니까?  그렇습니다. 간단한 예를 들어보겠습니다.  float2(10,1), 즉 G*10 + B로 도트프로덕트로 언패킹된 텍스처가 두 개의 이웃이 29(G=2,B=9)와 30(G=3,B=0)이라고 가정해 보겠습니다.  중간에 이선 필터링된 샘플은 29.5의 값을 제공해야 합니다.  실제로 그렇습니다: G는 2.5, B는 4.5가 됩니다. 도트곱은 예상대로 2.5*10 + 4.5 = 29.5입니다.
 
 
 

RGB565Color gConvertScalarToDXT1Endpoint_RoundDown(float inScalar, rcVec3 inUnpackDot)
{
    // gDXT1_5BitChannelValues and gDXT1_6BitChannelValues are lookup tables holding the exact value of each of the 32 R/B levels, and 64 G levels.
    int index_r = sFindLowerBoundIndex(gDXT1_5BitChannelValues, inScalar / inUnpackDot.mX);
    float val_r = gDXT1_5BitChannelValues[index_r] * inUnpackDot.mX;
    float residual_after_r = inScalar - val_r;

    float query_g = residual_after_r / inUnpackDot.mY;
    int index_g = sFindLowerBoundIndex(gDXT1_6BitChannelValues, query_g);
    float val_g = gDXT1_6BitChannelValues[index_g] * inUnpackDot.mY;
    float residual_after_g = residual_after_r - val_g;

    float query_b = residual_after_g / inUnpackDot.mZ;
    int index_b = sFindLowerBoundIndex(gDXT1_5BitChannelValues, query_b);

    return gCreate565Color(index_r, index_g, index_b);
}

안타깝게도 5-6-5 RGB 끝점 색상은 이전 슬라이드에서 암시한 단순한 선형 방식으로 [0,1]로 디코딩되지 않으며, 보간된 두 색상의 계산에도 몇 가지 복잡한 문제가 있습니다.  원래 GPU에는 비용이나 성능 때문에 이상한 반올림이 있으며, 이러한 동작은 그대로 유지되었습니다.  자세한 내용은 다음 웹페이지를 참조하세요:
http://www.ludicon.com/castano/blog/2009/03/gpu-dxt-decompression/
https://fgiesen.wordpress.com/2021/10/04/gpu-bcn-decoding/

즉, 언패킹에 사용되는 도트 프로덕트가 그렇게 간단하지 않다는 뜻입니다.  G 값은 R 채널에서 가능한 가장 큰 단계를 포함하도록 스케일업해야 하며, B 값도 비슷한 처리가 필요합니다.

도트곱이 완성되면 스칼라를 의사 코드가 표시된 엔드포인트 색상으로 변환할 수 있습니다.  먼저 입력값보다 낮은 가장 큰 R 값을 찾습니다.  약간의 오차가 있을 수 있으므로 그 차이보다 작은 가장 큰 G값을 찾습니다.  그런 다음 B 값도 동일하게 처리합니다.

두 개의 엔드포인트 값을 얻었으면 보간된 두 값을 계산하여 각 텍셀에 대해 가능한 네 가지 값을 생성할 수 있습니다.  그런 다음 블록의 16개 텍셀 각각에 대해 앞서 설명한 구체 추적 오류를 피하기 위해 전체 정밀도 값보다 낮으면서도 가장 가까운 옵션을 찾습니다.

가장 확실한 엔드포인트 선택은 4x4 블록의 최소값과 최대값을 사용하는 것이지만, 이는 인코딩된 최대 색상이 4x4 텍셀 중 하나에서만 사용된다는 것을 의미합니다.  따라서 평균 오류를 개선하기 위해 높은 엔드포인트 값을 낮게 조정합니다.
 
 
 
 

복셀 방식에 대한 마지막 NVDF이므로 잠시 시간을 내어 각 시스템의 메모리 비용을 비교해 보겠습니다.
VP = 0.5메가바이트
EVM = 9메가바이트
VM = 25메가바이트이므로 모든 추가 세부 정보가 어디에서 나오는지 알 수 있습니다.
무료로 제공되는 것은 없지만 복셀 클라우드를 사용하면 다른 방법보다 훨씬 더 많은 작업을 수행할 수 있습니다.
 
 
 
 

하지만 이것이 복셀 광선 행진을 가속화하는 유일한 방법은 아닙니다. 
먼저 광선의 바운드를 정의하여 가능한 작업의 양을 제한합니다. 
광선은 카메라에서 시작하여 
복셀 그리드 바운딩 박스와 교차하는 곳에서 끝나거나 
지오메트리와 교차하는 지점 중 더 가까운 곳에서 끝납니다.  



다음으로 다음 샘플에 대한 단계의 크기를 계산합니다. 이 거리는 3개의 소스로부터 구축됩니다.  
부호화된 거리 
거리에 따라 점진적으로 증가하는 적응형 스텝 크기
그리고 샘플링 부족 노이즈에 대응하도록 설계된 시간적 지터 오프셋. 
SDF는 클라우드 외부에 스텝을 최적으로 배치하는 데 사용됩니다. 
반면 적응형 스텝 크기는 카메라로부터의 거리에 따라 증가하며 클라우드 내부에 샘플을 배치하는 데 사용됩니다. 이렇게 하면 카메라에서 멀리 떨어진 구름은 더 큰 스텝 크기의 이점을 누릴 수 있고, 구름 사이의 샘플은 최대한 최적으로 배치할 수 있습니다. 
'지터가 가까운 곳의 애니메이션에서 먼 곳의 정적으로 전환되어 멀리 있는 구름을 반짝이게 만들지 않고 샘플링 아티팩트를 줄입니다.  
부호화된 거리는 적응형 스텝 크기로 최대화되므로 클라우드 내부를 샘플링하여 음수가 되면 적응형 스텝 크기를 사용하도록 전환합니다.  
그런 다음 이 단계 크기를 샘플 거리에 추가하여 다음 샘플의 샘플 위치를 구축하는 데 사용할 수 있습니다.  




샘플을 채취할 때는 최적화를 위해 SDF를 살펴봅니다. 
SDF가 0보다 크면 클라우드 외부에 있으므로 샘플을 수집할 필요가 없습니다.
0보다 작으면 앞서 설명한 대로 밀도 및 조명 샘플을 수집하여 뷰 레이를 따라 통합합니다.

 
Voxel Cloud Ray-March HLSL Code

//
// Execute main raymarch function for the view ray - marches over the detected intersection segment and returns pixel data
//
void RaymarchVoxelClouds(CloudRenderingGlobalInfo inGlobalInfo, float2 inRaySegment, inout CloudRenderingPixelData ioPixelData)
{
    // Init raymarch info struct and set the start distance to the beginning of the ray segment
    CloudRenderingRaymarchInfo raymarch_info;
    raymarch_info.mDistance = inRaySegment[0];

    // Ray-march as long as the distance to place a sample is less than the distance to the end of the segment or terrain intersection
    while (ioPixelData.mTransmittance > view_ray_transmittance_limit && raymarch_info.mDistance < min(inRaySegment[1], inGlobalInfo.mMaxDistance))
    {
        // Calculate the sample positon by adding thre distance times the view vector to the camera position - this saves solve vgprs by not passing a float3 through the loop.
        float3 sample_pos = inGlobalInfo.mCameraPosition.xyz + inGlobalInfo.mViewDirection * raymarch_info.mDistance;

        // The adaptive step size increases over distance but can be no smaller than 1 meter.
        float adaptive_step_size = max( 1.0, max(sqrt(raymarch_info.mDistance), EPSILON) * 0.08);

        // Get the distance to the nearest voxel cloud "surface" by looking up the signed distance from the SDF at this position
        raymarch_info.mCloudDistance = GetVoxelCloudDistance(sample_pos);

        // The step size is the maximum of the Signed distance and the adative step size. This effectively makes the step size the adaptive stepsize when we are inside of a cloud.
        raymarch_info.mStepSize = max(raymarch_info.mCloudDistance, adaptive_step_size);

        // Apply the jitter offset - this is based on the step size and only animates within a certain distance from camera.
        float jitter_distance = (raymarch_info.mDistance < 250.0 ? inGlobalInfo.mJitteredHash : inGlobalInfo.mStaticHash) * raymarch_info.mStepSize;
        sample_pos += inGlobalInfo.mViewDirection * jitter_distance;

        // Only take samples where the distance to the cloud is lower than a threshold value - this ensures that we dont start taking density samples in areas where we aren't yet in a cloud.
        if (raymarch_info.mCloudDistance < 0.0)
        {
            // Get Sample Data
            CloudRenderingSampleData voxel_cloud_sample_data = GetVoxelCloudSampleData(inGlobalInfo, raymarch_info, sample_pos);

            // Integrate at this step like we do for all other samples in the cloud renderer
            IntegrateCloudSampleData(voxel_cloud_sample_data, inGlobalInfo, raymarch_info, ioPixelData, false);
        }          

        // Take a step.
        raymarch_info.mDistance += raymarch_info.mStepSize;
    }
   
}

레이마칭 구현



 

지난 30분 동안 특정 사례의 성능 향상에 대해 백분율로 설명하는 데 많은 시간을 할애했습니다.

이제 성과를 체계적으로 살펴볼 차례입니다.  

지금부터 살펴볼 수치에 대해 몇 가지 유념해야 할 점이 있습니다:

구름 속을 비행할 때는 렌더링하는 대부분의 부분이 구름입니다. 
지상에서 멀리 떨어져서 볼 때는 나머지 콘텐츠에 비례하여 비용이 확장되어야 합니다. 이러한 리밸런싱이 동적으로 작동하도록 하는 것은 다각형 게임 콘텐츠와 마찬가지로 클라우드에서도 핵심이며, 이 게임에서 이 시스템에서 성공하기 위한 척도였습니다. 


이제 지상에서 구름을 
구름을 살펴보겠습니다, 
지면과 구름 콘텐츠가 혼합된
 그리고 완전히 구름만 있는 구름을 살펴보겠습니다. 각 렌더링은 960x540 해상도로 이루어졌습니다.


 
 

지면부터 시작하여 라이팅이나 레이 마칭 최적화가 없는 경우 비용은 10ms입니다.
복셀 기반 라이팅 최적화를 적용하면 6.1ms로 감소합니다.
그리고 레이 마치를 최적화하면 2.2ms로 감소합니다. 

지오메트리 패스의 비용을 살펴보세요. 이것과 클라우드를 합치면 9.2ms가 됩니다.
 
 
 

구름과 눈에 보이는 풍경 사이의 공중으로 이동할 때 조명이나 광선 행렬 최적화가 없는 경우 12ms의 비용이 발생하는데, 이는 구름의 밑면을 따라 스쳐 지나가는 광선이 많기 때문에 발생하는 현상입니다.  이 문제는 구름의 높이를 오프셋하여 광선을 줄임으로써 완화할 수 있습니다.
복셀 기반 조명 최적화를 사용하면 8.2ms로 단축됩니다.
그리고 광선 행렬 최적화를 사용하면 4ms로 감소합니다.
지오메트리 패스 비용은 2밀리초 감소한 반면 클라우드 비용은 2밀리초 증가한 것을 살펴보십시오. 아직 균형을 유지하고 있습니다.

 
 

클라우드에 완전히 몰입하고 라이팅이나 레이 마칭 최적화가 없는 경우 비용은 10ms입니다.
복셀 기반 라이팅 최적화를 적용하면 8.0ms로 감소합니다.
그리고 레이 마치를 최적화하면 4.0으로 떨어집니다. 
이제 지오메트리 패스의 비용을 살펴보면 1ms가 감소하고 구름은 동일하게 유지되었습니다.

따라서 지오메트리와 클라우드 간에는 한 상황에서 다음 상황으로 비용이 잘 전달됩니다. 
 
 
 
 

요약하자면:
우리는 모든 뷰레이 단계마다 캡처되는 샘플의 메모리 병목 현상을 피하기 위해 압축된 SDF를 사용합니다.
SDF, 적응형 스텝 크기 및 지터링 샘플링을 결합하여 샘플링 부족 노이즈를 완화하면서 두 가지 장점을 모두 얻을 수 있습니다.
비용은 지상인지 공중인지에 따라 2.2ms에서 4ms까지 다양합니다.  
지상에서 완전한 몰입감으로 성능이 적절하게 확장됩니다.

 
 
 

30hz, 60hz, 40hz. 포테이토, 파테이토.....  푸-타더? 누구나 좋아하는 플레이 방식이 있습니다. 그래픽 프로그래머와 아티스트로서 저희의 임무는 모든 모드가 지원되도록 하는 것입니다.  30hz 모드에서는 성능이 약간 떨어지지만, 프레임 렌더링에 사용할 수 있는 시간이 절반으로 줄어드는 고프레임 모드와도 싸워야 합니다.

 
 
 

엔벨로프 메서드에서는 템포럴 업스케일링을 없애고 렌더링을 두 패스로 나눈 것을 기억하세요:  원거리는 고해상도로 렌더링하여 에일리어싱을 방지하고, 근거리에서는 저해상도로 렌더링하여 광선 행진에서 가장 비용이 많이 드는 부분의 성능을 개선했습니다.  비슷한 접근 방식이 40 및 60hz 모드에서도 작동할까요? 정답은 '그렇다'입니다.  고프레임레이트 모드에서도 정확히 동일한 방식을 사용합니다.

 
 

다음은 예시입니다:
이 프레임은 30hz 모드에서 4ms가 소요됩니다. 하지만 렌더링을 두 가지 해상도로 분할하여 심도를 높이면 비용은 2.1ms로 떨어집니다. 이 솔루션은 40 및 60hz 모드 모두에 사용했습니다. 
비행 중에 두 가지 접근 방식을 나란히 비교해 보겠습니다.



오른쪽 60hz 비디오에서 구름의 선명하고 밀도가 높은 부분의 픽셀이 약간 더 많은 것을 볼 수 있지만 밀도가 낮은 영역은 이를 더 잘 숨깁니다.  이 점을 제외하면 경험 자체는 변하지 않았기 때문에 결과는 매우 성공적입니다.




 

요약하자면
렌더링을 두 개의 패스로 분할하여 가까운 곳은 저해상도로, 먼 곳은 고해상도로 렌더링합니다.
이렇게 하면 렌더링 시간이 거의 절반으로 줄어드는데, 아시다시피 60hz 모드에서 프레임을 그리는 데 걸리는 시간이 절반으로 줄어든다는 점을 고려하면 좋은 방법입니다. 
40hz도 같은 방법을 사용합니다.
 
 
 

이제 배경 요소이자 탐색 가능한 환경으로 작동하는 클라우드 스케이프를 구축한 방법부터 시작하여 게임에서 이 시스템을 실제로 적용한 몇 가지 사례를 살펴보겠습니다.


 
 

종말 이후의 로스앤젤레스를 그린 "치킨스크래치" 낙서입니다. 태양의 호를 기준으로 실루엣을 잘 구성해야 하기 때문에 동서 방향과 남북 방향을 따라 배치하는 것이 도움이 되었습니다.

 
 

먼저 큰 구름을 배치하여 하루 종일 여러 각도에서 지상에서 보기 좋게 만듭니다.  
그런 다음 다른 기능과 결합 조직을 추가하여 나머지 구름 풍경에 통합합니다. 
일부 구름의 경우 탐색 기능을 향상하기 위해 Atlas 툴을 사용하여 터널과 동굴을 추가했습니다. 
 
 
 




우리는 또한 하늘을 날아다니며 하늘에서 어떻게 보이는지 주시했습니다.  - 이렇게 빠르지는 않았지만요.
동굴 다이빙은 정말 재미있어서 때때로 일과 놀이를 구분하기 힘들 정도였어요. 
 
 
 
 

정상 속도에서 터널 다이빙을 하는 다른 영상을 살펴봅시다. 
구름 안쪽과 터널 뒤쪽으로 갈수록 빛의 품질이 어떻게 달라지는지 보세요. 조명이 제대로 작동합니다. 
날개 끝에 있는 작은 깃발도 보이시나요? 구름과의 교차점을 감지할 때마다 거기에서 입자를 방출했습니다.
 
 
 
 

"구름에는 이런 터널이 없다"고 생각하실 경우를 대비해 말씀드립니다, 
지난 금요일에 촬영한 사진입니다.
그리고 이것은 기계 공룡이 아니라 그냥 새입니다.
 
 
 
 

각 구름 형성에 대해 이 작업을 여러 번 반복하여 모든 구름을 클라우드 스케이프에 결합했습니다. 

 
 
 

다음은 다양한 위치에서 게임플레이 클라우드 스케이프가 어떻게 보이는지 보여주는 몇 가지 예시입니다.
 
 
 
 
 

시네마틱의 경우 트랜스폼을 사용하여 샷마다 클라우드 스케이프의 위치를 변경할 수 있어 메모리를 늘리지 않고도 다양한 변형을 만들 수 있었습니다.
 
 
 

게임 마지막에 등장하는 보스전을 위해 저희는 플레이 가능한 영역을 경기장처럼 감싸는 구름 풍경을 만들었습니다.

 
 

요약하자면
프랑켄클라우딩을 사용하면 복셀로 구름을 더 쉽게 모델링할 수 있습니다.
하늘에 텍스트를 쓰는 등 거의 모든 것을 만들 수 있기 때문에 워크플로 측면에서 진정한 패러다임의 전환이라고 할 수 있습니다. 
시네마틱은 트랜스폼을 사용하여 기존 클라우드 스케이프를 활용하여 메모리를 절약할 수 있습니다.
보스전 등 맞춤형 환경 클라우드 스케이프 제작이 가능합니다.  
 
 
 
 

이제 거의 이야기의 마지막에 다다랐으니 이제 우리가 배운 것을 되돌아볼 차례입니다. 

 
 

현재 우리가 복셀 클라우드 메서드라고 부르는 것은 꽤 잘 작동하는 것으로 밝혀졌습니다.
이전과 비교했을 때 복셀 클라우드는 거의 모든 상자를 체크하는 것을 볼 수 있습니다.
진화는 앞으로 더 발전해야 할 부분이지만, 이를 위해서는 실시간 복셀 기반 클라우드 진화 자체를 해결해야 할 뿐만 아니라 부호화된 거리 필드를 즉석에서 생성해야 합니다.  미래를 위한 일입니다.  
우리는 이것을 성공이라고 부릅니다. 우리가 원했던 모든 것을 유지하면서 더 발전된 것을 만들어내는 경우는 거의 없습니다.
여기서 한 가지 언급할 만한 점은 복셀 밀도가 중요한 것이 아니라 결과물의 시각적 복잡성이 중요하다는 것입니다. 더 높은 해상도로 작업하는 것도 분명 한 가지 방법이지만, 앞서 살펴본 것처럼 컴퓨터 그래픽에서 한 가지 작업을 수행하는 데는 일반적으로 한 가지 이상의 방법이 있습니다.  

이제 잠시 시간을 내어 구름 속을 날아다니는 경험을 즐겨 보시기 바랍니다.

 
 
 

 

...하지만 가기 전에 한 가지 더 말씀드릴 것이 있습니다. 
저희는 단순히 구름 위를 비행하는 것 이상의 것을 하고 싶었습니다. 
폭풍우 속에서 벌어지는 독특한 게임플레이 경험도 만들고 싶었습니다.

스톰버드에는 많은 전설이 있습니다. 스톰버드는 날씨를 국지적으로 제어하여 번개와 함께 작은 폭풍 구름을 생성할 수 있는 하늘의 정점 포식자입니다. 그렇다면 왜 게임에서 이 모습을 보여주지 않았을까요? 간단히 말해, 기술적으로 구현할 방법이 없었기 때문입니다. 지금까지는 말이죠.

수석 월드 디자이너인 엘리야 호크는 일종의 고양이와 쥐의 만남을 염두에 두고 있었습니다.  거대한 정점 포식자가 작은 새를 타고 날아다니며 그 공간에 들어오는 게임입니다. 새는 사람을 귀엽다고 생각하고 잠시 장난을 치다가 죽일지도 모릅니다. 엘리야는 이 새가 당신을 은신처인 폭풍 구름으로 유인한 다음, 장난스럽게 몇 차례 몰래 공격을 가한 다음 마침내 탈것을 죽여 추락에서 살아남았는지 확인하는 장면을 상상해 보았습니다.  다행히도 알로이에는 글라이더가 있습니다. 어떤 전투가 벌어졌는지 살펴봅시다.

 

스톰버드를 죽이고 나면 구름이 사라지는 것을 볼 수 있습니다.
 
 
 

그래서 제가 맡은 임무는 번개로 완성된 구름의 외관을 디자인하고 구름의 내부를 디자인하는 것이었으며, 가장 중요한 것은 단순한 구름 동굴이 아닌 그 이상의 무언가를 만드는 것이었습니다.  마지막으로 플레이어가 새를 죽이면 구름이 사라지도록 만드는 것이었습니다.

 
 

포비든 웨스트의 슈퍼스톰처럼 거대한 것이 아니라 고도로 국지적인 뇌우처럼 보이기를 원했습니다. 
그렇게 작은 폭풍은 어떤 모습일까요? 

 
 

이는 바닥에 물렁물렁한 소재의 스커트가 있는 서 있는 구름과 유사한 유체 시뮬레이션으로 구름을 만들려는 초기 시도였습니다.

 
 

다음은 포비든 웨스트의 슈퍼스톰 글로우 코드에서 제공한 내부 글로우를 추가한 게임 내 모습입니다.
이 결과 두 가지 측면에서 폭풍 구름의 개발 과정이 바뀌었습니다.
첫째, 우리가 만들고자 하는 조우를 구현하기에는 내부 구조물이 너무 작다는 것을 깨달았습니다. 
 
 
 

그래서 자연스럽게 규모를 확장하고 프랭클라우드를 사용했습니다. 다음은 최종 모델입니다. 

 

둘째, 불빛이 밑면에 구멍이 있음을 암시하는 방식에 주목하세요. 내부 조명을 사용하여 개구부를 드러낼 수 있겠다는 생각이 들었습니다.

 
 
 

그래서 지상에서 조명이 방전될 때마다 내부에 불이 켜지고 내부 구조물이 드러나도록 했습니다.  또한 내부 플래시를 추가하여 어떤 종류의 건물이 쌓여 있음을 암시했습니다. 
 
 
 

호라이즌 포비든 웨스트의 현지 날씨 시스템을 사용하여 구름 아래에 국지적인 비바람을 만들었습니다. 
 
 

다음은 조금 더 멀리서 바라본 외관의 완성된 결과물입니다.
구름 안에서는 플레이어가 다른 구름에서 모델링했던 동굴이 아닌, 인간에게는 기괴하지만 폭풍새에게는 아늑한, 예상치 못한 무언가를 만나게 하고 싶었습니다. 플레이어가 이 안에서 무슨 일이 벌어지는지 보고 '이상하고 무섭지만 기능적'이라고 생각할 수 있어야 합니다. 

 

스톰버드가 부자연스러운 폭풍을 지속시키는 데 사용하는 에너지의 중심점이 있다는 것은 당연한 일이었습니다.
그리고 스톰버드를 죽이면 그 구심점과 구름은 사라질 것입니다.  
제가 '에그'라고 이름 붙인 것을 만나보세요.
물론 이 작은 전기 호는 니콜라 테슬라에게서 영감을 받았습니다. 
결국 충분한 전하가 축적되면 '에그' 아래의 구멍을 통해 방전되는 것을 볼 수 있고, 이 에그는 분명한 기능을 갖게 됩니다.
구현 측면에서 슈퍼스톰 시스템의 라이팅 기술을 복셀 클라우드에 맞게 조정한 또 다른 곳입니다.


 

내부 공간은 아틀라스 툴을 사용하여 동굴을 잘라내고 알을 만들었습니다.  폭풍새가 숨어서 몰래 공격할 수 있는 주머니를 많이 추가했습니다. 

 
 

또한 빛이 들어와 시간대별로 공간이 다르게 보이도록 충분한 개구부를 추가했는데, 이는 결국 오픈 월드 게임이기 때문입니다. 
모든 요소가 준비된 후 엘리야는 만남을 구현했고 오디오는 멋진 음향 효과를 제공했습니다.  


 

볼류메트릭 클라우드 렌더링에 대한 새로운 접근 방식을 채택하고 클라우드가 월드에서 만지고 환경을 구축할 수 있는 물리적 사물로 간주하기 시작했으며, 보이는 모든 것을 게임 스크립트에 연결할 수 있었기 때문에 다른 팀과 실제로 더 많은 작업을 할 수 있었고 플레이어에게 단순히 구름 위를 날아다니는 재미 외에도 진정으로 독특한 경험을 제공할 수 있었죠. 이런 게임플레이의 시작에 불과한 것 같아 매우 흥미롭습니다.

 
 

이제 차트에 두 개의 행을 추가해 보겠습니다. 복셀 기반 클라우드는 퀘스트 디자인에 사용될 수 있을 뿐만 아니라 여러 가지 새로운 방식으로 성장할 수 있는 잠재력이 있습니다. 
 
 
 

축하하는 의미에서 우리가 직접 디자인한 DLC를 준비했습니다. 게임에서 클라우드의 해상도를 높이기 위해 사용한 3D 노이즈의 TGA 및 VDB와 함께 두 개의 복셀 클라우드 NVDF를 TGA 슬라이스 및 VDB 파일 형태로 패키지화했습니다. 또한 이 노이즈에 대한 제너레이터도 후디니 디지털 에셋 형태로 포함했습니다. 이 외에도 프레젠테이션이 온라인에 공개되면 프로덕션에 사용된 실제 코드와 일부 주제에 대한 추가 설명이 포함된 몇 가지 보너스 슬라이드를 찾을 수 있습니다.   
이 링크를 지금 확인하지 못하신다면 몇 분 후에 트위터나 X 또는 뭐라고 부르든 간에 트윗으로 알려드리겠습니다.  

 

실시간 오픈 월드 게임에서 몰입형 복셀 기반 클라우드를 구현할 수 있습니다. 우리는 이 기술이 적용된 게임을 출시했으며, 우리의 방식과 광기가 여러분이 원래 생각했던 것과는 다를 수 있지만, 우리의 접근 방식을 통해 여러분도 이제 고성능 복셀 기반 클라우드로 작업을 시작할 수 있기를 바랍니다. 

 

이 강연에서 언급했던 개발팀에게 감사의 말씀을 전하고 싶습니다. 이런 일을 해내려면 팀이 필요합니다.  
또한 이 프레젠테이션을 준비하는 동안 저를 참아준 팀인 Atmospherics에게도 감사의 말을 전하고 싶습니다. 특히 오늘 보신 영상 중 많은 부분을 촬영해준 브라이언 아담스에게 감사드립니다.
또한 FX 부서, 스톰버드 팀, 아트 디렉션 및 기술 팀도 마찬가지입니다.
무엇보다도 제 가족인 로사, 에이단, 리암, 아멜리아에게 고맙다는 말을 전하고 싶습니다.


고맙습니다.