¿Qué es fwidth y cómo funciona?

18

La documentación de OpenGL establece que fwidth returns the sum of the absolute value of derivatives in x and y.

¿Qué significa esto en términos menos matemáticos, y hay una manera de visualizarlo?

Según mi comprensión de la función, fwidth(p)tiene acceso al valor de pen píxeles vecinos. ¿Cómo funciona esto en la GPU sin afectar drásticamente el rendimiento, y funciona de manera confiable y uniforme en todos los píxeles?

ApoorvaJ
fuente

Respuestas:

18

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 pesos 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 fwidthque 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: fwidthes equivalente a abs(dFdx(p)) + abs(dFdy(p)). dFdx(p)es simplemente la diferencia entre el valor de pen el píxel x + 1 y el valor de pen el píxel x, y de manera similar para dFdy(p).

John Calsbeek
fuente
Entonces, si dFdx(p) = p(x1) - p(x), entonces x1puede ser cualquiera (x+1)o (x-1), dependiendo de la posición del píxel xen el quad. De cualquier manera, x1tiene que estar en el mismo warp / wavefront que x. ¿Estoy en lo correcto?
ApoorvaJ
3
@ApoorvaJ Esencialmente se calcula el mismo valor para dFdxcada 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 es p(x+1)-p(x)o p(x)-p(x-1)solo depende de su noción de lo que xestá exactamente aquí. Sin embargo, el resultado es el mismo. Entonces sí, tienes razón.
Chris dice que reinstala a Mónica el
@ChristianRau Eso responde a mi pregunta. Gracias.
ApoorvaJ
11

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 pcon respecto a los xy yDimensiones de la pantalla. Entonces denotan cómo se pcomporta 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, ...

Chris dice reinstalar a Mónica
fuente