Godot에 구현된 FFT 기반 해양 파도 렌더링
(github.com/2Retr0)- GodotOceanWaves는 Godot Engine에서 방향성 해양 파도 스펙트럼의 역 Fourier 변환을 이용해 열린 바다 표면을 생성하는 렌더링 실험이며, 실시간 스크립트 조정 가능한 파라미터로 다양한 해양 환경을 모사함
- Gerstner waves는 잔잔한 물의 저주파 디테일에는 적합하지만 거친 열린 바다 표면에는 한계가 있어, 이 프로젝트는 경험적 해양학 데이터 기반 스펙트럼과 FFT를 사용함
- 파도 생성은 Tessendorf 방식을 따라 초기 스펙트럼 상태를 만들고, 분산 관계로 시간 전파한 뒤 역 Fourier 변환으로 변위·노멀·폼 맵을 생성함
- 렌더링은 Atlas GDC 발표의 BSDF를 따르되 GGX 분포를 사용하고, 폼은 변위의 Jacobian이 음수인 영역에서 누적·감쇠되며, sea spray는 Godot의 GPUParticles3D와 커스텀 셰이더로 구현됨
- 다중 wave cascade, 업데이트 주기 조절, cascade별 부하 분산으로 타일링과 GPU 작업량을 다루지만, cascade는 같은 스펙트럼 텍스처 해상도를 써야 하고 sea spray 입자 방식은 밀도 증가 효율이 낮음
GodotOceanWaves가 하는 일
- GodotOceanWaves는 Godot Engine에서 열린 바다 렌더링을 실험하는 프로젝트임
- 파도 생성에는 방향성 해양 파도 스펙트럼의 역 Fourier 변환을 사용함
- 노출된 파라미터는 간결하게 구성되어 있으며, 스크립트로 실시간 수정해 여러 해양 파도 환경을 모사할 수 있음
- 데모 영상으로
ocean_demo.mp4와environment_demo.mp4가 포함되어 있음
Gerstner waves 대신 Fourier 기반 파도를 쓰는 이유
- 게임에서 물을 애니메이션화하는 흔한 방식은 Gerstner waves로 버텍스를 변위시키는 것임
- Gerstner waves는 잔잔한 물의 저주파 디테일을 모델링하는 데는 잘 맞지만, 열린 바다의 거친 표면을 정확히 표현하는 데는 부족함
- 해양학자가 수집한 경험적 데이터 기반 해양 파도 스펙트럼을 사용하면, 특정 해양 상태를 만들기 위해 어떤 파도와 파라미터를 바꿔야 하는지 더 직접적으로 다룰 수 있음
- FFT는 고전적 이산 Fourier 변환의
O(N^2)보다 낮은O(N log N)복잡도를 가지며, 병렬 시스템으로 확장 가능해 GPU 실행에 적합함 - Gerstner waves는 각 스레드가 파도 수
N개에 대해 계산해야 하지만, FFT 기반 파도는 각 스레드가log(N)에 해당하는 계산만 수행함
셰이딩, 폼, sea spray
- 해양 조명 모델은 대체로 Atlas GDC 발표의 BSDF를 따름
- 마이크로패싯 분포는 Beckmann 대신 GGX를 사용함
- GGX의 더 평평하고 부드러운 하이라이트가 테스트한 여러 해양 파도 환경에서 더 균일한 외관을 제공했기 때문임
- 노멀/폼 맵은 월드 공간 픽셀 밀도에 따라 bicubic과 bilinear 필터링을 섞어 샘플링함
- 낮은 표면 해상도에서는 텍스처 aliasing 아티팩트를 줄이고, 높은 표면 해상도에서는 디테일을 유지함
- Sea foam은 Tessendorf 방식에 따라 파도 봉우리가 안쪽으로 말리는 위치, 즉 변위의 Jacobian이 음수인 곳에서 생성 여부를 판단함
- 폼은 여러 파도 업데이트에 걸쳐 텍스처에 선형 누적됨
"foam grow rate"와"foam decay rate"파라미터가 각각 누적과 지수 감쇠를 제어함
- Sea spray는 Godot의
GPUParticles3D노드와 커스텀 파티클 셰이더로 구현됨- 파티클은
GPUParticles3D바운딩 박스 안의 평면에 균등 분포함 - 해당 위치의 폼 양에 따라 culling되고, culling되지 않은 파티클은 무작위 오프셋에서 생명주기를 시작함
- 각 파티클은 정적 텍스처 하나를 쓰는 billboard sprite를 사용함
- 생명주기 동안 scale과 displacement를 바꿔 splash 외형을 모사하고, mesh shader의 dissolve 효과로 공중에서 물보라가 atomize되는 느낌을 냄
- 파티클은
- 이 sea spray 방식의 큰 단점은 파티클 수를 크게 늘려도 밀도 증가는 작다는 점임
- 파티클이 바운딩 박스에 균등 분포해 추가된 파티클 대부분이 culling되기 때문임
파도 시뮬레이션 파이프라인
- 표면 파도 생성 방식은 Tessendorf를 밀접하게 따름
- 방향성 해양 파도 스펙트럼 함수에 Gaussian 분포 난수를 곱해 초기 스펙트럼 바다 상태를 생성함
- 초기 상태는 파도의 주파수와 전파 속도를 연결하는 분산 관계로 시간에 따라 전파됨
- 전파된 상태에 역 Fourier 변환을 적용해 변위 맵과 노멀 맵을 생성함
- 구현은 Godot의
RenderingDevice추상화를 사용하는 compute shader pipeline으로 구성됨
해양 파도 스펙트럼 구성
- 방향성 해양 파도 스펙트럼 함수
S(ω, θ)는 주파수ω와 방향θ가 주어졌을 때 파도의 에너지를 반환함 - 이 함수는 비방향성 스펙트럼 함수
S(ω)와 방향성 확산 함수D(ω, θ)로 구성되며, 두 선택은 서로 독립적임 - 비방향성 스펙트럼에는 Horvath가 설명한 Texel-Marsen-Arsloe(TMA) 스펙트럼을 사용함
- TMA는 풍속
U, 수심D, fetch lengthF를 사용함 - TMA는 선행 JONSWAP 스펙트럼과 수심 감쇠 함수를 결합함
- TMA는 풍속
- 방향성 확산 함수에는 Horvath의 flat 및 Hasselmann 방향성 확산을
spread파라미터μ로 섞은 방식을 사용함- Horvath가 제안한
swell파라미터ξ도 포함해 해양 파도의 elongation을 모델링함 - Hasselmann 방향성 확산은 정규화 계수
Q(σ)에 대한 근사 해석해를 제공하기 때문에 선택됨
- Horvath가 제안한
- Tessendorf의 제안에 따라 결과 스펙트럼 함수에는 작은 파도 억제 항도 곱함
- 이 항은 파수 벡터 크기
k와detail파라미터δ를 사용함
- 이 항은 파수 벡터 크기
- 최종 방향성 해양 파도 스펙트럼은 TMA, mixed directional spread, small-wave suppression을 결합함
GPU FFT 구현
- FFT는 compute shader로 작성한 커스텀 GPU 구현을 사용함
- Cooley-Tukey 대신 Stockham FFT 알고리듬을 사용해 초기 bit-reversal permutation을 피함
- Flügge를 따라, FFT의 데이터 흐름을 인코딩하는
"butterfly"텍스처를 계산함- 이 텍스처는 스펙트럼 텍스처 해상도가 바뀔 때마다 한 번 계산됨
- 2D FFT는 먼저 스펙트럼 텍스처의 행 방향으로 FFT 커널을 적용함
- 이후 compute shader로 텍스처를 transpose해 같은 행 방향 FFT 커널을 열 방향 FFT처럼 재사용함
- transpose는 파이프라인 재사용을 가능하게 할 뿐 아니라 메모리 접근 패턴도 개선함
Wave cascade와 타일링 대응
- 먼 거리에서는, 특히 sea foam이 있을 때 타일링 아티팩트가 매우 뚜렷해짐
- 파도 생성 시스템은 여러 wave cascade를 동시에 레이어링할 수 있게 해 이 문제를 다룸
- 각 cascade는 자체 타일링 크기와 파라미터 세트를 가짐
- cascade는 실시간으로 동적 추가·제거할 수 있음
- 모든 cascade가 같은 compute pipeline을 사용하므로 같은 스펙트럼 텍스처 해상도를 가져야 함
- 대안으로 파도 변위와 노멀을 노이즈와 블렌딩해 더 낮은 성능 비용으로 타일링 아티팩트를 줄일 수도 있음
- cascade를 겹칠 때 파도 간섭을 피하려면 각 cascade의 파라미터와 크기를 신중히 골라야 함
- cascade의 파동 phase도 서로 간섭하지 않게 offset되어야 함
- 시스템은 각 cascade의 시작 시간을 다르게 offset해 이를 자동으로 시도함
- 이 방식이 실제로 작동하는지는 확실하지 않다는 caveat가 포함되어 있음
업데이트 부하 분산
- 파도 움직임은 불규칙하기 때문에 변위를 매 프레임 갱신하지 않아도 지각적으로 부드러워 보일 수 있음
"update rate"파라미터는 wave cascade를 초당 얼마나 자주 업데이트할지 제어함- 프레임을 건너뛰면 FFT에 쓰는 GPU 시간이 줄어들지만, 파도 생성 파이프라인이 실행되는 프레임에서는 여전히 stutter가 발생함
- Godot의 local
RenderingDevice로 cascade 업데이트를 비동기 계산하는 실험은 CPU와 GPU 사이 텍스처 전송 때문에 큰 성능 오버헤드를 만들었음 - 대신 시스템은 cascade의 부하 분산을 시도함
- 프레임 시간이 update rate보다 짧으면 프레임당 cascade 하나만 업데이트함
- 이 방식은 stutter를 줄이면서 프레임 스킵으로 얻는 낮은 GPU 작업량의 이점을 유지함
참고한 자료
- Realtime GPGPU FFT Ocean Water Simulation: Fynn-Jorin Flügge, Hamburg University of Technology, 2017
- I Tried Simulating The Entire Ocean: Garrett Gunnell, 2023
- Empirical Directional Wave Spectra for Computer Graphics: Christopher J. Horvath, DigiPro, 2015
- Simulating Ocean Water: Jerry Tessendorf, SIGGRAPH, 2004
- Implementing Fast Fourier Transform Algorithms of Real-Valued Sequences: Robert Matusiak, Texas Instruments, 2001
- Wakes, Explosions and Lighting: Interactive Water Simulation in 'Atlas': Mark Mihelich, GDC, 2019
- FFT-Ocean: Ivan Pensionerov, GitHub, 2020
댓글과 토론
Hacker News 의견들
-
이 사람이 만든 다른 Godot 저장소 두 개도 매우 흥미로움. 저장소를 설명할 때 디테일을 많이 넣어주는 점이 좋고, 특히 이 저장소가 눈에 띔: https://github.com/2Retr0/GodotGaussianSplatting
학생인지 궁금함. 다른 작업을 자주 인용하고, 최근 발표된 자료를 꽤 잘 이해하는 것처럼 보임- 학생일 가능성이 있어 보임. 저장소 하나의 제목과 설명이
ENGR96A-coursework,Relevant coursework for ENGR 96A Introduction to Engineering Design F23이고, 커밋 날짜로 보면 F23은 2023년 가을 학기라는 뜻 같음
물론 저장소에 언급된 UCLA 수업들이 모두에게 공개되어 있을 수도 있으니, 전통적인 의미로 UCLA에 등록한 학생이어야만 들을 수 있는 건 아닐 수도 있음
- 학생일 가능성이 있어 보임. 저장소 하나의 제목과 설명이
-
20년 전에는 렌더링에서 해수면을 몇 달씩 조정해도 이 수준에 근처도 못 갔을 것 같음. 정말 잘 만든 결과물임
다만 데모 영상은 약간 과장되어 보임. 항해 기록에 외해 항해가 5만 해리 넘는 입장에서 보면, 저렇게 날카롭고 높은 파도라면 바람이 훨씬 강하게 불어야 함. 그래도 파라미터만 조정하면 될 부분일 듯함
Godot로 만든 거라면 실시간 렌더링이라고 봐도 되나? 괴물급 GPU가 필요한지도 궁금함- 비판이 아니라 관찰인데, 이건 파란색으로 염색한 뜨거운 옥수수 시럽의 바다를 상상하면 이런 모습일 것 같음. 점성은 맞아 보이지만, 표면장력은 실제 바닷물과 다를 수 있음. 실제 바닷물은 소금물 H2O와 생체 물질의 콜로이드라 현실에서는 흔하지만 전산 유체역학에는 꽤 지저분한 대상임
또 여기서 바다 물보라는 사후 효과처럼 보이는데, 실제 바다에서는 물보라가 파도의 날카로움을 무디게 만들어 시각적으로도 어렴풋이 드러남
물론 이 우아하고 단순하며 효과적인 모델에는 거의 물리가 없으니, 이런 건 팔짱 끼고 비판한다기보다 이리저리 찔러보고 시도해볼 방향을 제안하는 정도로 봐줬으면 함 - 요즘은 이런 걸 다루는 영상이 많아서 전체 과정이 어느 정도 접근 가능해졌지만, 여전히 함정이 많음
이 주제에서 좋아하는 영상 중 하나: https://m.youtube.com/watch?v=yPfagLeUa7k
이것도 괜찮음: https://m.youtube.com/watch?v=kGEqaX4Y4bQ - 게임 프로젝트가 있는데, 멋진 게임 기술을 보면 늘 너드 스나이핑당해서 직접 구현하고 싶어짐. 기존 에셋을 쓰거나 샀다면 지금까지 한 진행도는 48시간 게임잼에서도 가능했을 것 같음
대신 주말마다 물 셰이더를 만지며 원하는 모습이 나올 때까지 놀고 있음. 그런데 내 게임은 퍼즐 게임이라 물이 필요 없음. 이제 멋진 시작 화면만 생겼을 뿐임 - 내가 졸업할 때만 해도 파티클 엔진, 행진 큐브 같은 걸 하는 게 졸업 논문 프로젝트였음
요즘은 게임 엔진의 체크박스 하나일 뿐이고, 그런 기능이 수없이 많음. 현대 엔진 덕분에 얼마나 좋은 환경인지 사람들이 잘 상상하지 못함
이 작업의 가치를 깎아내리려는 건 아니고, 정말 훌륭한 성취라 제작자에게 찬사를 보냄. 다만 지금 우리가 얼마나 좋은 시대에 있는지를 말하고 싶었음 - 영상만 보면 약간 무작위적으로 보인다는 데 동의함
그래도 매우 좋아 보이고, 최근 대충 찾아본 범위에서는 가능한 것들 중 아마 최고 수준 같음
개인적으로는 외해 파도가 보통 더 리듬감을 갖는 모습을 보고 싶음. 15노트 바람이 1km 수면 위를 몇 시간 동안 불 때의 시뮬레이션이 실제로 관찰하는 것, 즉 비교적 정돈된 파열이 형성됐다가 흩어지는 모습과 맞는지 궁금함
- 비판이 아니라 관찰인데, 이건 파란색으로 염색한 뜨거운 옥수수 시럽의 바다를 상상하면 이런 모습일 것 같음. 점성은 맞아 보이지만, 표면장력은 실제 바닷물과 다를 수 있음. 실제 바닷물은 소금물 H2O와 생체 물질의 콜로이드라 현실에서는 흔하지만 전산 유체역학에는 꽤 지저분한 대상임
-
이런 것들이 나를 컴퓨터 세계로 끌어들였는데, 어느새 라이브러리와 엔드포인트, 기업식 헛소리를 이어붙이는 쉽고 지루한 삶에 빠져 번아웃으로 이어지고 있음
언젠가는 다시 돌아갈 수 있기를 바람- 조금씩 옮겨가면 된다고 봄. 처음에 흥미를 느꼈고 더 배우고 결국 해보고 싶은 분야를 찾아서, 매주 몇 시간씩 파고들다가 점차 늘리면서 현재 일에서 멀어지는 식이면 됨
해보길 바람. 다들 응원하고 있음 - 그런 일을 하며 나보다 돈을 더 많이 버는 친구들을 많이 보지만, 나는 관심사를 따라 첫 기회에 로보틱스로 갔고 후회하지 않음. 적어도 나에게는 내 코드가 실제 세계와 상호작용하는 걸 보는 느낌만 한 게 없음
- 조금씩 옮겨가면 된다고 봄. 처음에 흥미를 느꼈고 더 배우고 결국 해보고 싶은 분야를 찾아서, 매주 몇 시간씩 파고들다가 점차 늘리면서 현재 일에서 멀어지는 식이면 됨
-
접근 방식은 다르지만, 파도/해양 시뮬레이션에 관심 있다면 Acerola가 이 주제로 늘 그렇듯 훌륭한 영상을 올렸음. https://youtu.be/PH9q0HNBjT4 그리고 https://youtu.be/yPfagLeUa7k
수정: 하나는 참고문헌에 이미 링크되어 있었는데, 닉네임이 아니라 실명으로 되어 있어서 이제야 알아봄 -
이 문제가 얼마나 어려운지 흥미로움. 수십 년 동안 시도해왔는데도 아직 불쾌한 골짜기에 머물러 있음
이 장면을 정지해보면 솟아오른 파도가 눈 덮인 산처럼 보임. 물이 이렇게 날카로운 형상을 가지려면 바람이 꽤 강해야 하는데, 바람이 곧장 위로 불 리는 없어서 비현실적으로 느껴짐. 이런 날카로운 특징들은 방향성이 있어야 함
그래도 너울성 파도의 성질은 꽤 잘 잡았음. 수평 이동보다 위아래 움직임이 더 큰데, 외해에서 기대할 수 있는 모습임- Nightingale을 해봤는데, 거기 바닷물은 정말 특별함. 분명히 말하면 파도를 잔잔하게 유지해서 물보라 같은 문제는 다루지 않고, 대신 다른 부분에 집중할 수 있었던 듯함
빛 투과를 어떻게 시뮬레이션하는지 모르겠지만 잘 작동함. 일출이나 일몰 때 파도 사이로 비치는 빛이 훌륭해서 효과를 정말 그럴듯하게 만들어줌
약간 회화적이라고 할 수는 있겠지만, 틀려 보이지는 않음
- Nightingale을 해봤는데, 거기 바닷물은 정말 특별함. 분명히 말하면 파도를 잔잔하게 유지해서 물보라 같은 문제는 다루지 않고, 대신 다른 부분에 집중할 수 있었던 듯함
-
해변에서 몇 년을 살았음. 해변에 살면 바다가 매혹적이고 감각적으로 좋아서 몇 시간씩 바라보게 됨. 이걸 보고 렌더링이라고는 한순간도 생각 못 했을 것 같음
- 지금 해변에 살고 있고 수면에서 약 14층 높이의 전망도 있는데, 이상하게도 이건 가짜라는 게 분명히 보임. 돌풍이 만드는 어두운 패치가 없고, 하얀 부분이 살짝이라도 말려 들어가지 않고 곧장 위로 올라감. 그 외에는 꽤 실제 같음
- 실제로 파도와 그 움직임을 본 적이 있나? 해변에서 몇 년을 살았다면 거품 형성과 파도의 움직임이 자연스럽지 않다는 걸 바로 알아챌 수 있을 것임
-
Shadertoy에도 괜찮은 예제가 있음: https://www.shadertoy.com/view/Ms2SD1
-
Fourier가 조석파를 측정하다가 파동 주파수 변환을 떠올렸으니, 어떤 의미에서는 거의 한 바퀴 돌아온 셈임
매우 인상적임 -
훌륭하지만, 이 접근은 정말 큰 파도에서는 비선형성 때문에 어느 정도 무너질 것임. 물결은 사인파가 아니지만, 작은 파도와 중간 높이의 파도에서는 충분히 비슷해서 이런 방법이 아주 잘 작동함
아주 큰 파도는 사인파에서 훨씬 더 멀어질 뿐 아니라, 파도끼리 비선형적으로 상호작용하기 시작해서 여기서 쓰는 선형 중첩 방식으로는 잡아낼 수 없음
그래서 대부분의 해상 상태에서는 환상적임. 하지만 ‘퍼펙트 스톰’ 같은 파도를 현실적으로 만들고 싶다면 다른 접근이 필요함 -
매우 인상적이고 지금까지 본 것보다 낫지만, 너울이 약간 어긋난 느낌임. 굳이 설명하자면 영상에서처럼 고주파 파도가 저주파 파도 위를 타고 이동하지는 않는다고 봄