Em termos inteiramente técnicos, fwidth(p)
é definido como
fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))
E dFdx(p)
/ dFdy(p)
são as derivadas parciais do valor p
em relação às dimensões x
e da y
tela. Portanto, eles denotam como o valor de p
se comporta ao passar um pixel para a direita ( x
) ou um pixel para cima ( y
).
Agora, como eles podem ser praticamente computados? Bem, se você conhece os valores dos pixels vizinhos p
, pode apenas computar essas derivadas como diferenças finitas diretas como uma aproximação para suas derivadas matemáticas reais (que podem não ter uma solução analítica exata):
dFdx(p) := p(x+1) - p(x)
Mas é claro que agora você pode perguntar: como sabemos os valores de p
(que depois poderiam ser qualquer valor arbitrariamente calculado dentro do programa shader) para os pixels vizinhos? Como calculamos esses valores sem incorrer em grandes despesas gerais, fazendo todo o cálculo do shader duas (ou três) vezes?
Bem, você sabe, esses valores vizinhos são calculados de qualquer maneira, já que para o pixel vizinho você também executa um shader de fragmento. Portanto, tudo o que você precisa é de acesso a essa chamada de shader de fragmento vizinho quando executado para o pixel vizinho. Mas é ainda mais fácil, porque esses valores vizinhos também são calculados ao mesmo tempo.
Os rasterizadores modernos chamam shaders de fragmentos em blocos maiores de mais de um pixel vizinho. No menor, esses seriam uma grade de pixels 2x2. E para cada um desses blocos de pixels, o sombreador de fragmento é chamado para cada pixel e essas invocações são executadas em uma etapa de bloqueio perfeitamente paralela, de modo que todos os cálculos sejam feitos exatamente na mesma ordem e no mesmo momento para cada um desses pixels no bloco (é também por isso que a ramificação no shader de fragmento, embora não seja mortal, deve ser evitada, se possível, pois cada invocação de um bloco deve explorar todos os ramos que são capturados por pelo menos uma das invocações, mesmo que apenas jogue fora os resultados posteriormente, como também foi abordado nas respostas a esta pergunta relacionada) Portanto, a qualquer momento, um shader de fragmento teoricamente tem acesso aos valores do shader de fragmento dos pixels vizinhos. E enquanto você não tem acesso direto a esses valores, você tem acesso a valores computados a partir deles, como as funções de derivativos dFdx
, dFdy
, fwidth
, ...
dFdx(p) = p(x1) - p(x)
, entãox1
pode ser um(x+1)
ou(x-1)
, dependendo da posição do pixelx
no quad. De qualquer maneira,x1
deve estar no mesmo warp / wavefront quex
. Estou correcto?