원문 : 如何在虚幻引擎中制作3A级交互雪地(Deformable Snow)
by : Text丨kenny Liu (Tencent Interactive Entertainment Technology Art)
Introduction
인터렉션(상호작용) 가능한 눈(snow) 효과는 눈이 있는 씬의 형태(shape) 표현에 필수적이며, RT(Render Texture. 이하 RT로 표기)를 사용하면 다른 많은 유형의 씬효과에도 유연하게 적용할 수 있습니다. 이 글에서는 언리얼 엔진을 기반으로 한 주류 트리플A 게임을 언급하고 0에서 1까지 상호 작용이 가능한 눈 제작 방법을 소개합니다.
이 글에서 제작된 인터랙티브 눈을 제외하고 샘플 비디오에 등장하는 아트 에셋은 모두 Unreal Engine Marketplace에서 가져온 것입니다.
- 설경 : Low Poly Snow Forest/Dokyo : https://www.unrealengine.com/marketplace/ko/product/low-poly-snow-forest
- 라이딩 시스템: 고급 라이딩 이동 시스템/CT 게임 : https://www.unrealengine.com/marketplace/ko/product/advanced-riding-locomotion-system
- 말과 애니메이션/MarberS Animations : https://www.unrealengine.com/marketplace/en-US/product/horse-animset
- 눈과 상호작용하는 다양한 물체: 식료품점 소품 컬렉션/JessyStorm의 asset : https://www.unrealengine.com/marketplace/en-US/product/grocery-store-props-collection
1. Deformable Snow의 일반적인 사례 소개
1) Introduction to RT and its application in the game
언리얼 엔진+에서 RT는 Render Target의 약자이며, 그 본질은 동적으로(하지만 동시에는 아님) 읽고 쓸 수 있는 2D 텍스처입니다. 텍스처는 색상 변경, 높이, 깊이, 마스크, 벡터 방향 등과 같은 많은 정보를 저장하는 데 사용할 수 있습니다. 바로 동적으로 읽고 쓸 수 있는 특성 때문에 게임에서 대부분의 3차원 장면의 상호 작용 정보를 추상화하고 2차원 평면으로 압축하여 처리 및 운영할 수 있으므로 실시간으로 상호 작용할 수 있는 일부 환경 효과를 만드는 데 특히 적합합니다. 이러한 종류의 정보와 운영의 추상화 및 단순화는 실시간 렌더링에 정확히 필요한 것입니다.
잘 알려진 말이 있습니다. "하나의 RT로 해결할 수 없는 장면 효과는 없습니다. 있다면 두 가지입니다."
이 기사에서 설명한 대화형 눈 외에도 다른 일반적인 RT 기술이 사용됩니다. + 생성된 실시간 장면 효과에는 대화형 진흙, 초원 및 모래, 식물 바람 움직임, 수면 변동 및 잔물결, 수역 상호 작용, 동적 풍력 발전소 등이 포함됩니다. 1
일부 3A 게임에서 RT를 사용하여 얻은 효과 : [Video content selected from “ The Last Survivor" "And "God of War”]
Advances in real time rendering in game, part 2(ACM Siggraph 2019) 2
2) Interact with RT in the snow
핵심은 RT를 사용하여 눈 위의 캐릭터나 물체의 이동 궤적을 기록한 다음 궤적 정보를 높이 정보로 변환하는 것입니다(예를 들어 캐릭터가 밟은 영역의 눈은 무너져 높이가 낮고 밟히지 않은 영역은 원래 높이와 같음). 그런 다음 지면 머티리얼이 이 RT를 샘플링하여 기록된 높이 정보를 얻고 높이 정보가 있는 높이가 변경된 영역은 표면을 세분화하여 정점 수를 늘린 다음(필요한 경우) 해당 부분의 정점 위치를 높이 정보에 따라 이동하여 지나가는 눈 영역을 실시간으로 변형하므로 "변형 가능한 눈(Deformable Snow)"이라고 합니다. 마지막으로 다양한 후속 처리 및 렌더링 효과를 중첩하여 자연스럽고 실제적이며 부드러운 상호작용이 가능한 눈 효과를 얻습니다!
따라서 이 텍스처를 그리는 방법이 핵심입니다. 이미지 비유는 RT가 보드고 플레이어의 발자국은 붓입니다. 플레이어가 눈 위를 움직일 때 발자국은 붓처럼 눈 RT에 끊임없이 그려지고, 끊임없이 그려진 발자국은 눈 위를 밟는 궤적을 형성합니다.
하지만 RT의 크기와 정확도는 제한되어 있고, 장면의 광대한 눈을 덮기에 충분하지 않으므로 RT가 플레이어 캐릭터와 함께 움직이게 합니다. 따라서 플레이어 주변의 눈은 항상 상호 작용하고 궤적을 기록할 수 있으며, RT 범위를 벗어난 궤적 부분은 점차 흐릿해지고 사라집니다. 이런 식으로 플레이어가 상호 작용할 수 있는 영역은 무한히 커집니다.
2. Infrastructure
위에서 언급했듯이, 우리는 RT를 핵심으로 하는 시스템을 구축하기 위한 솔루션 세트가 필요합니다. 이 시스템의 인프라는 세 부분으로 구성됩니다. 발자국을 감지하는 구성 요소, 핵심 작업과 계산을 수행하는 관리자, 세 개의 RT.
1) 구조 개요(Structure overview)
읽기와 개요를 쉽게 하기 위해, 제작 부분에 관련된 문서의 파일 구조, 기능 및 관련 챕터가 아래에 나열되어 있습니다.
관련 링크
DeformableSnow
Blueprints
- BP_TrailComponent : 광선 추적을 사용하여 지형과 충돌하는 발자국 정보를 가져오는 컴포넌트, 일반적으로 캐릭터 발자국에 사용됨.
- BP_TrailComponent_Capture : 광선 추적을 사용하여 발자국 위치를 기록하고 RT에 현재 정보를 저장하는 컴포넌트.
- BP_SharedSceneCapture :모든 BP_TrailComponent_Capture의 물리적 정보를 공유로 캡처하고, BP_TrailsManager에서 통합 관리.
- BP_TrailsManager : 전체 시스템을 최적화하여 업데이트, 한 장면에 하나만 존재.
- RT_Trails :가장 중요한 RT로, 발자국 모양 정보를 기록함.
- RT_Current : 발자국이 새로 만들어질 때, 현재 지형의 정보를 기록함.
- RT_History : 최초 발자국 생성 시, 이전 발자국의 RT_Trails 정보를 저장함.
- RT_Prints : 발자국 깊이를 기록하는 히스토리 RT, 크기는 작음.
- S_PrintsCapture : Manager에 발자국 데이터를 전달하는 구조체.
Materials
- M_TrailDrawer : RT_Current에 발자국을 그리는 함수.
- M_CaptureDrawer : RT_Current에 발자국 흔적을 그리는 함수.
- M_HistoryMerge : RT_Trails에서 RT_History와 RT_Current를 합성함.
- M_HistoryCopy : RT_Trails를 RT_History로 복사함.
- M_TrailsOperation : RT_Trails에서 노이즈 등 발자국을 수정하는 연산을 수행함. 이는 후처리 작업임.
- M_SnowGround : 눈 지면 재질.
- MPC_TrailsParams : 눈 재질에서 사용하는 몇 가지 기본 변수들의 집합.
- MF_Trails : 눈 재질에서 RT에 대해 평활화 및 조정 처리를 함 (계산/복사 함수 포함).
2) 컴포넌트 감지
캐릭터 컴포넌트의 발에 장착된 컴포넌트+, 캐릭터가 눈 위를 밟을 때 위치, 깊이, 반경 등의 일련의 데이터를 감지하는 데 사용됩니다. 반경, 회전 등 3
그림은 기본 감지 구성 요소가 수행하는 일부 작업을 보여줍니다. 여기서 저자는 구형 추적(Sphere trace)을 사용합니다. 단일 선 추적을 사용하지 않고 감지하려면 눈을 밟을 때 발과 눈 사이의 접촉이 점보다는 반구형 영역과 더 비슷하기 때문입니다. 그리고 위치 정보 외에도 반경, 깊이 및 기타 관련 정보도 필요합니다.
위는 감지 구성 요소의 다이어그램입니다. 구형 추적을 사용하여 후속 데이터 처리를 통해 눈 밟기 트랙의 반경(즉, 접촉 반경)을 쉽게 계산할 수 있습니다.
눈을 밟았다는 것을 감지하면 얻은 거리를 사용하여 얻을 수 있습니다.:
그래서,
3) Manager singleton
장면에는 관리자가 하나뿐입니다. 위 그림의 구성 요소 감지 마지막 단계는 획득한 풋프린트 관련 데이터를 관리자의 관련 데이터 세트에 전달하고 그리기를 기다리는 것입니다. 아래 그림에서 볼 수 있듯이 작성자는 데이터를 처리하여 해당 배열에 추가했습니다.
또한 관리자의 주요 기능은 다음을 포함합니다.
- 초기화가 시작될 때 RT와 관련된 머티리얼 인스턴스를 생성하고 머티리얼 파라미터를 설정합니다.
- 각 프레임에서 풋프린트 트랙 정보를 탐색하여 하나씩 그립니다.
- RT 그리기 프로세스를 제어하고 RT 등을 정리합니다.
4) The drawing logic of the three RT's
RT는 동적으로 읽고 쓸 수 있지만 읽는 동안 읽고 쓸 수 없으므로 RT 중 하나를 각 프레임을 그리는데 사용하고 나머지 두 개의 RT를 "left hands upside down" 사용하여 트랙 정보를 저장하고 그리기 프로세스를 완료합니다. 그 중 RT_Trails는 직접 수동으로 생성한 반면 다른 두 개의 RT는 초기화 중에 자동으로 생성되었으며 기본적으로 RT_Trails 사양 설정과 동일합니다. 4
사양 측면에서 크기는 실제 필요에 따라 달라질 수 있으며, 여기서는 2k로 설정됩니다. 형식은 16f 정확도로 설정해야 합니다. 1보다 큰 값을 그려야 하기 때문입니다.
(참고: 여기서 저자는 데모 효과를 보여주기 위해 2k 크기의 RT를 사용하여 반경 6000 내의 눈 영역을 기록했습니다. 실제 프로젝트 애플리케이션에서는 그렇게 큰 거리가 필요하지 않습니다. 1k RT를 사용할 수 있으며 영역 반경을 3000으로 줄일 수 있습니다. 세 개의 RT를 사용하기 위해 특정 최적화도 할 수 있습니다. 이 글에서는 심도 있게 논의하지 않고 주로 제작 방법을 명확히 설명합니다.)
아래에 표시된 것처럼 RT_Current 위에서 언급한 "브러시"와 동일하며 현재 프레임의 발자국만 그립니다.
RT_Trails는 가장 중요한 RT이며 눈 소재 쓰기에서 높이 정보를 읽을 때도 샘플링됩니다.
RT_History는 전송 스테이션 역할을 하며 RT_Trails의 내용을 직접 복사하므로 RT가 동시에 읽고 쓸 수 없어 발생하는 문제를 해결합니다.
- RT_Current
- 각 프레임을 그리기 전에 먼저 초기화(clear).
- 현재 프레임의 모든 발자국을 그린다.
- RT_Trails:
- 각 프레임을 그리기 전에 먼저 초기화(clear).
- 현재와 히스토리를 혼합.
- RT_History:
- 각 프레임마다 플레이어 이동에 따라 이동한 위치를 기준으로 샘플링.
- RT_Trails의 정보를 RT_History로 복사.
3. Draw the specific content of RT (Materials)
1) Draw a single footprint on RT_Current
먼저 Manager의 각 프레임 시작 부분에서 RT_Current를 지우고 현재 그려야 할 발자국이 있는지 확인한 다음 그리기 기능인 "Render Trails"를 입력합니다.
그리기 기능에서 이전에 감지 구성 요소에서 수집한 데이터를 기반으로 모든 현재 프레임의 발자국을 탐색하고 매개변수 값을 그리기 RT의 재료에 설정합니다. 그 전에는 구성 요소의 테스트 결과를 기반으로 단일 발자국의 위치, 회전, 깊이 및 반경을 계산했습니다. 여기서 이러한 데이터는 그리기 RT 블루프린트 노드에서 UE 설정에 사용됩니다. 이런 식으로 재료에서 단일 발자국 모양에 집중할 수 있습니다.
그림에서 발자국이 그려진 머터리얼이 표시되어 있으며, 중심에서 주변 영역으로 점진적으로 변하는 원을 구성합니다. Depth And Hardness는 Manager 블루프린트에서 전송한 데이터입니다. Blend 모드는 그리는 영역이 일치할 때 이전 발자국을 덮어쓰는 결함을 방지하기 위해 Translucent를 사용한다는 점에 유의해야 합니다.
머티리얼에서 발자국 크기를 추가로 조절하여(블루프린트에서 계산된 반지름에 따라 설정되었지만) 트랙 두께 변경을 더 명확하게 만들 수 있습니다. 따라서 아래 그림의 빨간색 와이어프레임에 있는 계산이 추가되었습니다. 그런데, 머티리얼 에디터에서 if 노드를 사용하는 것을 두려워할 필요가 없습니다. 엔진 내부에서는 여전히 효율적인 방식으로 대체됩니다.
수정의 효과는 다음과 같습니다. 풋프린트 크기는 깊이 파라메터에 따라 변경됩니다.
이외에도 곡선 활용, 샘플링 맵 등 발자국 모양을 구성하는 방법은 다양합니다. Naughty dog The Last of Us Part II의 스노우 트랙 제작 시 다음과 같은 모양을 구성하였습니다.
마찬가지로 UE 머티리얼 에디터에서는 곡선 수동 조정이나 사용자 정의 노드를 사용하여 곡선 기능을 직접 매핑하여 발자국 모양을 사용자 정의할 수 있습니다.
2) Merge RT_Current and RT_History
RT_History는 밟힌 궤적을 기록합니다. 기본적으로 플레이어의 움직임을 따르는 RT이므로 드로잉 자료에서 RT_History를 샘플링할 때 이전 프레임의 오프셋 값을 고려해야 합니다.
다음과 같이 Manager 블루프린트에서 간단한 계산을 수행하고 이를 머티리얼에 전달합니다.
눈이 점점 더 깊어질 것이므로 Max distance의 두 RT를 혼합하여 Max 값을 찾습니다. 가장자리의 페이드 처리도 여기에서 수행됩니다. 마찬가지로 가장자리의 그래디언트 처리도 눈 재료가 RT_Trails를 샘플링할 때 나중에 나타납니다.
3) RT_Trails를 RT_History에 복사
RT_Trails가 현재 프레임 RT_Current에 그려진 모든 발자국과 이전 프레임에 기록된 RT_History를 병합한 후, 현재 RT_Trails의 내용을 RT_History에 직접 복사하여 다음 프레임에서 병합할 때까지 보관합니다.
4. RT sampling offset algorithm correction(보정)
1) 흐릿한 궤적의 원인
위의 작업 후, 우리는 이미 이동 가능한 RT를 그리는 기본 기능을 가지고 있습니다. 하지만 캐릭터가 실시간으로 움직일 때 RT_Trails가 기록한 내용을 보았을 때, 무언가 잘못되었다는 것을 깨달았습니다.:
사진에서 보이는 것처럼 걷는 흔적이 수묵화처럼 흐려지는 것은 분명 우리가 원하는 효과는 아닐 것이다.
이 문제는 실제로 엔진이 RT를 샘플링할 때 필터 모드와 관련이 있습니다. 기본적으로 샘플링 RT에서 사용되는 필터링 모드는 선형 필터링(Bilinear)과 유사합니다. LearnOpenGL의 설명을 참조하세요.
그리고 샘플링 중에 인접한 픽셀의 색상 값이 섞여 있어도 왜 이렇게 흐릿하게 될까요? 이는 RT_Trails를 구성하는 과정에서 위에서 언급한 RT_History를 샘플링할 때 현재 프레임과 이전 프레임 사이의 오프셋 값을 고려할 때 문제의 핵심은 이 오프셋 값을 계산하는 데 있기 때문입니다. 두 프레임의 위치를 직접 빼면 이 결과는 언리얼 엔진의 1cm 단위 정확도에 기반하지만 RT의 픽셀이 반드시 UE의 1cm에 해당하는 것은 아닙니다. (설정 방법에 따라 다릅니다. 예를 들어 저자의 예에서 캐릭터 주변 반경 6000cm의 영역을 나타내기 위해 2k 크기의 RT를 사용했습니다. 그러면 RT의 픽셀은 아마도 5cm 이상의 거리에 해당할 것입니다.) 두 가지의 정확도 차이로 인해 오프셋된 후 샘플링 UV RT 픽셀의 정확도를 완전히 정렬할 수 없습니다.
캐릭터가 임의로 움직일 때 두 프레임 사이의 오프셋 값은 끊임없이 변하고, 정확도 문제로 인한 오프셋 UV의 작은 오류도 변합니다. 필터링 모드에서 두 프레임 사이의 오프셋 값은 끊임없이 변합니다. 효과의 작용 하에서 주변 픽셀과의 믹싱 결과도 끊임없이 변하고, 동시에 믹싱 결과의 각 변화는 RT_History에 누적되어 전체 궤적이 점점 더 흐릿해집니다!
2) Align the coordinates with the pixels
문제의 원인을 알게 되면 해결할 수 있습니다. 좌표의 정확도를 픽셀과 정렬하기만 하면 됩니다.
즉, 픽셀 정렬 위치를 설정하기만 하면 됩니다. 각 프레임의 시작 부분에서 정렬 작업을 수행하여(탐지 구성 요소에서 관리자에게 보낸 풋프린트 데이터를 처리할 때 포함) 시스템의 어느 곳에서나 사용되는 위치 좌표 데이터가 정렬되도록 합니다.
그중에서 Locatoin Calc Helper는 RT의 각 픽셀에 해당하는 거리 길이로, 처음에 초기화되어 계산됩니다. 카메라 위치는 플레이어 액터 위치 대신 여기에서 가져옵니다. 둘 다 괜찮습니다. 이 글에서는 구분하지 않습니다.
Truncate는 0으로 반올림하는 데 사용됩니다.
RT의 내용도 정상입니다.
이제 우리는 바닥에 이 RT를 그린 다음 먼저 간단한 UV 계산을 수행할 수 있습니다.
MaxDistence는 RT로 표현된 영역의 반경입니다. 위의 계산은 지형 자료의 중심점(0.5, 0.5)으로 currentLocation(캐릭터의 현재 위치)을 사용하여 0-1 범위의 UV 샘플링 영역을 구성합니다. UV 샘플링 영역
이 RT를 지형 자료에 그리면 오프닝 개략도의 효과가 나타납니다.
5. Shadow problem
그림자는 궤적의 시각적 효과를 개선하는 데 필수적입니다. 눈이 주로 흰색이기 때문에 그림자는 트랙 부분의 변형을 가장 직관적으로 반영할 수 있습니다.
이전 일련의 작업 후에 그림자 문제에 대한 논의를 시작하기 전에 먼저 기존 RT를 사용하여 터레인 머티리얼에 대한 몇 가지 간단한 음영 처리를 수행합니다. 기본 눈 PBR 맵은 RT_Trails를 마스크로 처리하고 기록된 정보를 사용합니다. 짓밟힌 트랙 영역에 다양한 세부 노멀을 추가하고 정점 변위(vertex displacement)작업 등을 수행하는 데 사용됩니다.
여기서 더 말씀드리자면, vertex displacement는 Deformable Snow에서 매우 중요한 부분입니다. 본질적으로 우리는 눈의 원래 모양(붕괴 또는 축적)을 변경해야 상호작용이 가능하고 이를 "Deformable Snow"라고 부를 수 있기 때문입니다. ".
일반적인 방법은 Tessellation + Displacement 이지만 UE 5.0 이후에는 표면 세분화 및 Displacement를 자체 VHM (Virtual Heightfield Mesh) 솔루션으로 대체했습니다. 하지만 최신 5.3에서는 재질의 Displacement가 다시 추가되어 구성방법이 달라졌습니다.
또한, 일부 트리플A 게임에서는 성능 균형을 고려하고 다양한 콘솔 플랫폼을 지원하기 위해 POM(Parallax Occlusion Mapping) 방안을 사용합니다. 예를 들어, Naughty Dog가 SIGGRAPH 2020에서 공유한 The Last of Us Part II의 눈 덮인 지역 제작 부분이 그 예입니다.
naughtydog.com/blog/naughty_dog_at_siggraph_2020)도 이 점을 언급했습니다. 하지만 POM 방식에도 가로줄무늬나 블로킹 오류 등 고유한 결함이 있습니다.
단순 음영 처리 효과(텍스처만 추가하여 추적 부분을 구분)는 효과가 매우 나쁘고, 세부 수준이 없으며, 궤적 변형이 명확하지 않습니다.:
여기서 저자는 지형의 캐스트 그림자를 껐습니다. 변위된(displaced) 정점에서 생성된 지오메트리 그림자는 정확도가 충분하지 않기 때문에 사용되지 않으며 고르지 않은 톱니 모양이 생성되고 표면의 테셀레이션이 아무리 조밀하더라도 RT의 픽셀에서 기록된 높이 정보는 정확하지 않습니다. 다음으로 머티리얼의 그림자를 수동으로 계산합니다.
1) Construct normals
ddx를 사용합니다. ddy 방법은 RT의 높이(height) 정보를 사용하여 노멀을 구성합니다. UE에서 엔진과 함께 제공되는 "NormalFromHeightmap" 함수를 직접 사용합니다.
여기서 주의해야 할 점은 격자 모양의 결과가 나오는 경우 하이트맵 UV 오프셋 값을 늘리면 됩니다. 즉, 노멀에 의해 생긴 그림자가 바깥쪽으로 확장되도록 하면 됩니다.
2) Fake cast shadow of parallel light
터레인의 그림자 효과를 끄고, 이제 머트리얼에서 수동으로 계산합니다.
위 사용자 정의 노드 "HeightmapToShadow"의 HLSL 코드:
float SnowHeight; float CurrentHeight; float LastHeight; float TraceHeight; float Distance = 0; float Shadow = 1; int i=1; Steps = min(max(Steps, 1), 32); SnowHeight = Texture2DSample(Tex, TexSampler, Unit).r; CurrentHeight = SnowHeight * SnowThickness; TraceHeight = CurrentHeight; float StepHeight = max(((2 - SnowHeight) * SnowThickness / Steps), 0.25); float DistanceOffset = StepHeight / TraceDir.z; float2 WorldOffset = TraceDir.xy * DistanceOffset; float2 Offset = WorldOffset / SnowSize; for (i = 1; i < Steps; i++) { LastHeight = CurrentHeight; SnowHeight = Texture2DSample(Tex, TexSampler, Unit + i * Offset).r; CurrentHeight = SnowHeight * SnowThickness; TraceHeight += StepHeight; Distance += DistanceOffset; Shadow = min(Shadow,(TraceHeight - CurrentHeight) / (Distance * SoftDistance)); if(TraceHeight < CurrentHeight) { Shadow=0; return Shadow; } } return Shadow; |
위와 같이 Direcitonal Light의 방향을 얻은 후 Custom Node에서 Ray-Marching과 같은 방법을 사용하여 그림자 영역을 계산합니다(엔진 자체 기능인 Parallax Occulusion Mapping 구현 참조). 성능을 향상시키기 위해 전환 영역의 그림자.
엔진과 함께 제공되는 TAA 기능은 여기 TAA 기능을 수정하여 수직으로 동적으로 변환할 수 있고 샘플링 단계 수가 교란되며 Ray-marching 또는 POM에서 흔히 볼 수 있는 수평 및 수평 줄무늬 영향의 아티팩트가 어느 정도 제거되었습니다. 5
POM의 실제 적용 및 관련 문제 해결에 관해 아래 링크에서 게시한 매우 포괄적이고 실용적인 일련의 튜토리얼을 살펴볼 수 있습니다.
Parallax Occlusion Mapping Deep Dive EP: 2 - Pixel Depth Offset and Stair Stepping
그림자 결과를 계산한 후 이를 사용하여 법선 방향을 방향성 조명의 반대 방향으로 오프셋하여 엔진 조명과 일치하는 그림자 결과를 얻습니다.
Cast Shadow 추가 전과 후의 효과 비교:
위 두 가지 그림자 처리 방법은 SIGGRAPH 2020에서 Naughty Dog가 공유한 Waylon Brinck와 Steven Tang의 The Technical Art of The Last of Us Part II의 Snow에 관한 부분에서 가져온 것입니다.
6. Snow accumulation on both sides of the track(트랙 양쪽에 쌓인눈)
그림에서 보듯이, 이것은 우리의 이상적인 궤적의 횡단면 모양입니다. 양쪽에 쌓인 융기된 모서리(또는 트레일 고도)는 전체 눈 상호작용 궤적의 성능 개선에 매우 중요합니다. 인터넷의 많은 사람들이 상호작용 눈 효과를 구현하려고 시도했을 때, 그들은 이 부분을 무시했습니다.
1) 스태킹 실현
UE에서 눈 축적의 핵심은 "SpiralBlur"를 사용하는 것입니다. 텍스처" 이 기능은 궤적 RT를 흐리게 합니다.
축적 면적을 얻은 후에도 일부 형태 조정이나 평활화 및 미화 처리가 필요합니다.(After obtaining the accumulation area, some shape adjustment or smoothing and beautification treatment is also required)
그림에서 보듯이, 사인 함수 sin을 사용하여 퍼지 결과에 간단한 처리를 수행한 후, 원래 TrailsBuffer 1-x 결과에 추가하고 최종 결과로 RT에 기록합니다. 이런 식으로, RT에서 변경되지 않은 원래 눈 부분의 값은 1이고, 트랙 양쪽에 쌓이고 올라간 부분의 값은 1보다 크고, 트랙의 계단식 붕괴 부분의 값은 1보다 작습니다. 이것의 장점은 RT에 의해 기록된 값의 크기가 이제 눈의 두께에 직접 대응한다는 것입니다.
또한, 세부 사항을 풍부하게 하기 위해 Noise가 추가되었습니다. Noise의 샘플링 방법은 TexCoord TexCoord(플레이어의 움직임을 따름)를 직접 사용할 수 없지만 들어오는 현재 위치와 협력합니다. 매개변수가 세계 좌표를 재구성할 때까지 기다린 다음 UV 샘플링으로 전환합니다.
2) Integrate into the existing process
RT는 동시에 읽고 쓸 수 없습니다. 누적된 계산 결과를 저장하기 위해 다른 RT를 추가하시겠습니까? 당연히 아닙니다. RT_Trails를 RT_History에 복사한 후(이때 RT_Trails와 RT_History의 내용은 정확히 동일함), 위 그림의 왼쪽에 있는 TrailsBuffer 매개변수에 RT_History를 필요한 트랙 정보로 전달한 다음, 위 계산 결과를 RT_Trails에 플로팅할 수 있습니다. 계산 결과는 RT_History에 그려지지 않습니다. 각 프레임을 사용하기 전에 RT_History가 지워지지 않아 SpiralBlur 효과가 중첩되고, 터레인 머티리얼에서 RT_Trails를 샘플링하는 것이 설계에 더 부합하기 때문입니다.
이전 기사의 RT 드로잉 프로세스는 이렇게 되었고, 빨간색 부분을 추가하고 빨간색으로 표시된 부분에 "드로잉 에지 누적 계산 결과"를 추가했습니다.:
"Only Textures->normal build->add fake cast shadow->pile up on both side of track(트랙 양쪽에 쌓임)" shading process
7. Let the accumulated snow fall fluffy
이것이 바로 "Black Myth: Wukong"입니다. 예고편에 나온 눈 효과는 매우 눈길을 끄는 특징적인 포인트를 보여주었습니다.
그림에서 보듯이 눈이 쌓인 후에는 눈에 띄는 떨어지는 상태가 나타나 사람들에게 푹신푹신한 느낌을 주는데, 이는 매우 자연스럽고 사실적입니다. 그렇다면 제가 직접 만든 눈 효과에 이 기능을 어떻게 추가할까요?
1) Production method
RT_History로 시작하기로 했습니다. 이는 완전한 프로세스이며 기록된 역사적 정보(historical information : 누적정보)가 필요하기 때문입니다. 각 프레임에 대해 명확하고 결과만 보는 RT_Trails는 분명히 요구 사항을 충족하지 못합니다.
그림과 같이 빨간색 상자로 표시된 단계에서 수정해주세요.
HistoryBuffer와 CurrentBuffer 혼합 RT 드로잉 자료에서 빨간색 프레임 부분이 추가되었습니다. 원래 궤적 혼합의 계산 결과는 변경되지 않고 R 채널에 남아 있습니다. 떨어지는 눈의 높이 정보의 계산 결과는 G 채널의 RT_Histroy에 저장됩니다.
계산 아이디어 : 현재 프레임의 궤적 혼합 결과를 사용하여 이전 프레임의 궤적 혼합 결과를 빼고 얻은 차이는 임계값 0.1로 판단합니다. 임계값보다 크면 효과적인 눈 밟기 영역으로 계산되고 현재 프레임의 눈 밟기 영역의 마스크가 구성됩니다. 그런 다음 CurrentBuffer의 값을 곱합니다. 값은 현재 프레임의 효과적인 눈 밟기 영역의 높이 정보를 얻습니다.
여기서 CurrentBuffer를 직접 사용하지 않는 주된 이유는 캐릭터가 가만히 서 있을 때 떨어지는 눈의 높이 정보를 항상 새로 고침해서는 안 되기 때문입니다. 이때, 눈 위를 밟은 것은 무효로 간주되어야 하며, 푹신한 낙하 효과는 없어야 합니다.
낙하 높이 부분의 계산은 매우 간단합니다. G 채널에 기록된 이전 프레임의 높이 값에서 deltaTime에 따라 직접 뺍니다. 현재 프레임이 낙하해야 하는 계산된 높이:
FallingTime은 Manager가 전달한 사용자 정의 매개변수로, 폭신한 눈이 내리는 과정의 지속 시간입니다. 그런 다음 얻은 결과는 현재 프레임의 높이를 사용하여 최대로 사용됩니다. 이러한 방식으로 눈이 내리는 과정에서 다시 밀어올릴 수 있습니다.
현재 RT_Trails의 G 채널 결과는 다음과 같습니다. 발자국 주변에 쌓이고 떨어지는 눈의 면적과 높이 변화를 기록합니다.
푹신한 낙하 효과도 스태킹 효과의 일부이므로 위의 스태킹 효과의 도면 자료로 돌아가서 이 효과 부분의 계산을 추가하세요. 빨간색 상자는 수정된 부분입니다.:
After zooming in↓:
그림과 같이 궤적 양쪽에 쌓인 눈을 기준으로 G 채널에 의해 제어되는 푹신한 폭포의 높이 값이 추가되며 이는 "BoostHeightScale" 매개 변수에 의해 제어됩니다.
그런데, 이것은 쌓인 페인트 재료의 최종 버전입니다. 여기서는 다른 처리 없이 TrailsBuffer의 흐릿한 값만 최종 결과의 G 채널에 유지되며, 이는 후속 재질 맵 혼합을 위한 마스크로 사용됩니다.
“fluffy falling” 6
효과 스위치를 비교하면서, 말발굽과 캐릭터 주변의 눈의 역학을 주의 깊게 관찰하세요:
2) 보너스: "The Trail fades over Time" 효과(시간에 따라 트레일 채우기)
HistoryBuffer의 작동에서, 당신은 또한 더 실용적인 효과 기능인 "트랙 페이딩"을 간단히 완성할 수 있습니다.
https://pic4.zhimg.com/v2-47049e0894eecde0bbd5392745f4ec87_b.webp
그림에서 보듯이 밟은 트랙은 점차 회복되고 눈이 내리는 장면이 필요할 수 있습니다. 이를 위해 HistoryBuffer의 샘플링 결과를 위의 재료 수정에서 (0, 1) 간격의 값으로 곱하기만 하면 기록된 높이 정보가 프레임마다 매끄럽게 감소하여 0에 가까워집니다.:
파라메터는 "Preservation"이라는 이름이 지정되고 관리자가 각 프레임에 대해 전달합니다. 계산은 다음과 같습니다.
사용자 정의 파라메터인 Trails Attenuation을 사용하여 감쇠 강도를 설정하면 완료됩니다.
8. 다양한 모양의 사물의 상호작용을 만나보세요
위에서 언급한 궤적의 모양과 발자국은 모두 RT_Current의 드로잉 머티리얼에서 계산되지만 눈 상호 작용에서는 쓰러진 캐릭터, 말, 장면의 다양한 개체 및 소품과 같은 불규칙한 모양의 개체와 상호 작용하는 경우가 많습니다. 도면 정보를 실시간으로 캡처하려면 장면 캡처를 사용해야 합니다.
그림에서 보듯이, "배트맨: 아캄 나이트"에서 눈이 상호작용하는 장면은 모두 작고 평평한 지붕이기 때문에, 전체 눈 평면의 높이 정보는 RT로 직접 기록됩니다. 하지만 우리는 이렇게 하지 않습니다. 이 방법은 터레인에는 적용되지 않습니다.
위의 발자국 감지 구성 요소와 마찬가지로, 우리는 여전히 구성 요소의 형태를 사용하여 Scene capture에 필요한 관련 데이터 정보를 처리 및 그리기를 위해 매니저에게 전달합니다. 여기서 씬캡처는 아래에서 위로 수직으로 직교뷰(Othergraphic view)를 취합니다. 직교 뷰 깊이(depth)(height로 변환 가능) 정보, 크기는 캡처된 객체 주변의 작은 영역만 커버하며, 일반적으로 128*128 크기의 RT만 있으면 기록을 저장할 수 있습니다. 다음 도식도에 따르면 :
1) Component detection
여기서 Sphere Trace는 발자국 감지처럼 사용되지 않고 Line Trace를 직접 사용합니다. 지면과의 접촉 반경을 감지할 필요가 없고 감지된 객체 바로 아래의 지면 위치만 얻을 수 있기 때문입니다. 모델의 불완전한 엇갈린 촬영을 방지하기 위해 들어오는 Manager 등록 데이터 Location(즉, 레이-ray와 지면 사이의 hit point 지점 위치)의 위치를 길이 단위 100만큼 아래로 이동했습니다. 직교 뷰에서 캡처했기 때문에 영향이 없습니다. 100 unit의 차이는 나중에 머티리얼을 변환할 때 메워집니다.
2) Share the same SceneCapture and RT
성능상의 이유로 추적 및 저장을 위해 캡처된 각 객체에 대해 SceneCapture와 RT를 생성하지 않고 사본을 하나만 생성합니다. 관리자가 그릴 때 각 프레임은 각 객체를 횡단하여 캡처하고 기록할 해당 위치로 호출한 다음 관리자가 특정 높이를 그립니다. 정보의 결과는 drawing board(RT 시스템)에 있습니다.
SceneComponent Blueprint Class를 구축하고 Scene Capture Component2D를 추가하고 Projection Type을 Orthographic으로 설계했습니다.
RT가 월드 좌표계의 축과 일치하는지 확인하세요.
캡처 카메라를 만든 후 작성자는 캡처된 RT의 xy 축을 나타내기 위해 두 개의 빨간색과 녹색 화살표를 추가했습니다. 이는 RT를 샘플링할 때 uv 방향(왼쪽 위에서 오른쪽 아래로)이기도 합니다. 그런 다음 Y축을 중심으로 90도 회전하여 수직 위쪽으로 세워집니다. 아래 그림에서 볼 수 있듯이 이때 RT의 xy축은 월드 좌표의 xy 평면 축과 정확히 반대입니다. 왜냐하면 눈 재료가 RT를 샘플링할 때 월드 좌표의 xy 값은 uv로 변환됩니다. 샘플링을 위해서는 RT 캡처 축도 일치해야 합니다.
여기서 x와 y 스왑 연산을 수행하는 것은 편리하지 않으므로, 이후의 드로잉 자료에서 처리하겠습니다.
공유 RT를 호출하는 데 사용되는 함수 만들기 :
3) Easy to draw
그리기 기능(Drawing function)
발자국 트랙과 마찬가지로 그리기 기능을 만들고, RT_Current에 캡처된 콘텐츠를 각 프레임에 그립니다.
그 중, 데이터 관리를 용이하게 하고, 발자국 도면 관련 데이터를 구분하기 위해 RT 캡처 임프린트 관련 데이터를 모두 저장하는 구조를 만들었습니다. :
RT_Current의 자료에 캡처 그리기
Blend Mode는 적용 범위가 겹치는 것을 방지하기 위해 여전히 반투명입니다. CaptureDepth는 캡처한 콘텐츠를 저장하고 깊이 정보를 기록하는 RT입니다. 샘플링 uv의 xy 값이 반전되어 위에서 언급한 캡처된 RT의 축 방향이 일관되지 않는 문제를 해결합니다. 100을 빼면 실제로 감지 대상의 지면 위치 바로 아래 100cm에서 캡처했기 때문에 감지 구성 요소의 차이가 보완됩니다. 눈의 두께로 나눈 값이 눈의 재질과 일치하여 최종적으로 높이 정보로 변환됩니다.
마지막으로, 캡처 결과를 저장하기 위해 이 작은 RT를 만드는 것을 잊지 마세요. 여기에서 사양 설정에 주의해야 합니다. 크기는 128x128로 더 작게 설정되고 렌더 타겟 형식은 16 정밀도를 사용해야 합니다. 왜냐하면 우리가 기록하는 것은 1보다 큰 값인 깊이 정보이기 때문입니다.
마지막으로 RT Capture를 이용해 그려야 할 객체나 객체에 컴포넌트를 추가하면 사용할 수 있습니다.:
9. RT performance data reference
RT이름 | 사용여부 | RT수 | 크기 | 정확성 | 채널수 | numbers of draws per frame |
RT_Current | 반드시 생성 | 1 | 1k/2k | 16f | single channel | feet to be drawn print run |
RT_Trails | 반드시 생성 | 1 | 1k/2k | 16f | "fluffy drop" 효과는 단일 채널에서는 열지 말고, 두 채널에서는 열어두세요. | edge stacking(raise edge)을 활성화하려면 2개가 필요합니다. 1개가 아닙니다. |
RT_History | 반드시 생성 | 1 | 1k/2k | 16f | "fluffy drop" 효과는 단일 채널에서는 열지 말고, 두 채널에서는 열어두세요. | 1 |
RT_Prints | 실시간으로 캡처하여 상호작용적인 체형을 사용해야 합니다. | 대부분의 경우, 하나면 충분하지만, 극단적인 감정을 포착하려면 객체의 모양이 매우 커져야 하며 더 큰 크기의 RT가 필요합니다. | 일반적으로 128x128보다 작습니다 | 16f | single channel | 실시간으로 캡처해야 하는 오브텍트 수 |
10. Conclusion
이 곳을 계속 찾아주셔서 감사합니다! 저는 오랫동안 공유할 글을 쓰지 않았습니다. 한편으로는 충분히 자세하지 않아서 놓치는 게 걱정입니다. 결국, 제가 실제로 R&D가 필요할 때는 늙은 엄마의 섬세한 공유와 안내를 받기를 바라지만, 다른 한편으로는 같은 분야가 아닌 친구들이 자세히 공유하고 안내할 수 있도록 최대한 간결하고 이해하기 쉽게 작성하기를 바랍니다. 또한 장애물 없이 전반적인 내용을 빠르게 살펴볼 수 있고 지루하지 않습니다.
전체 제작 과정에 복잡하고 심오한 어려움은 없으며, 다른 많은 기술 예술 문제와 마찬가지로 기능 계층, 분해 계층, 그리고 점차적으로 쌓여서 실현될 뿐입니다. 반대로 효과를 조정하는 데 소요되는 시간과 에너지는 전체 제작 과정의 약 1/3을 차지하는데, 이는 아마도 TA가 종종 무시하는 작업 내용의 일부일 것입니다(이전의 자아...). 저는 이전에 이런 문장을 본 적이 있습니다: "많은 TA가 기술에 대해 끝없이 이야기하지만, 그 효과는 항상 위축되고 둔감합니다." 저는 여러분 모두를 격려합니다!
References:
- Naughty Dog. "The Technical Art of The Last of Us Part II." SIGGRAPH 2020. 7
- Square Enix. "Rendering Techniques in Rise of the Tomb Raider." SIGGRAPH 2015.
- Santa Monica Studio. "Reinventing the Wheel for Snow Rendering." GDC 2023. 8
- WB Games Montreal. "Deformable Snow Rendering in Batman: Arkham Origins." GDC 2014. 9
- "GPU Pro 7: Advanced Rendering Techniques." I. 1. "Deferred Snow Deformation in Rise of the Tobm Raider."
- [UOD2022] Earth Drawing Board: Interactive Snow in the Big World | Sun Boheng (LingFeng)
- 비슷한 구현 사례를 unity hack week에서 대혁님이랑 재호님이 공개한 포스팅이 있다. https://blog.naver.com/daehuck/222413632188 RT를 사용해 grass의 vertex offset을 제어하는 개념으로 사용한 것인데 이 내용과 비슷한 케이스라 생각하면 이해가 쉽다 /역자 주 [본문으로]
- https://youtu.be/9-HTvoBi0Iw?si=PI-M4mHeXDNulDPf&t=699 [본문으로]
- https://dev.epicgames.com/documentation/ko-kr/unreal-engine/traces-in-unreal-engine---overview [본문으로]
- 프로그래밍이나 그래픽 작업에서 흔히 사용되는 교대로 역할을 수행하는 방식을 비유적으로 표현한 것으로 보임. 즉, 두 개의 RT(렌더 타겟)가 번갈아 가면서 작업을 수행하는 구조를 설명하는 것으로, 이런 방식은 더블 버퍼링(double buffering) 혹은 트리플 버퍼링(triple buffering)과 유사한 개념으로, 한 프레임 동안 한 RT는 작업을 수행하고, 나머지 RT는 정보를 저장하는 역할을 한다. 이를 다음 프레임에서는 역할을 바꾸어 진행하는 방식을 말한다/역자 주 [본문으로]
- TAA https://developer.unigine.com/en/docs/latest/principles/render/antialiasing/taa?rlang=cpp [본문으로]
일반적으로 입자 기반의 부드럽고 가벼운 물체나 입자가 떨어지는 장면을 시각적으로 표현하는 것을 의미한다. 크게 세가지 정도로 활용된다- 부드러운 입자 시뮬레이션 : 솜, 눈송이, 깃털, 먼지, 꽃잎 등과 같은 가볍고 부드러운 물체들이 떨어지는 것을 자연스럽게 표현할 때, 이 효과는 입자의 질감과 낙하 속도를 섬세하게 조절하여 가벼운 물체가 바람에 따라 천천히 떨어지거나 부드럽게 움직이는 느낌을 구현한다.
- 물리적 움직임 구현 : 중력, 공기 저항, 난류 등 물리적 요소들을 고려해 입자의 경로를 설정하고, 입자가 천천히 떨어지거나 불규칙하게 움직이는 모습을 구현한다.
- 부드럽고 자연스러운 시각 효과 : 시각적으로 부드러운 느낌을 주기 위해 사용되며, 떨어지는 물체의 각도, 회전, 속도 등이 자연스럽게 변하도록 구현.
- https://s3.amazonaws.com/nd.images/research/2020_siggraph/NaughtyDog_TechArt_TLOU2-final.pptx [본문으로]
- https://sms.playstation.com/media/documents/GOWR_Paolo_Surricchio_ReinventingTheWheel_GDC23.pdf [본문으로]
- https://colinbarrebrisebois.com/wp-content/uploads/2022/06/gdc2014-deformable_snow_rendering.pdf [본문으로]
'Technical Report > Unreal' 카테고리의 다른 글
[번역]Coding Standard (0) | 2018.02.22 |
---|---|
[번역]Graphics programming 개요 (0) | 2018.02.22 |
UE4 Lightmass 및 GI (0) | 2016.01.13 |
UE4 Viewport Setting (0) | 2015.12.14 |
UDK3 다채널 UV 제어 (0) | 2014.05.26 |