Derivados de la pantalla en el espacio de píxeles hacen drásticamente el rendimiento de impacto, pero su impacto en el rendimiento si los utiliza o no, por lo que desde un cierto punto de vista que están libres!
Cada GPU en la historia reciente empaqueta un quad de cuatro píxeles juntos y los coloca en el mismo warp / wavefront, lo que esencialmente significa que se ejecutan uno al lado del otro en la GPU, por lo que acceder a los valores desde ellos es muy barato. Debido a que las deformaciones / frentes de onda se ejecutan de manera sincronizada, los otros píxeles también estarán exactamente en el mismo lugar en el sombreador que usted, por lo que el valor de p
esos píxeles simplemente estará en un registro esperándolo. Estos otros tres píxeles siempre se ejecutarán, incluso si sus resultados se descartan. Entonces, un triángulo que cubre un solo píxel siempre sombreará cuatro píxeles y arrojará los resultados de tres de ellos, ¡solo para que estas características derivadas funcionen!
Esto se considera un costo aceptable (para el hardware actual) porque no se trata solo de funciones como las fwidth
que usan estos derivados: cada muestra de textura también lo hace, a fin de elegir qué mipmap de su textura leer. Considere: si está muy cerca de una superficie, la coordenada UV que está utilizando para muestrear la textura tendrá una derivada muy pequeña en el espacio de la pantalla, lo que significa que debe usar un mapa MIP más grande, y si está más lejos, la coordenada UV tendrá una derivada más grande en el espacio de la pantalla, lo que significa que debe usar un mapa MIP más pequeño.
En cuanto a lo que significa en términos menos matemáticos: fwidth
es equivalente a abs(dFdx(p)) + abs(dFdy(p))
. dFdx(p)
es simplemente la diferencia entre el valor de p
en el píxel x + 1 y el valor de p
en el píxel x, y de manera similar para dFdy(p)
.
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?dFdx
cada uno de los 2 píxeles vecinos en la cuadrícula de 2x2. Y este valor solo se calcula utilizando la diferencia entre los dos valores vecinos, si eso esp(x+1)-p(x)
op(x)-p(x-1)
solo depende de su noción de lo quex
está exactamente aquí. Sin embargo, el resultado es el mismo. Entonces sí, tienes razón.En términos totalmente técnicos,
fwidth(p)
se define comoY
dFdx(p)
/dFdy(p)
son los derivados parciales del valorp
con respecto a losx
yy
Dimensiones de la pantalla. Entonces denotan cómo sep
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):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
, ...fuente