Derivadas em espaço de tela
dFdx, dFdy e fwidth medem o quanto um valor muda de um pixel para o seguinte, para bordas que ficam com um pixel de suavidade em qualquer tamanho.
Como funciona
Um shader de fragmentoO programa da GPU que roda uma vez para cada pixel coberto por um triângulo e decide a cor dele. Shader de fragmento O programa da GPU que roda uma vez para cada pixel coberto por um triângulo e decide a cor dele.
dFdx, dFdy e fwidth
dFdx(v)é, aproximadamente, a diferença deventre este pixel e o vizinho horizontal no quad.dFdy(v)é o mesmo, na vertical.fwidth(v)éabs(dFdx(v)) + abs(dFdy(v)): o quantovmuda em cerca de um pixel, em qualquer direção.
Elas funcionam com qualquer expressão float ou vetorial que o shader calcula, não só com as entradas. Se uv vai de
0 a 1 num canvas de 1000 pixels, fwidth(uv.x) vale cerca de 0.001. Aproxime a câmera da superfície e o valor
diminui; incline a superfície para longe, espremendo a textura em menos pixels, e ele aumenta. O GLSL ES 3.00 tem as
três embutidas (o WebGL 1 precisava de uma extensão), e elas só existem no shader de fragmento.
Bordas de um pixel
Um smoothstepUm limiar suave. Vale 0 abaixo da primeira borda, 1 acima da segunda e segue uma curva em S com as pontas planas entre elas. Smoothstep Um limiar suave. Vale 0 abaixo da primeira borda, 1 acima da segunda e segue uma curva em S com as pontas planas entre elas.t = clamp((x − e0) / (e1 − e0), 0, 1); s = t² (3 − 2t)smoothstep(0.49, 0.51, d), fica nítido num tamanho
e borrado ou serrilhado em outro, porque 0.02 unidade cobre um número diferente de pixels a cada vez. O fwidth mede
um pixel nas unidades de d, então a borda pode ter exatamente essa largura onde quer que seja desenhada:
float d = length(p) - 0.5; // distância com sinal até um círculo de raio 0.5
float w = fwidth(d); // um pixel, nas unidades de d
float disc = clamp(0.5 - d / w, 0.0, 1.0);
A última linha é uma rampa linear de 1 a 0 ao longo de um pixel centrado na borda, perto de quanto do pixel a forma cobre. Isso é antialiasing analítico: nenhuma amostra extra, só uma derivada. Funciona com qualquer campo que mude de forma suave, e é por isso que campos de distância com sinal e derivadas andam juntos.
Filtrando um padrão
Um padrão repetido desenhado com fract sofre aliasing à distância: quando um período fica menor que um pixel, o pixel
pega um ponto praticamente aleatório dentro dele, e o padrão se desfaz em cintilação e moiré. A derivada diz quantos
pixels um período cobre, então o padrão pode sumir antes que isso aconteça. Aqui, linhas de grade que se apagam ao
longo de um pixel de cada lado, a qualquer distância:
vec2 g = uv * 10.0; // dez células por unidade
vec2 fw = fwidth(g); // células por pixel, em cada eixo
vec2 dist = abs(fract(g - 0.5) - 0.5) / fw; // distância até a linha mais próxima, em pixels
float line = 1.0 - clamp(min(dist.x, dist.y), 0.0, 1.0);
Quando fw passa de 0.5, as linhas preenchem a célula inteira. Uma solução completa leva line em direção ao
valor médio do padrão conforme fw cresce. A filtragem de texturas faz a mesma coisa em hardware: o nível de mipmap
que uma consulta de textura escolhe vem das derivadas das coordenadas dela.
Uma normal de graça
As derivadas também dão a orientação de uma superfície. As diferenças de posição no mundo para a direita e para cima são dois vetores ao longo da superfície; o produto vetorial deles é perpendicular a ela:
vec3 n = normalize(cross(dFdx(vWorldPos), dFdy(vWorldPos)));
O resultado é constante em cada triângulo, um visual facetado, e não precisa de normais na malha. Inverta o sinal se as faces saírem escuras: ele depende da convenção do eixo y.
Armadilhas
- Desvios. As derivadas comparam valores dentro do quad, então os quatro pixels precisam calcular o valor. Dentro
de um
ifcuja condição muda entre vizinhos, o resultado é indefinido. Calcule as derivadas, e as consultas de textura que dependem delas, antes do desvio. - Saltos. Um valor com uma descontinuidade, como
fract(x)na virada ou um ângulo deatanem ±π, tem uma derivada enorme ali, e um filtro baseado nela desenha uma emenda. Tire a derivada do valor suave antes do salto, como a grade acima faz comgem vez defract(g). Para um ângulo polart, que não tem versão suave, tire as derivadas dete defract(t + 0.5), fique com o par de menorfwidthe passe-o aotextureGrad. - Precisão. As derivadas são aproximadas: muitas vezes um único valor vale para o quad inteiro. Servem para filtragem e larguras de borda, não para contas exatas.