[KOR][30]Between-Tech-and-Art--The-Vegetation-of-

https://m.youtube.com/watch?v=wavnKZNSYqU&pp=ugUEEgJlbg%3D%3D

Frame at 5.86s

전해지기로는 신께서 아름다운 우리 세상을 칠일 만에 창조하시고, 일요일은 쉬셨다고 합니다.

Frame at 14.30s

Horizon의 세계를 구축하는 데 7년이 걸렸으며, 저희는 종종 일요일에도 작업해야 했습니다.

Frame at 22.78s

이곳에 있는 유일한 농담은 이것뿐이며, 나머지는 모두 기술적인 이야기입니다. 참석하신 모든 분들을 환영합니다. 자리가 꽉 차서 기쁩니다. 휴대폰 전원을 꺼주시길 부탁드립니다. 아직 하지 않으셨다면 부탁드립니다. 다시 한번 참석하신 모든 분들을 환영합니다. 제 소개를 하겠습니다. 저는 Gilbert Saunders이며, Guerrilla Games의 Principal Artist입니다. 다른 두 명의 아티스트와 함께 Horizon Zero Dawn의 vegetation 제작을 담당했습니다. 저는 2006년부터 Guerrilla Games에서 일하고 있습니다.

Frame at 68.44s

저는 Killzone 2에서는 environment artist로, Killzone 3에서는 shaded texture artist로 참여했습니다. 이 두 게임을 마친 후, 2011년 초에 스튜디오 내 작은 팀에 합류하여 나중에 Horizon Zero Dawn이 될 프로젝트의 테스트와 프로토타이핑 작업을 시작했습니다. Shadowfall에도 일부 참여했지만, 이 내용은 추후에 더 자세히 다룰 것입니다. 그리고 2014년부터는 Horizon Zero Dawn의 본격적인 개발에 집중했습니다. Horizon Zero Dawn을 플레이해보신 분이 얼마나 되시는지 손을 들어주시겠습니까? 좋습니다. 반갑군요. 게임을 플레이하실 때는 세상의 위험에 집중하느라 vegetation에 크게 신경 쓰지 않으셨을 수도 있습니다. 그래서 오늘 우리는 vegetation에 대해 이야기하고자 합니다. 먼저 간단히 소개부터 시작하겠습니다.

Frame at 127.21s

최종 결과물을 보여드리기 위한 클립입니다. 소리가 포함되어 있지 않으므로, 보통은

Frame at 138.07s

별도의 언급은 하지 않겠습니다.

Frame at 148.06s

이어서 조금 더 설명드리겠습니다.

Frame at 154.06s

알겠습니다.

Frame at 160.52s

오늘 여러분께 저희 게임에서 제작한 vegetation에 대해 말씀드리고자 이 자리에 모였습니다. 아마 여러분 중 대부분은 nature assets를 제작, simulate, render하는 과정에서 이와 같거나 유사한 challenge를 겪으셨을 것입니다.

Frame at 176.66s

현세대 하드웨어에서도 Nature asset은 여전히 정적입니다.

Frame at 180.78s

최선의 경우, 현실 세계를 짜증 날 정도로 단순화한 것입니다. 이것이 짜증 나는 것을 넘어 이 주제를 흥미롭게 만드는 이유이기도 합니다. '더 적은 것으로 더 많은 것을 만든다'는 철학이 여기에 분명히 적용됩니다. 화면에 지금 보이는 것들이 오늘 다룰 내용들입니다. 이들은 Decima 엔진에서 방대한 양의 Vegetation을 생성하고 렌더링하는 데 따르는 예술적, 기술적 도전 과제들을 대표합니다. 먼저, Decima 엔진과 렌더러에 대한 전반적인 정보와 역사를 말씀드린 후, 빠르게 주제로 넘어가겠습니다.

Frame at 229.67s

Decima 엔진 내에서 아티스트로서, 저희가 이 분야에서 완전히 독보적이지는 않겠지만, 저는 할 수 있습니다.

Frame at 234.80s

저희는 자체 셰이더(shader)를 제작하고 유지 관리합니다. 물론 이는 기술팀의 철저한 감독 하에 이루어집니다. 셰이더 제작은 Maya에서 Maya 자체 셰이더 노드와 그래픽 프로그래머가 제공하는 다수의 커스텀 셰이더 노드를 함께 사용하여 진행됩니다. 이러한 셰이더의 성능 프로파일링은 아티스트가 직접 담당하며, 역시 기술팀의 지도 아래 이루어집니다. 따라서 셰이더의 품질, 에셋, 그리고 작업 결과물의 성능에 대한 강한 주인의식을 느낍니다. 저희 Decima 엔진은 세 번째 셰이딩 방식을 사용하며, Horizon Zero Dawn은 PS4에서 1080p 30fps, Pro에서는 4K 해상도에서 실행되도록 목표했습니다.

Frame at 295.06s

Horizon Zero Dawn이라는 이름이 붙여지기 전, 초기 개발 단계에서 저희가 구상했던 새로운 IP에는 현재 화면에서 보시는 모든 요소들이 포함될 예정이었습니다.

Frame at 303.59s

이 새로운 IP에 대한 제안을 듣는 순간, 대부분의 저희는 이미 매료되었습니다. 특히 BBC 자연 다큐멘터리에서 영감을 받은 부분에 깊이 공감했습니다. 그리고 7년이 지난 지금, 이 자리에 서서 Horizon의 핵심적인 부분으로 자리 잡은 것에 대해 말씀드리게 되어 기쁩니다.

Horizon의 세계관을 천천히 구축하고 창조하는 동안, 저희 스튜디오의 다른 팀은 PS4의 대작인 Killzone Shadowfall 개발에 매진하고 있었습니다. 이 게임 내, 적진 깊숙한 곳에 배치된 단 하나의 자연 레벨은...

Frame at 343.46s

호라이즌(Horizon)의 테스트베드로 여겨졌습니다. 이는 프로덕션, 비주얼, 그리고 움직임 등을 다루는 데 있어 첫 번째 실질적인 시험이었습니다.

Frame at 350.73s

vegetation 에셋은 static asset이 아니라 simulation layer가 추가된 asset이므로, outsourcing 및 maintenance에 어려움이 있었습니다. 또한, 이 nature level의 performance를 통해 Killzone 방식으로는 Horizon의 open world에 foliage를 적용하기 어렵다는 것을 알게 되었습니다. polygon, shader, texture budget을 재검토해야 했습니다.

이 level은 placement system의 초기 형태를 보여주었습니다. 'Behind Enemy Lines'에서 간단한 형태로 placement system을 구현했지만, 이것이 없이는 Horizon의 truly envisioned world를 만들 수 없음을 깨달았습니다. 이에 대한 자세한 내용은 동료 Jaap이 작년 GDC에서 발표한 내용을 참고하시기 바랍니다.

Frame at 413.31s

Gorilla 웹사이트에서 해당 페이퍼를 찾으실 수 있습니다. 물론 GDC에서도 시청 가능합니다.

Frame at 419.74s

요약하자면, 저희는 아티스트가 전 세계 어디에나 적용 가능한 다양하고 흥미로우며 사실적인 환경을 묘사할 수 있는 시스템을 개발했습니다. 이 시스템은 아티스트의 의도를 최대한 반영하고 수동으로 배치된 아트와 매끄럽게 통합되어야 했습니다. 또한, 완전히 data-driven, deterministic, locally stable 해야만 했습니다. 이 시스템은 단순히 월드를 빠르게 채우는 것을 넘어, 테스트 단계 및 신규 에셋의 scale과 performance를 테스트하는 데에도 큰 도움을 줍니다. 따라서 Jaap의 발표를 꼭 확인해보시기를 강력히 권장합니다. 발표 마지막 슬라이드에 링크를 제공해 드리겠습니다.

Frame at 464.56s

네, 식생(Foliage)의 움직임부터 시작하겠습니다. 저희는 처음부터 게임의 날씨 시스템과 자연 시뮬레이션이 연동되어야 함을 인지하고 있었습니다.

Frame at 477.52s

흥미로운 게임플레이를 이끌어내고 플레이어의 통제력을 높이는 것이 중요했습니다. 또한, 저희 Vegetation Shaders의 Vertex Program을 구동하도록 설계해야 했습니다.

Frame at 489.03s

저희 시뮬레이션의 기반에는 global wind force field가 자리하고 있습니다.

Frame at 494.32s

기존 Horizon의 force field(또는 wind box)는 주로 파티클 움직임을 제어하는 데 사용되었습니다. 이들은 로컬하게, 수동으로 배치되었으며 Horizon 월드에 필요한 바람 규모와는 훨씬 작은 규모였습니다.

글로벌 wind force field는 이러한 wind box를 확장한 것으로, 몇 가지 변경 사항이 있습니다. 매 프레임 시작 시 실행되는 compute shader로, 4가지 스프링 설정 범주에 대한 힘의 영향을 시뮬레이션하고 셰이더가 샘플링할 3D 텍스처를 업데이트합니다.

바람은 플레이어나 카메라에 연결됩니다. 이를 통해 시뮬레이션이 플레이어에 가장 가까운 지점에서 항상 최고 해상도로 유지됩니다. 거리에 따라 더 큰 규모와 낮은 해상도의 3D 텍스처를 점진적으로 샘플링합니다. 이제 게임 내 글로벌 wind force field를 시각적으로 살펴보겠습니다.

Frame at 569.75s

플레이어 주변을 로컬로 잡습니다. Aloy가 멈추면 약간 줌 아웃합니다. 이것이 두 번째 cascade인 것을 볼 수 있습니다. scaling이 약간 맞지 않지만,

Frame at 592.00s

이것은 우리가 하는 일을 보여줍니다.

Frame at 597.51s

앞서 보여드린 설정은 네 가지 다른 스프링(spring) 설정 범주를 시뮬레이션할 수 있게 합니다. 추가적인 셰이더(shader) 로직 없이도, 이를 통해 세 가지 유형의 에셋(asset)에 대해 확연히 다른 움직임을 구현할 수 있습니다. 보시는 바와 같이, 에셋은 세 가지 범주로 나뉩니다. 네 가지까지 시뮬레이션할 수 있다고 말씀드렸기에, 최종적으로 이를 '라고 부릅니다.

Frame at 624.77s

특별했지만, 결국 마지막 샘플을 사용하여 배너와 타프를 구동하게 되었습니다.

Frame at 632.63s

렌더링 및 셰이딩을 위해 저희는 사실상 두 개의 셰이더만을 사용합니다. 처음에는 모든 것을 처리할 하나의 셰이더로 시작하였으나, 시간이 지나면서 잔디(grass)는 좀 더 전문화된 접근 방식이 필요하다는 것을 깨닫고 이를 위한 전용 셰이더를 구축했습니다. 이 부분은 나중에 더 자세히 설명드리겠지만, 먼저 나무와 식물을 구동하는 셰이더의 버텍스 프로그램(vertex program)부터 살펴보겠습니다.

Frame at 667.40s

바람을 시뮬레이션하는 방법을 구현하였으나, 이는 화면상의 에셋 움직임으로 연결되어야 합니다. 이를 위해 GPU GEMS 3에서 설명된 방식을 차용하였으며, skinning approximation을 mesh의 vertex colors에 저장했습니다. 2011년, vegetation을 처음 다루기 시작했을 때 이 논문을 참고했던 것이 기억납니다.

수년간 많은 부분을 변경했지만, 이 skinned data 저장 방식은 제작 과정에서 거의 변하지 않은 몇 안 되는 요소 중 하나입니다. 여러 시도를 거쳤음에도 이 설정은 크고 작은 모든 에셋에 효과적으로 적용되었습니다. 어떻게 구현했는지 보여드리겠습니다.

Frame at 711.94s

실제 바람과 움직이는 foliage를 관찰한 결과, 이를 설득력 있게 모방하기 위해 세 가지 LOD(Level of Detail)가 필요함을 확인했습니다. 첫째, 에셋 전체의 대규모 움직임 또는 완전한 휨을 표현합니다. 둘째, 가지에 해당하는 중규모 움직임을 처리합니다. 셋째, 가지의 일부 또는 잎에 해당하는 소규모 움직임을 다룹니다. 따라서 가장 먼저 에셋 전체가 바람에 반응하도록 구현하고자 합니다.

Frame at 741.01s

해당 에셋(asset)들은 상단 부분이 그렇지 않은 부분보다 훨씬 덜 rigid(강성)하기 때문에 구부러지는 효과를 구현하고자 합니다.

Frame at 747.45s

산성액 높이를 Large Scale Motion을 위한 Gradient로 활용합니다. 다음 움직임은 가지와 그에 부착된 모든 요소들의 움직임입니다. Trunk부터 시작하여, 산성액에 Gradient를 적용하는데, 이는 파란색으로 표현되며 가지의 강성(rigidity)을 근사화합니다. 간단히 말해, Trunk까지의 거리를 저장합니다. 마지막 단계는 잎, 즉 잎처럼 작고 얇은 모든 요소에 대한 것입니다. 빨간색 채널에는 이를 저장합니다.

Frame at 782.68s

'distance to branch'를 사용하며, green channel 또한 활용합니다.

Frame at 788.71s

이를 인덱스로 활용합니다. 이러한 방식을 통해 셰이더에서 씬(scene) 애니메이션을 오프셋(offset)할 수 있습니다. 마치 시간 오프셋처럼 생각하시면 됩니다. 또한, 사전 계산된 앰비언트 오클루전(ambient occlusion)을 알파 채널에 저장하여, 이후 셰이더에서 다양한 조정 작업에 활용합니다.

Frame at 814.38s

vertex colors가 적용되었으므로, vegetation shader를 사용하여 모든 asset을 처리할 수 있습니다.

Frame at 826.92s

본 Vertex Shader에서는 애셋별 애니메이션을 조절할 수 있는 변수들을 추가했습니다. 이 변수들은 주황색으로 표시되며, 게임 내 모든 변화를 제어하기에 충분했습니다. Rigidity는 간단합니다. 나무마다 흔들림 정도가 다르므로, 특정 나무의 흔들림을 줄이거나 늘릴 수 있습니다. 가지(Branches)에는 세 가지 제어 기능이 있습니다. 바람 방향을 따라 움직이는 Bend, 바람 벡터에 수직인 움직임의 크기를 조절하는 Sway, 그리고 상하 움직임을 조절하는 Lift입니다. 이를 통해 가지의 세 축 방향 움직임을 스칼라 값으로 제어합니다. 마지막으로 잎(Leaf)의 미세 움직임(Micro motion)입니다. 바람의 세기에 따라 오브젝트 공간에서 작은 3D 노이즈 텍스처를 이동시켜, 잎으로 지정된 부분(빨간색 채널로 표시)을 움직입니다.

식물은 나무와 동일한 셰이더를 사용하지만 크기가 작기 때문에, 셰이더에서 전체적인 Bend를 완전히 펴서 비용을 절감하고 지형으로의 불필요한 굽힘을 방지할 수 있습니다. 또한, 셰이더 내에서 램프 함수(ramp function)라는 간단한 공식이 큰 도움이 됩니다.

Frame at 933.16s

게임 내 바람의 최대값에 이를 곱합니다.

Frame at 937.67s

각 에셋에 대한 바람의 영향력을 조절할 수 있습니다. 예를 들어, 아스펜 나무 잎이 더 낮은 바람 속도에서도 일찍 움직여야 한다는 아트 디렉션 피드백이 있었습니다. 이 수식을 통해 해당 에셋을 다른 에셋과 차별화할 수 있었습니다.

Frame at 964.68s

Horizon 개발 초기, 잔디는 매우 야심 찬 계획의 대상이었습니다. 잔디는 어디에나 존재해야 했으며, 이는 흔들림 없는 목표였습니다. 또한, 알파 플레인(alpha planes)이 아닌 지오메트리(geometry)로 잔디를 구현하고자 했습니다. Aloy가 발로 밟거나 손으로 움직이는 등, PS4의 성능을 정확히 알지 못했던 당시의 상상력을 담아내고자 했습니다. 이러한 구현은 초기 프로토타입 단계부터 가능했지만, 잔디 렌더링에 프레임 전체가 소모되었습니다. 이에 따라 우리는 점진적으로 잔디를 단순화하며 '적은 리소스로 더 많은 것을 만드는' 여정을 시작했습니다.

Frame at 1016.21s

그 결과, 저희가 도달한 모습은 이렇습니다.

Frame at 1022.22s

최대한 적은 수의 triangles를 사용하고, 전용 grass shader와 영리한 artistic 기법을 활용합니다.

Frame at 1027.64s

텍스처 선택지에 대해 말씀드리겠습니다. 화면에 보이는 목록에서도 언급되겠지만, 이제부터 High shader와 Low shader에 대해 이야기할 것입니다. 이는 각 메시 또는 각 로트(lot) 단위로 셰이더의 특정 기능을 끄고 켤 수 있다는 의미입니다. 잔디의 경우, 이 거리에서는 lot 3에 애니메이션 버텍스 프로그램이 적용되지 않을 것입니다. 또한, 해당 인스턴스에 대해서는 더 이상 노멀 텍스처를 샘플링하지 않을 것입니다. 하지만 이야기가 조금 벗어났네요.

Frame at 1064.86s

vertex program으로 이동하겠습니다. object center의 force field에서 wind를 sample하고, grass의 height를 gradient로 사용하여 mesh의 rigidity를 모방합니다. grass의 vertex를 displacement하기 시작하며, 이것이 base motion이 됩니다. 하지만 이것만으로는 항상 spectacular한 결과를 얻을 수 없습니다. 더 많은 것이 필요합니다. 또한, grass가 특정 방향으로 기울거나 오랫동안 한 위치에 고정되는 것을 피하고 싶었습니다. 이는 원치 않는 artifact를 발생시켰기 때문입니다. 따라서 추가적인 motion을 layering합니다. force field의 length에 의해 driving됩니다.

Frame at 1117.79s

대규모 모션이 추가되었으며, 두 개의 주기 함수가 결합되어 작은 숫자 8 형태의 움직임을 만듭니다. 이는 매우 부드럽고 안정감을 주는 애니메이션이라고 생각합니다.

Frame at 1130.52s

애니메이션은 전체 메쉬에 대해 샘플링되므로, asset별로 vertices는 동일하게 움직입니다. 여기에 더하여, vertices를 변위시키는 소규모의 주기적 함수를 추가합니다.

Frame at 1142.04s

객체들은 자체 법선(normal)을 따라 움직입니다. 큰 애니메이션과 작은 애니메이션이 함께 작동하며,

Frame at 1148.88s

전체 애니메이션의 약 80%를 차지합니다. 나머지는 두 슬라이드 전의 wind sample에서 나옵니다. 시뮬레이션 외에도, 저희 잔디는 조금 더 많은 작업을 수행해야 합니다. 플레이어가 위에서 아래로 내려다볼 때 바람이 느껴지도록 하는 것과, 메쉬를 내려다볼 때 지오메트리가 사라지는 느낌을 피하고 싶었습니다.

Frame at 1176.24s

그래서 메쉬 평면을 카메라로부터 기울이는데, 이때 카메라가 위로 움직이면 버텍스를 밀어냅니다.

Frame at 1184.86s

잔디가 마치 카드처럼 보이게 되는 것을 피하기 위해 조금 더 거리를 두었습니다. 사실, 잔디 메시는 약 1.5미터의 발자국(footprint)을 가지고 있으며, 지형을 고려하지 않고 배치할 경우 원치 않는 클리핑(clipping)이나 플로팅(floating) 문제가 발생했습니다. 모든 버텍스(vertices)가 지형을 따라가도록 함으로써 이러한 문제가 해결되었습니다. 따라서 지형의 높이 맵(height map)을 기반으로 잔디의 버텍스를 오브젝트 스페이스(object space)에서 변위(displace)시켰습니다.

Frame at 1218.73s

추가로, 로딩을 돕기 위해 두 가지 작은 작업을 수행합니다. 거리에 따라 애니메이션을 축소하는 것입니다.

Frame at 1224.45s

이를 통해 마지막 처리 과정에서 해당 animation을 완전히 제거할 수 있습니다. 또한, 전체 mesh를 scaling down하면 terrain과의 asset blending이 더욱 용이해지며, 약간의 performance boost도 얻을 수 있습니다. 이 모든 작은 프로그램들이 합쳐져 저희 grass의 완전한 vertex program을 구성합니다.

Frame at 1245.18s

이제 셰이딩(shading)으로 넘어가겠습니다. 메쉬(mesh) 정렬이 완료되었으므로 셰이딩 설정을 살펴보겠습니다. 게임에서 투명도, 심지어 알파 테스트(alpha tested) 투명도까지도 성능 부담이 큽니다. 'Killzone' 시절 알파 테스트 에셋 렌더링 경험이 많지 않았습니다. 단순화된 식물(vegetation)의 경우 알파 테스트에 크게 의존합니다. 따라서 'Horizon'의 방대한 월드에 필요한 식물을 어떻게 처리할지가 과제였습니다.

Frame at 1282.18s

저희는 다음과 같은 방식으로 진행했습니다. 먼저, 모든 alpha-tested mesh를 렌더링합니다.

Frame at 1287.41s

이는 두 단계로 이루어집니다. 첫 번째로, early occluders로 렌더링한 후, 두 번째 단계에서 정상적으로 렌더링합니다.

Frame at 1295.68s

이 초기 depth-only pass에서 alpha test를 수행합니다. 이 pass를 먼저 렌더링하면 모든 depth 정보를 얻을 수 있으며, 이후에는 alpha test 없이 geometry path를 렌더링할 때 depth compare만 수행하면 됩니다. alpha test는 매우 비용이 많이 들기 때문에, 이 방식은 매우 저렴한 depth only shader로 작동하게 합니다. geometry shader는 훨씬 더 비용이 많이 들지만, 이제 매우 효율적인 fixed function depth로 가속됩니다.

Frame at 1327.84s

자, 저희 게임의 한 프레임을 분석하여 GPU 렌더링 시간에 미치는 영향을 알아보겠습니다.

Frame at 1339.34s

현재 인게임 GPU profiler 화면을 보여드립니다. 필터링 중이며, 우측 상단에서 Placement를 필터링하고 있음을 확인하실 수 있습니다. 현재 보이는 퍼센트는 저희 Placement System에 의해 배치된 모든 asset과 관련된 수치입니다. 엄밀히 말하면 모든 asset이 alpha-tested asset은 아니지만, Placement System은 alpha-tested asset 외에도 훨씬 많은 용도로 사용됩니다. 하지만 이와 같은 숲과 같은 환경에서는 95%가 alpha-tested asset이라고 보셔도 무방합니다. 저희가 주목해야 할 수치는 Depth Prime Pass가 프레임의 약 10%, Geometry Pass가 약 11.5%를 차지한다는 것입니다. 이는 총 21.345%에 달합니다.

이제 Depth Only Pass 없이 이 모든 것을 렌더링하는 비용을 살펴보겠습니다. 이는 엄청난 차이를 보여줍니다. 이처럼 Depth Only Pass가 만드는 거대한 성능 개선 효과를 초기에 인지할 수 있었습니다. 간단히 말해, alpha-tested 부분은 가능한 가장 저렴한 셰이더에서 실행될 필요가 있었습니다.

Frame at 1412.68s

저희의 Alpha Texture에 대해 말씀드리겠습니다. Horizon Zero Dawn을 개발할 때, 아티스트들에게 제어권을 부여할 수 있었던 Sign Distance Alpha Texture로 시작했습니다.

Frame at 1423.53s

쉐이더의 alpha 크기를 넘어서.

Frame at 1429.11s

저희는 셰이더에서 alpha 값을 확대 및 축소할 수 있었으며, 거리에 따라 alpha 값을 확대하는 방식을 사용했습니다. 이는 텍스처가 낮은 mid map으로 전환될 때 발생하는 품질 저하를 막기 위함이었습니다. 또한, 특정 시점에는 셰이더에서 동적인 눈 효과를 구현하여 alpha 값을 실시간으로 조절하기도 했습니다. 그러나 Horizon의 세계가 더욱 방대해지고 많은 콘텐츠로 채워짐에 따라 저희는 항상 성능 개선을 추구했습니다. 셰이더에서 alpha 값을 조절하는 것이 비용이 많이 드는 작업임을 발견하였고, 저희는 최적화를 위한 작은 부분이라도 줄여나가기를 원했습니다.

Frame at 1473.24s

GPU profiler를 다시 살펴보겠습니다. 몇 가지 주의사항이 있습니다. 프로젝트 진행 중에 많은 부분이 변동되면 특정 상황을 재현하기 어려울 때가 있습니다. 따라서 이것은 효과를 최대한 모방하고자 제가 만든 약간의 테스트입니다. GPU profiler를 다시 보겠습니다.

Frame at 1496.36s

여기서 주목해야 할 두 가지 수치는 depth prime와 쉐이더에서 alpha texture 처리 방식에 영향을 받는 shadow pass입니다. 그림자는 프레임의 약 9%를 차지하고, debt prime는 6%입니다. 변경 후 그림자는 거의 2% 감소하고 debt prime는 약 1% 감소하였습니다.

Frame at 1524.35s

저희는 이러한 결과를 얻기 위해 어떤 방법을 사용했습니까? 셰이더를 통해 알파 값을 조정하는 데 드는 비용을 파악한 후, 해당 기능을 제거하고 커스텀 MIP를 생성하는 것으로 대체했습니다. 이는 셰이더에서 이미 해당 값을 샘플링하고 있었기 때문에 합리적인 접근 방식입니다. 따라서 여기서 얻을 수 있는 모든 것은 추가 비용 없이 무료로 사용할 수 있습니다. 저희는 이를 수행하기 위한 오프라인 툴을 개발했습니다. 이 툴에 대한 설명은 슬라이드에 나와 있습니다. 간략히 말해, 이 툴은 다음과 같은 작업을 수행합니다. 이 프로세스는 알파 테스트된 이미지의 커버리지를 계산하고 노멀 MIP 체인을 구축합니다. 그런 다음 각 MIP를 샘플링하고, 각 MIP를 빌리니어(bilinear)로 업샘플링하며, 히스토그램을 구축합니다. 이 히스토그램 내에서 원본 커버리지와 일치하는 지점을 찾습니다. 게임 내 알파 테스트 값은 0.5입니다. 따라서 0.5를 찾은 값으로 나누면 원본 커버리지를 얻기 위해 해당 MIP를 스케일링해야 하는 비율을 알 수 있습니다. 합리적으로 들리시죠? 다음은 저희 MIP 체인의 시각적 표현입니다.

Frame at 1596.81s

0.5에서 클립된 후의 커버리지입니다. 그리고 나타난 하단 두 행은 저희 자체 커버리지 알고리즘으로 스케일 업된 알파와 그 이후의 커버리지를 나타냅니다.

Frame at 1610.54s

0.5에서의 클립입니다. 따라서 셰이더가 수행한 모든 작업은

Frame at 1616.97s

이제 단일 텍스처 샘플에 포함됩니다. Alpha 값을 시각적으로 가능한 한 작게 만드는 것은 언제나 좋은 습관입니다. 하지만 특히 수량이 많이 필요한 에셋의 경우, 가능한 한 Alpha 값을 작게 만들어 캐시(cache)에 들어갈 수 있도록 하는 것도 중요합니다. 특히 잔디(grasses) 에셋의 경우, 이 간단한 변경만으로도 큰 성능 향상을 얻을 수 있었습니다. BC4 압축 텍스처, 저기 보이는 텍스처는 256x128 크기입니다.

Frame at 1656.77s

Mipmaps 없이 16KB, Mipmaps 포함 21KB입니다. 처음에는 Mipmaps 없이 시작하였습니다.

Frame at 1669.71s

그래픽 프로그래머가 저희의 anti-aliasing solution에 대한 제 check-in을 보고 만족스럽지 않아했습니다. 그래서 mipmap을 포함한 더 큰 버전을 선택했습니다. 하지만 이 퀄리티를 극대화하기 위해서는 훌륭한 anti-aliasing solution이 필수적입니다. 저희 그래픽 프로그래머가 Kojima 프로그래머와 함께 작성한 논문에 감사를 표합니다.

Frame at 1706.29s

작년에 SikGraft에서 발표되었던 내용입니다. 이는 현재 게임에 적용되고 있는 anti-aliasing solution을 간략히 설명합니다. 발표 후 마지막 슬라이드에 관련 링크를 제공할 예정입니다.

Frame at 1720.59s

이어서 픽셀 프로그램의 나머지 부분을 살펴보겠습니다.

Frame at 1727.32s

저희 Decima 엔진은 deferred shading 방식을 사용하며, vegetation shaders는 다음과 같이 적용됩니다.

Frame at 1734.19s

G-buffers입니다. 셰이더에 제공되는 텍스처들이며, 예외는 다음과 같습니다.

Frame at 1747.49s

잔디의 ambient occlusion texture를 사용하지 않습니다. 또한 대부분의 bark 렌더링 시 translucency를 생략합니다. 현재 슬라이드에서 보시는 툴은 texture set tool입니다. 이 툴은 Photoshop 파일을 불러와 버튼 하나로 game 및 editor에서 바로 사용할 수 있는 압축된 DDS 파일로 pack 해주는 매우 유용한 툴입니다. 여기 보시는 것은 일반적인 vegetation texture 설정으로, BC7 두 개와 BC4 하나를 사용합니다.

Normals의 경우, 슬라이드에 보이는 식물과 같이 proper geometry를 다룰 때는 다른 3D asset과 크게 다르지 않습니다. 다만, double sided 렌더링이 필요하다는 점만 고려하면 됩니다. front 또는 back facing triangle인지 확인하여 normal을 뒤집습니다. 그러나 alpha planes 또는 triangles에 크게 의존하는 asset의 경우, normal을 잘못 뒤집는 것이 canopies와 grass의 뻣뻣한 느낌을 숨기는 데 매우 유용할 수 있습니다. vertex의 normal을 조정하고 view space normal의 absolute value를 취하여 렌더링을 방지합니다.

Frame at 1832.78s

카메라에서 떨어진 방향으로 향하는 노멀(normals)을 사용합니다. 당사는 노멀 생성을 위해 ISO 서비스를 활용합니다.

Frame at 1839.21s

이것은 저희 캐노피를 둘러싼 메쉬가 어떻게 보이는지에 대한 예시입니다. 그리고 우리는 이 노멀(normals)을 버텍스(verts)로 전달합니다. 이것이 노멀 버퍼(normal buffer)가 어떻게 보이는지를 보여줍니다. 이제 잘못된 경우와 비교해보겠습니다.

Frame at 1858.84s

그리고 노멀 normals입니다. 이것은 G-boover이며, 최종적으로 선택한 결과물입니다. 이는 매우 예술적인 선택입니다.

Frame at 1870.84s

조명 및 Translucency 표현에 있어서, 해당 기법을 모든 부분에 적용하지는 않았습니다. 다만, Geometry 근사에 있어서는 단순함을 감추는 데 도움을 줄 수 있습니다.

Frame at 1883.30s

자산의 특성, 특히 잔디의 경우 32개의 삼각형이라는 사실을 숨기기 위해 많은 노력을 기울였습니다. 지오메트리 노멀은 그대로 유지하되, 뷰 공간 노멀의 Z 컴포넌트에 앱스(apps)를 적용합니다. 다시 한번, G-bervish 샷입니다.

Frame at 1907.99s

알베도(Albedo)입니다. 저희 게임의 모든 vegetation은 colorized 되어 있습니다.

Frame at 1913.53s

전체 게임의 colorization을 위해, 우리는 texture array에 저장하는 방식을 사용합니다.

Frame at 1920.90s

128x8 픽셀 크기의 에코톱(ecotope) 컬러라이즈드 텍스처(colorized textures) 64장을 사용합니다. 이 텍스처들은 녹음(vegetation)의 색상뿐만 아니라 바위, 지형, 먼지, 이끼(lichen)의 색상 정보도 포함합니다. 에셋(asset)의 월드(world) 내 위치에 따라 샘플링할 텍스처가 결정되며, 이는 W 컴포넌트(component)로 제어됩니다. 다양한 월드 데이터는 하나의 그레이스케일 텍스처에 담겨 V 컴포넌트를 구동합니다. 아티스트는 8개의 밴드(band) 중에서 선택할 수 있으며, 이는 컬러라이제이션 텍스처 내 다양한 종류의 에셋(acid)을 선택하는 것과 같습니다. 모든 녹음 에셋은 잔디(grass)를 제외하고는 오브젝트 전체에 대해 한 번의 샘플링을 사용하며, 잔디는 버텍스(vertex)당 샘플링합니다. 이를 위해 알베도 텍스처(albedo textures)의 색상을 미드 그레이(mid-gray) 값으로 평균화합니다. 컬러라이즈(colorize)하고 싶지 않은 부분이 있다면 마스크 텍스처(mask texture)를 사용합니다. 셰이더(shader)에서는 마스크 텍스처를 통해 샘플링된 컬러라이즈드 값을 샘플링된 알베도 텍스처 위에 오버레이(overlay)합니다. 이때 일루미네이트 함수(illuminate function)를 사용합니다. 결과 색상은 배경 색상과 전경 색상이 혼합되며, 전경이 밝을수록 더 밝고 어두울수록 더 어두워집니다. 이 결과는 셰이더에서 조합했을 때 얻을 수 있는 것입니다. 러프니스(roughness)와 리플렉턴스(reflectance)는 텍스처 샘플, 버텍스 알파(vertex alpha)에 베이크(bake)된 앰비언트 오클루전(ambient occlusion)의 조합으로부터 도출됩니다.

Frame at 2038.00s

반사(reflectance)는 4 dielectric으로 고정하였으며, 러프니스(roughness)는 아티스트의 세밀한 조절을 위해 해당 변수를 열어 레벨(level) 또는 세트 레인지(set range) 버전으로 추가하였습니다.

Frame at 2049.27s

이를 보완하기 위해, 잎과 나무껍질이 단일 텍스처에 포함될 경우, 두 재질 간의 roughness를 쉽게 구분하기 위해 translucency texture를 사용합니다. 잔디의 경우, vertex color stream이나 ambient occlusion texture가 없기에, translucency texture를 활용하여 유사한 기능을 수행합니다.

Frame at 2076.15s

화면에 보이는 첫 번째, 세 번째 항목의 **translucency**는 백그라운드에서 처리되고 있습니다.

Frame at 2081.72s

번역 및 요약:

말하자면, 셰이더(shaders)에서 접근하거나 조정할 수 없는 부분이 있지만, 명확성을 위해 언급하고자 합니다. 마지막 세 가지 항목은 아티스트가 조정할 수 있습니다. 표면 두께(surface thickness)는 표면 내부의 흡수량과 표면을 통과한 후 산란 콘(scattering cone)의 백색도를 결정합니다. 이를 G-buffer(G-buffers) 상의 translucency amount와 translucency diffusion 값으로 인코딩합니다. translucency amount는 흡수량, translucency diffusion은 산란량을 의미합니다. 어느 시점에서든 모든 식물(vegetation)의 diffusion 양은 특정 값으로 고정했으며, translucency amount는 텍스처 샘플(texture sample)로 제어합니다. 또한, AO 텍스처와 정점 색상(vertex colors)에 저장된 AO를 결합한 pre-computed AO를 계층적으로 적용합니다. 잔디(grass)의 경우, translucency amount 텍스처의 파생(derivative)을 pre-computed AO로 사용합니다.

마지막 단계에서 물리적 정확성(physical correctness)을 추구하는 것에서 벗어납니다. 이곳에서는 산란광(scattered light)의 양을 증폭시킬 수 있습니다. 이를 통해 게임의 전반적인 비주얼에 더 부합하는 초현실적인(surreal) 효과를 얻을 수 있습니다.

Frame at 2173.43s

여기까지 셰이딩에 대한 설명이었습니다. 이제 우리가 바람 시뮬레이션을 위해 필요한 것이 무엇인지 알게 되었습니다. 이제 식물 제작 파이프라인을 살펴보겠습니다. 다시 한번, 우리가 시작했을 때 우리는…

Frame at 2192.90s

Killzone 경험을 통해 이러한 요소를 어떻게 제작해야 하는지 알고 있었습니다. 저희는 많은 노력을 기울여 Killzone Vegetation도 구현해냈습니다. 이를 통해 저희는 앞으로 해결해야 할 과제가 명확함을 알 수 있었습니다. 또한, 저희는 Vegetation의 성능 한계를 시험하기 위해 자체적인 Art Benchmark를 개발하여 역량을 확장했습니다.

Frame at 2226.50s

저희가 제작한 결과물입니다. 2014년 초, 회사의 모든 사람들에게 저희가 추구하는 에셋의 밀도와 숲에 배치하고자 하는 다양한 에셋의 양을 알리기 위해 제작되었습니다. 이 결과물은 게임 내에서 초당 10프레임이라는 놀라운 성능으로 실행되었습니다. 재미있는 버섯들도 일부 포함되어 있습니다. 하지만 이 테스트 단계에서는 아직 모델을 제작 중이었습니다.

Frame at 2264.26s

기존 Killzone 에셋과 마찬가지로, 최상의 퀄리티를 위해 많은 노력을 기울였습니다. 또한, 거리에 따른 vertex 수를 충분히 줄여 성능을 확보하고자 했습니다. 이러한 작업을 제작 과정 동안 지속적으로 진행하였습니다.

Frame at 2280.58s

E3 2015에서 Horizon을 처음 공개했습니다. 저희 부스에서 플레이 가능한 데모를 선보였으며, 개발자들이 게임을 시연하기 위해선 게임 플레이가 가능해야 했습니다. 이에 따라 LODs(Level of Detail) 최적화가 필수적이었습니다. 가장 높은 LOD는 그대로 두되, 나머지 LOD들은 대폭 개선이 필요했습니다.

E3 행사 이후, 소프트웨어 솔루션에 의존하지 않고 LOD의 성능과 품질을 향상시킬 방법을 모색했습니다. 삼각형 수를 줄이기 위한 소프트웨어적인 접근 대신, 제작 과정 자체를 변경하기로 결정했습니다.

새로운 접근 방식은 제작 프로세스를 뒤집는 것이었습니다. 가장 낮은 LOD부터 먼저 제작하고, 인게임에서의 외형과 성능에 만족하면 필요한 디테일을 추가하여 상위 LOD 메쉬를 구축하는 방식으로 변경했습니다.

Frame at 2346.20s

이것을 좀 더 명확하게 설명해 드리겠습니다.

Frame at 2350.74s

원하는 트리나 기타 에셋에 적용할 컴포넌트들의 가장 낮은 lot을 구축하였습니다.

Frame at 2357.36s

일반적으로 트라이앵글(triangle) 또는 쿼드(quad) 형태로 시작하며, 텍스처나 실루엣의 가능성을 스케치하는 데 시간을 투자합니다. 이를 바탕으로 에셋(asset) 제작을 진행합니다. Horizon 개발 과정에서는 Speedtree를 적극 활용했습니다. 만족스러운 결과가 나오면, 게임 내에 에셋을 배치하고 퍼포먼스, 실루엣, 캐노피(canopy) 밀도 등을 검토합니다. 이 단계에서는 수정이 용이하며, 많은 부분을 빠르게 변경할 수 있습니다. 에셋이 적합하다고 판단되면 텍스처 제작에 필요한 고해상도 컴포넌트(component)를 빌드합니다. 이 예시는 최종적으로 제작된 브랜치(branch)로, Maya와 Speedtree의 조합으로 만들어졌습니다. 중요한 것은 시작 단계에서 스케치한 텍스처 및 레이아웃과 고해상도 컴포넌트가 일치하는 것입니다.

Frame at 2433.67s

고해상도 모델 제작 및 배치 완료 후, Maya로 이동하여 texture baking을 진행합니다. 자체 shader를 적용하고, 게임 모델에 필요한 UV space로 mesh와 texture를 baking합니다.

Frame at 2449.76s

이제 텍스처 베이킹(texture baking)이 완료되고 준비되었으므로, 이전에 작업했던 speed models로 돌아가겠습니다.

Frame at 2456.21s

이는 로트 세 개로 간주하여 로딩을 시작합니다. 즉, 로트 두 번째와 첫 번째 로트의 디테일 작업을 시작하는 것입니다. 이 메쉬들을 둘러싸고 있는 작은 삼각형들은

Frame at 2472.02s

Speedtree에서 LOD(Level of Detail)의 bounding box가 일관되도록 하는 workaround를 적용하였습니다. 저희는 프로덕션 전반에 걸쳐 Speedtree 6 버전을 사용하였으며, LOD가 완벽하게 정렬되도록 하기 위해 이 작업이 필요했습니다. 최신 버전에서는 해당 이슈가 없으나, 파이프라인이나 프로젝트 중간에 소프트웨어를 변경하는 것을 원치 않았습니다. Speedtree 내에서 기존 speed model mesh를 새로운 lot chain으로 교체하였고, 이는 성공적으로 마무리되었습니다. 재미있는 점은 Speedtree UI에 Aloy를 hack하는 것을 시도했는데, 비교적 간단하면서도 사람들이 발견했을 때 즐거워하는 것을 볼 수 있었습니다. 이후, bones와 AO 정보가 포함된 lot chain을 Speedtree에서 FBX로 export 하였습니다.

Frame at 2530.50s

그 후, 사내에서 개발한 커스텀 Houdini 프로세스를 사용하여 해당 메쉬를 처리합니다.

Frame at 2535.80s

해당 skeletal 및 skinning 데이터를 가져와, 본 발표 시작 부분에서 설명드린 verticolor로 변환합니다. 이제 Maya에서 asset을 설정하고 게임으로 export하는 작업만 남았습니다.

Frame at 2551.74s

일반적인 나무 에셋의 LOD(Level of Detail) 체인은 다음과 같은 모습입니다. 다시 말씀드리지만, high와 low LOD 셰이더를 사용합니다.

Frame at 2562.22s

lot 4와 lot 5에 billboards가 포함된 것을 확인하실 수 있습니다. billboards에 대한 정보를 포함시키고 싶었으나, 특별한 점이 없어 cross planes 형태로만 제공됩니다.

Frame at 2583.32s

여기에서는 저희 다른 asset의 전형적인 setup을 보실 수 있습니다.

Frame at 2593.39s

저희는 태양 그림자(sun shadows)에 네 개의 캐스케이드(cascades)를 사용합니다. 가장 먼저 언급된 컴파트먼트 캐스케이드(compartment cascade)에서는 Aloy와 그녀의 머리카락, 의상, 장비를 렌더링합니다. 이를 통해 Aloy 자신과 주변 환경에 고품질 그림자가 드리워집니다. 이 캐스케이드에서는 어떠한 식생(vegetation)도 렌더링하지 않습니다.

캐스케이드 제로(zero)와 원(one)은 표준 섀도우 맵(shadow maps)이며, 저희는 거리 캐스케이드(distance cascade)에는 높이 필드 시스템(height field system)을 사용하여 그림자를 렌더링합니다. 이제 식생이 렌더링되는 표준 섀도우 맵인 캐스케이드 제로와 원에 대해 논의하겠습니다. 이 섀도우 맵을 렌더링할 때, 저희는 약간 특이한 방식을 사용합니다.

그림자 렌더링 시 LOD(Level of Detail)를 선택하는 일반적인 방법은 카메라와의 거리를 기준으로 합니다. 하지만 이는 LOD 간의 크로스페이딩(crossfading)이 없어 그림자가 눈에 띄게 튀는(pop) 현상을 유발합니다. 저희는 캐스케이드 간에는 크로스페이딩을 적용합니다.

그래서 저희는 고정된 거리를 기준으로 오브젝트를 섀도우 맵에 렌더링하고, 캐스케이드마다 하나의 오브젝트만 처리하도록 결정했습니다. 즉, 캐스케이드 0에 렌더링되는 모든 오브젝트는 카메라로부터 5미터 떨어진 것처럼 LOD를 선택합니다. 캐스케이드 1에 렌더링되는 모든 오브젝트는 25미터 떨어진 것처럼 LOD를 선택합니다.

이 방식은 오브젝트가 특정 캐스케이드에 머무르는 동안 항상 동일한 섀도우 LOD를 사용하므로 그림자 LOD 팝핑 문제를 해결합니다. 오브젝트가 다른 캐스케이드로 전환될 때, 캐스케이드 간의 부드러운 블렌딩(blend)으로 인해 다른 섀도우 LOD로 변경될 때 전혀 팝핑 현상이 발생하지 않습니다. 이는 렌더링 비용을 다소 증가시키지만, 그림자 팝핑 문제를 거의 완전히 해결했으며 충분히 가치 있는 작업이었습니다.

Frame at 2731.45s

자, 이제 모두 준비되었습니다.

Frame at 2738.47s

여기서는 cascade 0와 1 사이의 부드러운 transition을 보실 수 있습니다.

Frame at 2743.89s

이와 더불어, vegetation 및 기타 asset들에 대해서도 추가적인 작업을 수행하였습니다.

Frame at 2748.26s

우리가 shadow casters를 분리했습니다.

Frame at 2752.40s

저희 비주얼 로트 체인에서는 섀도우 캐스팅 메시가 두 개이며, 비주얼 메시가 여섯 개입니다. 이는 초반부터 엄청난 이점을 제공합니다. 삼각형 수를 크게 줄일 수 있기 때문입니다. 보시는 바와 같이, 두 번째 로트인 섀도우캐스터는 더 이상 알파 테스트를 사용하지 않으며 애니메이션도 적용되지 않습니다. 따라서 최대한 비용 효율적으로 구현할 수 있습니다.

Frame at 2795.76s

본 발표가 여러분께 즐거움을 드렸기를 진심으로 바랍니다. 이제 발표를 마무리할 시간이 거의 다 되었습니다.

Frame at 2810.03s

결론적으로, 여러분께서 vegetation 분야에 첫 발을 내딛으신다면 꼭 시도해 보시거나 활용해 보시기를 권장하는 몇 가지 사항을 간략히 소개해 드리겠습니다.

Frame at 2821.05s

이것으로 위아래 목록이 완성되었습니다. 언급해 드린 depth prime, custom MIP chain 등이 포함된 모든 항목이 이 목록에 포함됩니다.

Frame at 2830.77s

이전 경험에서 가장 중요했던 점은 섀도우 캐스터(shadow caster)를 비주얼 메쉬(visual meshes)와 분리하고 YAP의 배치 시스템을 활용하는 것이었습니다. 더불어 모든 작업을 사내에서 자체적으로 진행할 수 있었던 점 또한 큰 도움이 되었습니다. 덕분에 성능 최적화를 위한 반복 작업과 수정이 매우 용이했으며, 지속적으로 개선해 나갈 수 있었습니다. 이 부분이 저에게는 매우 중요했습니다.

Frame at 2861.45s

먼저 감사의 말씀을 드립니다. 그리고 제가 사용한 reference에 대한 slide를 제공할 것을 약속드렸습니다.

Frame at 2869.29s

이것이 바로 그것입니다.

Frame at 2872.75s

시간이 허락한다면, 질문이 있으신 분들은 마이크 앞으로 나와 주시면 감사하겠습니다.

Frame at 2886.94s

**눈(Snow) 표현 기법**

눈이 쌓인 덤불에 빛이 비칠 때 감탄을 자아냈던 표현에 대해 문의하셨습니다. 초기에는 셰이더 상의 동적 눈 시스템으로 구현하려 했으나, 성능 부담이 커서 전용 눈 에셋을 제작하는 방식으로 변경되었습니다. 이를 통해 눈이 많이 쌓인 지역에 눈 에셋을 배치하고, 그래비티(gravity)를 활용하여 자연스럽게 눈이 내려앉는 효과를 구현했습니다.

**Shadow Asset과 Visual Rendering Asset의 정렬 문제**

Shadow Asset과 Visual Rendering Asset을 분리하면서 발생하는 정렬 문제에 대한 질문이 있었습니다. 이를 해결하기 위해 Shadow Casting LOD(Level of Detail)를 최적화했습니다. 모든 구현은 근사치(approximation)를 기반으로 하며, 시각적 메쉬(visual mesh)의 삼각형 수를 줄이는 방식으로 최적화하여 성능을 확보하고 정상적인 작동을 유지했습니다.

**배치(Placement) 시스템의 아티스트 도구 및 반복 작업**

배치 시스템은 에디터 내에서 노드 기반으로 작동하며, 다양한 월드 데이터를 조합하여 GPU 상에서 밀도 맵(density maps)을 생성합니다. 숲 타일 위에 도로가 있다면, 도로 텍스처를 곱하여 도로 영역의 밀도를 0으로 만듭니다. 경사, 수역과의 거리, 하천선 등 현실적인 배치를 위한 다양한 요소를 고려하며, 아티스트는 이러한 규칙 세트를 기반으로 실시간으로 페인팅할 수 있습니다.

**식생(Vegetation)의 포토그래메트리(Photogrammetry) 활용**

식생 에셋 중 일부는 포토그래그래메트리 기법을 활용하여 제작되었습니다. 특히 DLC의 퀘이킹 아스펜(quaking aspen)은 더 많은 포토그래메트리 작업을 거쳐 실험적으로 구현되었습니다. 이를 통해 성능을 확보하면서도 더욱 사실적인 식생 표현을 시도했습니다.

**애니메이션되는 나무 줄기 및 가지와의 상호작용**

정확히는 가지 충돌(collision)은 원통형(cylinder)으로 단순화하여 처리합니다. 나무 캐노피(canopy)는 충돌이 없어 플레이어가 닿으면 밀려나는 방식으로 구현됩니다. 이는 게임플레이 상의 상호작용을 위한 것이며, 에로우(arrow)와 같은 오브젝트가 충돌하는 경우는 별도 처리됩니다.

**바람(Wind) Force Field 생성 방식**

바람 Force Field는 게임 내 특정 영역을 정의하여 생성됩니다. 여러 Force Field를 조합하고 블렌딩하여 다양한 강도의 바람 효과를 만듭니다. 각 Force Field는 흐름(flow), 최소/최대 흐름(min/max flow)과 같은 속성을 가지며, 복잡한 수학적 연산을 통해 바람의 움직임을 구현합니다.

**잔디(Grass) 상호작용 및 눈 시스템 활용 가능성**

잔디가 캐릭터 이동에 따라 매트해지는 상호작용은 아직 구현되지 않았지만, 개발팀의 위시리스트에 있습니다. DLC에서 도입된 눈(snow) 굴곡 시스템을 잔디를 포함한 다른 오브젝트에도 적용하는 방안을 검토 중입니다.