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

Ruído celular (Worley / Voronoi)

A distância até o mais próximo de muitos pontos espalhados, para células, escamas, rachaduras e vitrais.

Como funciona

Espalhe pontos pelo plano e pinte cada pixel conforme a distância até o ponto mais próximo. Perto de um ponto fica escuro, e clareia em direção às bordas onde dois pontos estão à mesma distância. As bordas formam um diagrama de Voronoi, o padrão de células que aparece na pele da girafa, na lama seca, na espuma e nos vitrais. Steven Worley descreveu isso como base de texturas em 1996, por isso o ruído também se chama ruído de Worley.

Um ponto por célula

Testar todos os pontos para cada pixel seria lento demais. O truque é colocar exatamente um ponto em cada célula da grade, num deslocamento aleatório dentro dela, vindo de um hash da célula. Assim, o ponto mais próximo tem de estar na própria célula do pixel ou numa das oito vizinhas, e cada pixel testa nove pontos:

vec2 hash22(vec2 cell) {
    uvec2 q = uvec2(ivec2(cell));
    // o hash inteiro do fundamento de hash
    uint h = mix32(q.x ^ mix32(q.y));
    return vec2(h >> 16, h & 0xffffu) / 65536.0; // dois valores em 0..1 de um só hash
}

vec2 cellular(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    float f1 = 8.0; // a menor distância até agora
    float f2 = 8.0; // a segunda menor
    for (int y = -1; y <= 1; y++) {
        for (int x = -1; x <= 1; x++) {
            vec2 o = vec2(float(x), float(y)); // qual vizinha
            vec2 point = o + hash22(i + o);    // o ponto dela, relativo a esta célula
            float d = length(point - f);
            if (d < f1) {
                f2 = f1;
                f1 = d;
            } else if (d < f2) {
                f2 = d;
            }
        }
    }
    return vec2(f1, f2);
}

Tudo é medido em relação à célula do pixel (f e o), então os números continuam pequenos onde quer que o padrão esteja. O length das noções de vetores dá a distância. O custo é de nove hashes e nove raízes quadradas por pixel, mais que uma oitava de ruído de gradiente, então empilhe com cuidado.

Com o ponto em qualquer lugar da célula, um ponto a duas células de distância pode, de vez em quando, estar mais perto que todos os nove testados, o que aparece como uma emenda reta, curta e rara. Reduzir o deslocamento (0.5 + 0.8 * (hash22(i + o) - 0.5)) mantém os pontos longe das bordas da célula e deixa a emenda bem mais rara, ao preço de um padrão um pouco mais regular.

O segundo ponto mais próximo cai fora das nove células com muito mais frequência que o mais próximo, então o f2, e as paredes de F2 − F1 mais abaixo, mostram essas emendas bem mais vezes que o F1. Para visuais baseados no F2, reduza o deslocamento como acima ou procure num bloco 5×5 (25 células) em vez de 3×3.

F1, F2 e suas combinações

f1 é a distância até o ponto mais próximo, f2 a distância até o segundo mais próximo. Cada um dá uma aparência:

  • F1 são manchas escuras com bordas claras: células, bolhas, escamas. 1.0 - f1 inverte isso em calombos redondos.
  • F2 sozinho tem um aspecto mais anguloso e facetado, como cristais ou papel amassado.
  • F2 − F1 vale zero exatamente nas bordas entre células e cresce em direção ao meio. Um smoothstep sobre ele desenha as paredes das células: 1.0 - smoothstep(0.0, 0.05, f2 - f1).

F2 − F1 é um substituto barato da distância real até a borda, não a mesma coisa: linhas desenhadas a partir dele ficam mais finas e mais grossas ao longo do comprimento. A distância exata até a borda exige uma segunda passada pelas vizinhas, medindo a distância até a mediatriz entre o ponto mais próximo e cada um dos outros; o artigo sobre bordas de Voronoi nas fontes explica o passo a passo. Use a versão barata para padrões orgânicos e a exata para contornos nítidos e regulares.

Ids de célula

Guarde de qual célula veio o ponto mais próximo e você tem um id por região, a base de vitrais e mosaicos:

vec2 nearest = vec2(0.0);
// dentro do laço, quando um novo mais próximo é encontrado:
//     nearest = i + o;
// uma cor aleatória por célula
vec3 tint = vec3(hash22(nearest), hash22(nearest + 7.0).x);

Todos os pixels de uma célula recebem o mesmo id, então a cor é chapada dentro dela e muda nas bordas.

Movimento e variantes

Anime os pontos fazendo cada um oscilar com a própria fase: point = o + 0.5 + 0.4 * sin(time + 6.2831853 * hash22(i + o)). Cada coordenada fica entre 0.1 e 0.9, dentro da célula, então a busca nas nove células falha tão raramente quanto com o deslocamento reduzido acima. Usar outra distância muda o formato das células: a distância de Manhattan (abs(d.x) + abs(d.y)) e a de Chebyshev (max(abs(d.x), abs(d.y))) dão células com paredes retas na horizontal, na vertical e na diagonal, com um ar mais mecânico. A versão 3D testa 27 células e dá os mesmos padrões na superfície de qualquer malha, sem precisar de UVs.

Leitura complementar