En términos totalmente técnicos, fwidth(p)
se define como
fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))
Y dFdx(p)
/ dFdy(p)
son los derivados parciales del valor p
con respecto a los x
y y
Dimensiones de la pantalla. Entonces denotan cómo se p
comporta el valor de cuando va un píxel a la derecha ( x
) o un píxel hacia arriba ( y
).
Ahora, ¿cómo se pueden calcular prácticamente? Bueno, si conoce los valores de los píxeles vecinos para p
, simplemente puede calcular esos derivados como diferencias finitas directas como una aproximación para sus derivados matemáticos reales (que podrían no tener una solución analítica exacta en absoluto):
dFdx(p) := p(x+1) - p(x)
Pero, por supuesto, ahora puede preguntarse, ¿cómo sabemos siquiera los valores de p
(que podrían ser cualquier valor calculado de forma arbitraria dentro del programa de sombreado) para los píxeles vecinos? ¿Cómo calculamos esos valores sin incurrir en una sobrecarga importante al hacer el cálculo del sombreador completo dos (o tres) veces?
Bueno, ya sabes qué, esos valores vecinos se calculan de todos modos, ya que para el píxel vecino también ejecutas un sombreador de fragmentos. Entonces, todo lo que necesita es acceder a esta invocación de sombreador de fragmentos adyacente cuando se ejecuta para el píxel vecino. Pero es aún más fácil, porque esos valores vecinos también se calculan exactamente al mismo tiempo.
Los rasterizadores modernos llaman sombreadores de fragmentos en mosaicos más grandes de más de un píxel vecino. Como mínimo, serían una cuadrícula de píxeles de 2x2. Y para cada bloque de píxeles, se invoca el sombreador de fragmentos para cada píxel y esas invocaciones se ejecutan en un paso de bloqueo perfectamente paralelo para que todos los cálculos se realicen exactamente en el mismo orden y al mismo tiempo para cada uno de esos píxeles en el bloque (razón por la cual también debe evitarse si es posible la ramificación en el sombreador de fragmentos, si bien no es mortal, ya que cada invocación de un bloque tendría que explorar cada rama tomada por al menos una de las invocaciones, incluso si simplemente se descarta los resultados posteriores, como también se aborda en las respuestas a esta pregunta relacionada) Entonces, en cualquier momento, un sombreador de fragmentos teóricamente tiene acceso a los valores del sombreador de fragmentos de sus píxeles vecinos. Y mientras usted no tiene acceso directo a esos valores, se tiene acceso a los valores calculados a partir de ellos, como las funciones derivadas dFdx
, dFdy
, fwidth
, ...
dFdx(p) = p(x1) - p(x)
, entoncesx1
puede ser cualquiera(x+1)
o(x-1)
, dependiendo de la posición del píxelx
en el quad. De cualquier manera,x1
tiene que estar en el mismo warp / wavefront quex
. ¿Estoy en lo correcto?