7. Perlin noise por dentro (opcional)

Cinco passos para transformar uma coordenada (x, y) em um número entre −1 e 1.

A imagem para ter em mente. Os pontos pretos são estacas fincadas no chão. A seta vermelha de cada estaca diz "o terreno sobe para cá". Você está no ponto laranja. Cada uma das quatro estacas ao redor dá uma opinião sobre a sua altura: se você andou da estaca até aqui na direção da seta, "subiu"; contra, "desceu"; de lado, "ficou no nível". A altura final é a média das quatro opiniões, com a estaca mais próxima pesando mais.

Mova o mouse sobre a grade (ou clique para fixar um ponto). Use os botões de passo para revelar o cálculo aos poucos. A grade tem nós em coordenadas inteiras; cada nó guarda um vetor gradiente pseudo-aleatório (setas vermelhas), escolhido pela semente.

Quem é quem no painel

NomeO que éNa imagem das estacas
Po ponto (x, y)onde você está
x0, y0 / fx, fycanto inferior esquerdo da célula / posição dentro dela (0 a 1)a estaca de trás / quanto você andou desde ela
g00 … g11gradiente de cada canto (primeiro dígito é x, segundo é y)a seta de cada estaca
d00 … d11vetor do canto até Po caminho que você andou saindo daquela estaca
n00 … n11produto escalar g · da opinião de cada estaca: positivo subiu, negativo desceu
u, vfade(fx), fade(fy)os pesos suavizados da média
nx0, nx1média da fileira de baixo e da de cimaopinião de cada fileira
nmédia das duas fileiras; ×√2 para caber em −1..1a altura do chão sob seus pés

A curva S: por que não interpolar linearmente

peso u = curva(fx) em função de fx

Com peso linear, o valor é contínuo mas a derivada salta ao cruzar um nó: aparecem "quinas" e a imagem mostra a grade. Smoothstep zera a primeira derivada nos nós; o fade de 2002 zera também a segunda. Troque a curva no seletor e olhe o fundo da grade: com linear, dá pra ver os quadrados.

Marcador laranja: o fx do ponto atual.

O algoritmo em 12 linhas

function perlin2(x, y):
  x0 = floor(x); y0 = floor(y); x1 = x0+1; y1 = y0+1
  fx = x - x0;   fy = y - y0                     // posição dentro da célula, 0..1

  g00 = gradiente(x0,y0); g10 = gradiente(x1,y0)  // 1. vetor pseudo-aleatório por canto
  g01 = gradiente(x0,y1); g11 = gradiente(x1,y1)  //    (tabela de permutação + semente)

  n00 = dot(g00, [fx,   fy  ]); n10 = dot(g10, [fx-1, fy  ])  // 2-3. distância canto→ponto
  n01 = dot(g01, [fx,   fy-1]); n11 = dot(g11, [fx-1, fy-1])  //      e produto escalar

  u = fade(fx); v = fade(fy)                      // 4. curva S nos pesos
  return lerp( lerp(n00, n10, u),                 // 5. interpolação bilinear
               lerp(n01, n11, u), v )
Por que gradiente e não valor? No value noise o valor no nó é o próprio número sorteado, então todo máximo e todo mínimo cai em um nó, e o olho percebe a grade. No Perlin, o valor em todo nó é exatamente zero (distância zero, produto escalar zero) e o que varia é a inclinação. O resultado tem mais "movimento" e menos alinhamento com a grade.
Consequência importante: se você amostrar Perlin só em coordenadas inteiras (por exemplo PerlinNoise(x, y) com x e y inteiros de um grid), todos os valores serão iguais. Sempre multiplique por uma escala fracionária. Isso é visto na demo 2.