Renderização de ondas oceânicas com base em FFT implementada no Godot
(github.com/2Retr0)- GodotOceanWaves é um experimento de renderização no Godot Engine que gera a superfície de oceano aberto usando a transformada inversa de Fourier de um espectro direcional de ondas oceânicas, simulando diversos ambientes marítimos com parâmetros ajustáveis em tempo real por script
- As Gerstner waves são adequadas para detalhes de baixa frequência em águas calmas, mas têm limitações para superfícies agitadas de oceano aberto, então este projeto usa espectros baseados em dados empíricos de oceanografia e FFT
- A geração das ondas segue o método de Tessendorf: cria um estado espectral inicial, propaga no tempo com a relação de dispersão e depois gera mapas de deslocamento, normais e espuma com a transformada inversa de Fourier
- A renderização segue o BSDF apresentado na GDC de Atlas, mas usa distribuição GGX; a espuma se acumula e decai em regiões onde o Jacobiano do deslocamento é negativo; o sea spray é implementado com GPUParticles3D do Godot e shaders customizados
- Cascatas múltiplas de ondas, controle da taxa de atualização e distribuição de carga por cascata ajudam a lidar com tiling e carga de GPU, mas as cascatas precisam usar a mesma resolução de textura espectral e a abordagem de partículas para sea spray é ineficiente para aumentar a densidade
O que o GodotOceanWaves faz
- GodotOceanWaves é um projeto experimental de renderização de oceano aberto no Godot Engine
- A geração das ondas usa a transformada inversa de Fourier de espectros direcionais de ondas oceânicas
- Os parâmetros expostos foram organizados de forma enxuta e podem ser alterados em tempo real por script para simular vários ambientes de ondas marítimas
- O projeto inclui os vídeos de demonstração
ocean_demo.mp4eenvironment_demo.mp4
Por que usar ondas baseadas em Fourier em vez de Gerstner waves
- Uma forma comum de animar água em jogos é deslocar vértices com Gerstner waves
- As Gerstner waves funcionam bem para modelar detalhes de baixa frequência em águas calmas, mas não representam com precisão superfícies agitadas de oceano aberto
- Ao usar espectros de ondas oceânicas baseados em dados empíricos coletados por oceanógrafos, fica mais direto controlar quais ondas e parâmetros devem ser alterados para produzir um estado marítimo específico
- A FFT tem complexidade
O(N log N), menor que osO(N^2)da transformada discreta de Fourier clássica, e pode escalar para sistemas paralelos, o que a torna adequada para execução em GPU - Nas Gerstner waves, cada thread precisa calcular
Nondas, enquanto em ondas baseadas em FFT cada thread executa apenas cálculos proporcionais alog(N)
Shading, espuma e sea spray
- O modelo de iluminação oceânica segue em grande parte o BSDF da apresentação da GDC de Atlas
- A distribuição de microfacet usa GGX em vez de Beckmann
- Isso porque os highlights mais suaves e achatados da GGX ofereceram uma aparência mais uniforme nos vários ambientes oceânicos testados
- Os mapas de normais e espuma são amostrados com uma mistura de filtragem bicúbica e bilinear conforme a densidade de pixels no espaço do mundo
- Em baixa resolução de superfície, isso reduz artefatos de aliasing das texturas, enquanto em alta resolução preserva os detalhes
- A sea foam é gerada seguindo o método de Tessendorf, detectando onde as cristas das ondas se enrolam para dentro, ou seja, onde o Jacobiano do deslocamento é negativo
- A espuma é acumulada linearmente na textura ao longo de várias atualizações das ondas
- Os parâmetros
"foam grow rate"e"foam decay rate"controlam, respectivamente, o acúmulo e o decaimento exponencial
- O sea spray é implementado com o nó
GPUParticles3Ddo Godot e um shader de partículas customizado- As partículas são distribuídas uniformemente em um plano dentro da bounding box de
GPUParticles3D - Elas são descartadas conforme a quantidade de espuma naquela posição, e as que não forem descartadas iniciam seu ciclo de vida com um deslocamento aleatório
- Cada partícula usa um billboard sprite com uma única textura estática
- Ao longo do ciclo de vida, scale e displacement são alterados para simular o visual do splash, e um efeito de dissolve no mesh shader cria a sensação de que o spray se atomiza no ar
- As partículas são distribuídas uniformemente em um plano dentro da bounding box de
- A principal desvantagem dessa abordagem de sea spray é que mesmo um grande aumento no número de partículas produz apenas um pequeno aumento de densidade
- Isso ocorre porque as partículas são distribuídas uniformemente na bounding box e a maioria das partículas extras acaba sendo descartada
Pipeline de simulação das ondas
- O método de geração da superfície segue de perto Tessendorf
- Um estado inicial espectral do oceano é criado multiplicando a função espectral direcional de ondas oceânicas por números aleatórios com distribuição gaussiana
- O estado inicial é propagado no tempo pela relação de dispersão, que liga a frequência da onda à sua velocidade de propagação
- Em seguida, aplica-se a transformada inversa de Fourier ao estado propagado para gerar mapas de deslocamento e normais
- A implementação é composta por um pipeline de compute shaders que usa a abstração
RenderingDevicedo Godot
Composição do espectro de ondas oceânicas
- A função espectral direcional de ondas oceânicas
S(ω, θ)retorna a energia da onda para uma frequênciaωe direçãoθ - Essa função é composta por uma função espectral não direcional
S(ω)e uma função de espalhamento direcionalD(ω, θ), e as escolhas das duas são independentes entre si - Para o espectro não direcional, é usado o espectro Texel-Marsen-Arsloe (TMA) descrito por Horvath
- O TMA usa velocidade do vento
U, profundidade da águaDe fetch lengthF - O TMA combina o espectro JONSWAP com uma função de atenuação pela profundidade
- O TMA usa velocidade do vento
- Para a função de espalhamento direcional, é usada uma mistura entre os espalhamentos direcionais flat e Hasselmann de Horvath, controlada pelo parâmetro
spreadμ- Também inclui o parâmetro
swellξproposto por Horvath para modelar o alongamento das ondas oceânicas - O espalhamento direcional de Hasselmann foi escolhido por oferecer uma solução analítica aproximada para o coeficiente de normalização
Q(σ)
- Também inclui o parâmetro
- Seguindo a proposta de Tessendorf, a função espectral resultante também é multiplicada por um termo de supressão de ondas pequenas
- Esse termo usa a magnitude do vetor de número de onda
ke o parâmetrodetailδ
- Esse termo usa a magnitude do vetor de número de onda
- O espectro direcional final combina TMA, espalhamento direcional misto e supressão de ondas pequenas
Implementação de FFT na GPU
- A FFT usa uma implementação customizada na GPU escrita com compute shaders
- Em vez de Cooley-Tukey, o algoritmo usado é o Stockham FFT, evitando a permutação inicial de bit-reversal
- Seguindo Flügge, é calculada uma textura
"butterfly"que codifica o fluxo de dados da FFT- Essa textura é calculada apenas uma vez sempre que a resolução da textura espectral muda
- A FFT 2D primeiro aplica o kernel FFT nas linhas da textura espectral
- Depois, um compute shader transpõe a textura para reutilizar o mesmo kernel FFT de linha como se fosse um FFT de coluna
- A transposição não só permite reutilizar o pipeline como também melhora o padrão de acesso à memória
Wave cascades e mitigação de tiling
- À distância, especialmente quando há sea foam, os artefatos de tiling ficam muito evidentes
- O sistema de geração de ondas lida com isso permitindo sobrepor várias wave cascades ao mesmo tempo
- Cada cascade tem seu próprio tamanho de tiling e conjunto de parâmetros
- As cascades podem ser adicionadas e removidas dinamicamente em tempo real
- Como todas as cascades usam o mesmo compute pipeline, elas precisam ter a mesma resolução de textura espectral
- Como alternativa, também seria possível reduzir artefatos de tiling misturando deslocamento e normais das ondas com ruído, com custo de desempenho menor
- Para evitar interferência entre ondas ao sobrepor cascades, é preciso escolher com cuidado os parâmetros e o tamanho de cada uma
- As fases das ondas de cada cascade também devem ser deslocadas para não interferirem entre si
- O sistema tenta fazer isso automaticamente aplicando offsets diferentes ao tempo inicial de cada cascade
- Há a ressalva de que não é certo se isso funciona de fato
Distribuição da carga de atualização
- Como o movimento das ondas é irregular, o deslocamento pode parecer perceptualmente suave mesmo sem ser atualizado a cada frame
- O parâmetro
"update rate"controla com que frequência por segundo cada wave cascade será atualizada - Pular frames reduz o tempo de GPU gasto com FFT, mas ainda pode haver stutter nos frames em que o pipeline de geração das ondas é executado
- Experimentos com cálculo assíncrono das atualizações de cascade usando o
RenderingDevicelocal do Godot geraram grande overhead de desempenho por causa da transferência de texturas entre CPU e GPU - Em vez disso, o sistema tenta distribuir a carga entre cascades
- Se o frame time for menor que a update rate, apenas uma cascade é atualizada por frame
- Isso reduz o stutter e ao mesmo tempo preserva a vantagem de menor carga de GPU obtida ao pular frames
Materiais de referência
- 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
1 comentários
Comentários no Hacker News
Dois outros repositórios Godot criados por essa pessoa também são muito interessantes. Gosto do nível de detalhe que ela coloca ao descrever os repositórios, e este em particular chama a atenção: https://github.com/2Retr0/GodotGaussianSplatting
Fico curioso se é estudante. Cita com frequência trabalhos de outras pessoas e parece entender muito bem materiais publicados recentemente
ENGR96A-coursework,Relevant coursework for ENGR 96A Introduction to Engineering Design F23, e, pelas datas dos commits, F23 parece significar o semestre de outono de 2023Claro, pode ser que as disciplinas da UCLA mencionadas no repositório estejam abertas ao público em geral, então talvez não seja necessário estar matriculado na UCLA no sentido tradicional para acompanhá-las
Há 20 anos, acho que poderíamos passar meses ajustando a superfície do mar em uma renderização e ainda assim não chegar perto desse nível. É um resultado muito bem feito
Dito isso, o vídeo de demonstração parece um pouco exagerado. Como alguém com mais de 50 mil milhas náuticas de navegação em mar aberto registradas no diário de bordo, se as ondas fossem tão pontiagudas e altas assim, o vento teria que estar soprando muito mais forte. Ainda assim, parece algo que se resolve ajustando os parâmetros
Se foi feito em Godot, dá para considerar renderização em tempo real? Também fico curioso se exige uma GPU monstruosa
Além disso, aqui a espuma/borrifo do mar parece um efeito de pós-processamento, enquanto no mar real o spray suaviza a nitidez das ondas, algo que também aparece visualmente de forma sutil
Claro, este modelo elegante, simples e eficaz quase não tem física, então espero que isso seja visto menos como uma crítica de braços cruzados e mais como sugestões de direções para cutucar e experimentar
Um dos meus vídeos favoritos sobre o tema: https://m.youtube.com/watch?v=yPfagLeUa7k
Este também é bom: https://m.youtube.com/watch?v=kGEqaX4Y4bQ
Em vez disso, passo fins de semana mexendo em shader de água até chegar ao visual que quero. Só que meu jogo é de puzzle, então ele nem precisa de água. Agora só tenho uma tela inicial bonita
Hoje isso é só uma caixa de seleção em um motor de jogo, junto com incontáveis outros recursos. As pessoas não conseguem imaginar direito como o ambiente ficou bom graças aos motores modernos
Não quero diminuir o valor deste trabalho; é uma realização excelente e dou os parabéns ao autor. Só queria dizer que vivemos em uma época muito boa
Mesmo assim, está muito bom e, dentro do que pesquisei rapidamente recentemente, talvez esteja entre o melhor do que é possível
Pessoalmente, eu gostaria de ver ondas de mar aberto geralmente com mais ritmo. Fico curioso se uma simulação de vento de 15 nós soprando por algumas horas sobre 1 km de superfície da água corresponderia ao que se observa na prática: arrebentações relativamente organizadas se formando e depois se dispersando
Coisas assim foram o que me atraíram para o mundo da computação, mas, em algum momento, caí na vida fácil e entediante de costurar bibliotecas, endpoints e besteira corporativa, o que está levando ao burnout
Espero um dia conseguir voltar
Espero que tente. Todo mundo está torcendo por você
A abordagem é diferente, mas, se você se interessa por simulação de ondas/oceano, o Acerola publicou um vídeo excelente sobre esse tema, como sempre. https://youtu.be/PH9q0HNBjT4 e https://youtu.be/yPfagLeUa7k
Edit: um deles já estava linkado nas referências, mas estava com o nome real em vez do apelido, então só percebi agora
É interessante como esse problema é difícil. Há décadas tentamos resolvê-lo e ainda ficamos no vale da estranheza
Se você pausar essa cena, as ondas levantadas parecem montanhas cobertas de neve. Para a água assumir formas tão pontiagudas, o vento teria que ser bem forte, mas o vento não sopraria diretamente para cima, então soa irreal. Essas características pontiagudas deveriam ter direcionalidade
Ainda assim, capturou bem as propriedades das ondas de swell. O movimento vertical é maior que o deslocamento horizontal, que é o que se esperaria em mar aberto
Não sei como simulam a transmissão da luz, mas funciona bem. A luz atravessando as ondas ao nascer ou pôr do sol é excelente e torna o efeito realmente convincente
Pode-se dizer que é um pouco pictórico, mas não parece errado
Morei alguns anos na praia. Quando você mora na praia, o mar é fascinante e agradável aos sentidos, então acaba olhando para ele por horas. Ao ver isso, acho que em nenhum momento eu teria pensado que era uma renderização
Também há um bom exemplo no Shadertoy: https://www.shadertoy.com/view/Ms2SD1
Fourier teve a ideia da transformação de frequências de ondas enquanto media ondas de maré, então, de certa forma, isso quase fecha o ciclo
Muito impressionante
Excelente, mas essa abordagem provavelmente quebra em certa medida em ondas realmente grandes por causa da não linearidade. Ondulações não são senoides, mas, para ondas pequenas e médias, são parecidas o suficiente para esse método funcionar muito bem
Ondas muito grandes não só se afastam muito mais de senoides, como também começam a interagir entre si de forma não linear, algo que a superposição linear usada aqui não consegue capturar
Portanto, para a maioria dos estados do mar, é fantástico. Mas, se você quiser criar ondas realistas no estilo de “Tempestade Perfeita”, precisará de outra abordagem
Muito impressionante e melhor do que qualquer coisa que vi até agora, mas o swell parece um pouco estranho. Se eu tivesse que explicar, diria que ondas de alta frequência não se deslocam sobre ondas de baixa frequência como no vídeo