Pular para o conteúdo
Fundamento · RuídoIntermediário

Distorção de domínio

Desloque as coordenadas de um ruído com outro ruído, para formas fluidas e dobradas como fumaça, mármore e líquido.

Como funciona

Mover um padrão é mudar a coordenada que você entrega a ele: pattern(p + offset) o desliza, como mostra o fundamento de transformações 2D. A distorção de domínio (domain warping) usa um deslocamento diferente em cada ponto, e tira esse deslocamento do próprio ruído. O padrão é empurrado para um lado aqui e para outro ali, e os traços retos se curvam em redemoinhos e dobras que lembram fumaça, tinta na água, mármore ou lava escorrendo.

O “domínio” é o espaço de entrada de uma função. Distorcê-lo significa deformar o espaço antes que a função o leia, em vez de mudar a função.

Uma distorção

Pegue um fBm, uma soma de oitavas de ruído, e calcule-o duas vezes, em dois lugares sem relação, para ter um vetor 2D aleatório em cada ponto. Some esse vetor, escalado, à coordenada de um terceiro fBm:

vec2 q = vec2(fbm(p), fbm(p + vec2(3.7, 8.1))); // um campo vetorial aleatório e suave
float n = fbm(p + 4.0 * q);                     // o fBm, lido através da distorção

O deslocamento vec2(3.7, 8.1) só serve para tornar os dois componentes independentes; qualquer deslocamento maior que algumas células funciona. O 4.0 é a força da distorção: com 0 você recebe o fBm puro de volta, perto de 1 as formas ondulam, e de 3 a 6 elas se dobram sobre si mesmas. Como q é suave, pixels vizinhos recebem deslocamentos parecidos, então o resultado também continua suave: o espaço é esticado e dobrado, não rasgado.

Distorcendo a distorção

A ideia se aninha. Distorça o próprio campo vetorial com um segundo nível e leia o fBm final através dele:

vec2 q = vec2(fbm(p), fbm(p + vec2(3.7, 8.1)));
vec2 r = vec2(fbm(p + 4.0 * q + vec2(6.4, 2.9)), fbm(p + 4.0 * q + vec2(1.6, 7.5)));
float n = fbm(p + 4.0 * r);

Cada nível deixa os redemoinhos mais intrincados, com filamentos finos se enrolando uns nos outros. Dois níveis é, em geral, onde para de compensar. O custo sobe rápido: um nível são três chamadas de fBm, dois níveis são cinco e, com cinco oitavas cada, são 25 avaliações de ruído por pixel. Num celular em tela cheia isso pesa; renderize em resolução menor, use menos oitavas nos níveis de dentro (eles só precisam ser suaves, não detalhados) ou grave o resultado numa textura se ele não precisar se mexer.

Cor a partir da distorção

Os vetores intermediários servem para mais do que o valor final. length(q) diz com que força cada ponto foi empurrado, e r.x e r.y são campos suaves que acompanham as mesmas dobras. Misturar cores com eles, além de com n, dá o visual riscado e de vários tons do mármore ou das nebulosas:

float tone = clamp(n * 1.5 + 0.5, 0.0, 1.0);
vec3 color = mix(vec3(0.10, 0.12, 0.30), vec3(0.85, 0.45, 0.20), tone);
color = mix(color, vec3(0.95, 0.90, 0.80), clamp(length(q), 0.0, 1.0) * 0.5);
color = mix(color, vec3(0.20, 0.50, 0.55), clamp(abs(r.x) * 2.0, 0.0, 1.0) * 0.4);

Animando

Some o tempo a um dos deslocamentos de dentro, como fbm(p + vec2(3.7, 8.1) + 0.1 * time). O campo de distorção deriva, e o padrão final se revolve e flui no lugar em vez de deslizar pela tela, o visual de fumaça e nuvens. Somar o tempo direto a p desliza tudo de uma vez e perde esse efeito.

Distorcendo outras coisas

A coordenada distorcida funciona com qualquer padrão, não só com ruído. Distorça a UV antes de ler uma textura e a imagem ondula como um reflexo na água. Distorça a entrada de listras ou anéis e eles se curvam em veios de mármore ou fibras de madeira. Distorça o ponto passado a um campo de distância e uma forma dura ganha uma borda orgânica. A regra é sempre a mesma: calcule um campo de deslocamento, some-o à coordenada e mantenha a força pequena o bastante para que o resultado ainda se leia como o padrão original.

Leitura complementar