¿Cuál es la utilidad del radio cuadrado y el radio cuadrado inverso para los cálculos de iluminación?

16

En una de las diapositivas de PowerPoint "Representación de DirectX 11 en Battlefield 3", noté el siguiente código:

struct Light {
    float3 pos; float sqrRadius;
    float3 color; float invSqrRadius;
}

No entiendo por qué almacenarían el radio cuadrado e incluso el cuadrado inverso (que creo que es simplemente un radio de 1 cuadrado) en lugar de simplemente almacenar el radio. ¿Cómo están utilizando estos datos en sus cálculos? Además, ¿qué pasa con las luces de cono y línea? Esta estructura debe ser solo para las luces de punto, no puedo ver que funcione para otros tipos, no hay suficientes datos. Aún así, me encantaría saber cómo usan ese cuadrado e invSquare.

ACTUALIZACIÓN: Ok, finalmente lo entendí.

Aquí está la ecuación clásica de atenuación de luz, que se encuentra fácilmente en la red:

float3 lightVector = lightPosition - surfacePosition;

float attenuation = saturate(1 - length(lightVector)/lightRadius);

Es relativamente costoso ya length(lightVector)que en realidad está haciendo esto:

length(lightVector) = sqrt(dot(lightVector, lightVector);

Además, la operación de división (/lightRadius)también es bastante costosa.

En lugar de calcular la atenuación de la luz de esta manera, puede calcularla de la siguiente manera, lo que sería mucho más rápido:

attenuation = saturate(1 - dot(lightVector, lightVector)*invRadiusSqr);

donde invRadiusSqr se puede calcular previamente a nivel de CPU y pasar como una constante de sombreador.

Además, obtienes una atenuación de luz cuadrática como resultado (en lugar de lineal en el primer caso), lo que es aún mejor, ya que la luz IRL ha demostrado tener una caída cuadrática.

¡Gracias a todos por su ayuda!

cubrman
fuente
13
Relleno. Los sombreadores están alineados a 16bytes. Y si observa cómo se formatea el código, verá que se empaquetará en dos flotantes4. ¿Por qué no almacenar algo útil cuando lo obtendrá de forma gratuita del caché?
Tordin

Respuestas:

23

Esto es simplemente un tipo de optimización dado que invSqrRadius = 1/SqrRadius, en lugar de calcular el radio cuadrado inverso para cada luz cada vez que simplemente lo almacenan en caché, la razón es que la división suele ser una operación "lenta" al menos en comparación con la multiplicación.

Esta optimización es relevante especialmente:

  • cuando la operación se realiza una gran cantidad de veces para cada luz, por lo que el almacenamiento en caché del valor liberará ciclos adicionales de CPU / GPU
  • Y suponiendo que el tiempo de acceso a la memoria para leer el valor es realmente más rápido que volver a calcularlo.

Con respecto a cómo se usa, no estoy seguro acerca de su implementación específica, pero con respecto a 1/sqrRadiusesto, esto simplemente se usa para atenuación de luz, caída y sacrificio. También es relevante para direccional y foco, la única diferencia en caso de foco es que necesita calcular el factor de foco después de aplicar la atenuación . Con respecto a las luces direccionales como el sol, generalmente no tiene atenuación ni caída, por lo que supongo que se ignorará.

[EDIT] Sólo para elaborar más, es que no los datos irrelevantes. La irradiancia de la luz se puede calcular utilizando la siguiente ecuación:

E = Phi / 4 * pi * rSqr;

Dónde

E es la densidad de área del flujo.

Phi es flujo de resplandor.

4 * pi * rSqr es el área de superficie de una esfera.

Esta ecuación explica por qué la cantidad de energía recibida disminuye con la distancia al cuadrado.

Otro punto es que necesita calcular la distancia entre el vértice y la luz para calcular la contribución de la luz en un vértice específico (es poco probable que este valor se almacene en caché), el vértice puede estar dentro o fuera del rango de luz, lo que nos lleva al siguiente punto donde Radius Squarees útil para sacrificar.

Si desea un ejemplo práctico para calcular la caída de luz y el sacrificio, esto es especialmente útil en renderizadores diferidos basados ​​en mosaicos, aquí hay un ejemplo .

concepto3d
fuente
¡Maldiciones, me has vencido por 7 segundos! ;) (¡y con una respuesta más completa también!)
Trevor Powell
Gracias por el comentario detallado, especialmente por el enlace! Por lo que entendí del último enlace, Battlefield 3 almacena no el radio sino la distancia real entre la fuente de luz y el receptor de luz, ¿verdad? Ese es el valor "d" que usan en el artículo.
cubrman
@cubrman es difícil especular sin ver el código. Mi conjetura es que es el radio inversoSqr. Y las ecuaciones que usan pueden variar mucho del artículo.
concept3d
Pero dime, ¿cómo puedes usar un radio de luz invertido al cuadrado en un cálculo de iluminación? Cada fuente que encontré en la red me dice que necesito encontrar la DISTANCIA entre la superficie receptora y la fuente de luz, dividir esta última por el radio de luz original Y LUEGO cuadrar el resultado. ¿Dónde usarías el radio cuadrado o invSqrRadius? Eso me parece datos completamente irrelevantes.
cubrman
1
@cubrman actualizó la respuesta.
concept3d
6

invSqrRadius no es 1 - sqrRadius; es 1 / sqrRadius.

Significa que puede multiplicar por invSqrRadius, en lugar de dividir por sqrRadius (ya que la división suele ser mucho más costosa que la multiplicación)

Trevor Powell
fuente
6

Las otras respuestas aquí trataron con el radio cuadrado inverso, pero voy a ver el radio cuadrado en su lugar (que concept3d mencionó, pero creo que merece más discusión).

Para qué son útiles los cuadrados son las comparaciones de distancia. Sabemos que calcular la distancia entre dos puntos implica una raíz cuadrada, y las raíces cuadradas son caras de calcular, pero si todo lo que queremos hacer es comparar distancias (para encontrar cuál es menor o mayor, y hacer algo interesante basado en el resultado) podemos tirar la raíz cuadrada.

Si sqrt (x)> sqrt (y), entonces también es el caso de que x> y.

Para una luz, el radio al cuadrado es el mismo que la distancia entre el centro de la luz y su extensión máxima, al cuadrado, por supuesto.

Para los cálculos de iluminación, esto se puede usar para un caso de salida anticipada. Si la distancia entre el punto que está iluminando y el centro de la luz (al cuadrado) es mayor que el radio al cuadrado, el punto no recibe luz y no necesita ejecutar el resto de sus cálculos. Por lo tanto, esto es solo una optimización (bastante común): podemos usar el radio cuadrado para hacer la comparación de distancia sin costosas raíces cuadradas, y a un costo de solo una resta y un producto de puntos.

Yo, por supuesto, no sé si esto es exactamente para lo que BF3 lo está usando, pero esperaría que no esté demasiado lejos de la marca.

Maximus Minimus
fuente
Entonces, si te entendí correctamente, el código será: if (dot ((lightPos - surfacePos), (lightPos - surfacePos))> lightRadiusSqr) no hace la iluminación, ¿verdad?
cubrman