Iluminación de Área Mejorada en WebGL y ThreeJS


He estado trabajando en una implementación de iluminación de área en WebGL similar a esta demostración:

Http://threejs.org/examples/webgldeferred_arealights.html

La implementación anterior en tres.js fue portado desde el trabajo de ArKano22 en gamedev.net:

Http://www.gamedev.net/topic/552315-glsl-area-light-implementation /

Aunque estas soluciones son muy impresionantes, ambas tienen algunas limitaciones. El problema principal con ArKano22 la implementación original es que el cálculo del término difuso no tiene en cuenta las normales de superficie.

He estado aumentando esta solución durante algunas semanas, trabajando con las mejoras de redPlant para abordar este problema. Actualmente tengo cálculos normales incorporados en la solución, PERO el resultado también es defectuoso.

Aquí está un adelanto de mi implementación actual:

área de iluminación teaser

Introducción

Los pasos para calcular el difuso el término para cada fragmento es el siguiente:

  1. Proyecta el vértice en el plano en el que se asienta la luz del área, de modo que el vector proyectado coincida con la dirección/normal de la luz.
  2. Compruebe que el vértice está en el lado correcto del plano ligero del área comparando el vector de proyección con el normal de la luz.
  3. Calcule el desplazamiento 2D de este punto proyectado en el plano desde el centro/posición de la luz.
  4. Sujete este vector de desplazamiento 2D para que se encuentra dentro del área de la luz (definida por su anchura y altura).
  5. Derive la posición del mundo 3D del punto 2D proyectado y sujeto. Este es el punto más cercano en la luz del área al vértice.
  6. Realice los cálculos difusos habituales que haría para una luz puntual tomando el producto escalar entre el vector vértice-a-punto más cercano (normalizado) y el vértice normal.

Problema

El problema con esta solución es que la iluminación los cálculos se realizan desde el punto más cercano y no tienen en cuenta otros puntos en la superficie de las luces que podrían iluminar el fragmento aún más. Permítanme tratar de explicar por qué {

Considere el siguiente diagrama:

situación problemática de la iluminación del área

La luz del área es perpendicular a la superficie y la interseca. Cada uno de los fragmentos en la superficie siempre devolverá un punto más cercano en la luz del área donde la superficie y la luz se cruzan. Desde el la superficie normal y los vectores de vértice a luz son siempre perpendiculares, el producto escalar entre ellos es cero. Posteriormente, el cálculo de la contribución difusa es cero a pesar de que existe una gran área de luz que se cierne sobre la superficie.

Solución potencial

Propongo que en lugar de calcular la luz desde el punto más cercano en la luz de área, la calculamos desde un punto en la luz de área que produce el mayor producto escalar entre el vector vértice-a-luz (normalizado) y el vértice normal. En el diagrama anterior, este sería el punto púrpura, en lugar del punto azul.

Ayuda!

Y así, aquí es donde necesito su ayuda. En mi cabeza, tengo una idea bastante buena de cómo se puede derivar este punto, pero no tengo la competencia matemática para llegar a la solución.

Actualmente tengo disponible la siguiente información en mi sombreador de fragmentos:

  • posición del vértice
  • vértice normal (vector unitario)
  • posición, anchura y altura de la luz
  • luz normal (vector unitario)
  • luz derecha (vector unitario)
  • iluminar (vector unitario)
  • punto proyectado desde el vértice sobre el plano de las luces (3D)
  • desplazamiento del punto proyectado desde el centro de las luces (2D)
  • desplazamiento de sujeción (2D)
  • posición mundial de este desplazamiento con abrazadera-el punto más cercano (3D)

Para poner toda esta información en un contexto visual, yo creado este diagrama (espero que ayude):

información de iluminación disponible

Para probar mi propuesta, necesito el punto de colada en el área de luz representada por los puntos rojos, de modo que pueda realizar el producto de punto entre el vértice a punto de colada (normalizado) y el vértice normal. Una vez más, esto debería producir el máximo valor de contribución posible.

ACTUALIZACIÓN!!!

He creado un boceto interactivo en CodePen que visualiza las matemáticas que tengo actualmente aplicada:

Http://codepen.io/wagerfield/pen/ywqCp

codepen

El código relavent en el que debes enfocarte es line 318.

castingPoint.location es una instancia de THREE.Vector3 y es la pieza faltante del rompecabezas. También debe notar que hay 2 valores en la parte inferior izquierda del croquis-estos se actualizan dinámicamente para mostrar el producto escalar entre los vectores relevantes.

Imagino que la solución requeriría otro pseudo plano que se alinea con la dirección del vértice normal Y es perpendicular al plano de la luz, pero podría estar equivocado!

Author: ROMANIA_engineer, 2013-06-10

5 answers

La buena noticia es que hay una solución; pero primero las malas noticias.

Su enfoque de usar el punto que maximiza el producto escalar es fundamentalmente defectuoso y no físicamente plausible.

En su primera ilustración anterior, suponga que su área de luz consistía solo en la mitad izquierda.

El punto "púrpura" the el que maximiza el producto escalar para la mitad izquierda is es el mismo que el punto que maximiza el producto escalar para ambas mitades combinado.

Por lo tanto, si se utilizara la solución propuesta, se concluiría que la mitad izquierda de la luz del área emite la misma radiación que toda la luz. Obviamente, eso es imposible.

La solución para calcular la cantidad total de luz que la luz del área proyecta en un punto dado es bastante complicada, pero como referencia, puede encontrar una explicación en el documento de 1994 El Jacobiano de Irradiancia para Fuentes Poliédricas Parcialmente Ocluidas aquí.

Le sugiero que mire la Figura 1, y algunos párrafos de la Sección 1.2 {y luego pare. :-)

Para que sea fácil, he codificado un sombreador muy simple que implementa la solución utilizando los tres.js WebGLRenderer not no el diferido.

EDITAR: Aquí está un violín actualizado: http://jsfiddle.net/hh74z2ft/1 /

introduzca la descripción de la imagen aquí

El núcleo del sombreador de fragmentos es bastante simple

// direction vectors from point to area light corners

for( int i = 0; i < NVERTS; i ++ ) {

    lPosition[ i ] = viewMatrix * lightMatrixWorld * vec4( lightverts[ i ], 1.0 ); // in camera space

    lVector[ i ] = normalize( lPosition[ i ].xyz + vViewPosition.xyz ); // dir from vertex to areaLight

}

// vector irradiance at point

vec3 lightVec = vec3( 0.0 );

for( int i = 0; i < NVERTS; i ++ ) {

    vec3 v0 = lVector[ i ];
    vec3 v1 = lVector[ int( mod( float( i + 1 ), float( NVERTS ) ) ) ]; // ugh...

    lightVec += acos( dot( v0, v1 ) ) * normalize( cross( v0, v1 ) );

}

// irradiance factor at point

float factor = max( dot( lightVec, normal ), 0.0 ) / ( 2.0 * 3.14159265 );

Más Bueno Noticias:

  1. Este enfoque es físicamente correcto.
  2. La atenuación se maneja automáticamente. (Tenga en cuenta que las luces más pequeñas requerirán un valor de intensidad más grande. )
  3. En teoría, este enfoque debería funcionar con polígonos arbitrarios, no solo rectangulares.

Advertencias:

  1. Solo he implementado el componente difuso, porque eso es lo que su pregunta aborda.
  2. Tendrá que implementar el componente especular utilizando un heurística razonable similar similar a lo que ya has codificado, supongo.
  3. Este simple ejemplo no maneja el caso donde la luz del área está "parcialmente debajo del horizonte" i es decir, no todos los 4 vértices están por encima del plano de la cara.
  4. Dado que WebGLRenderer no admite luces de área, no puede "agregar la luz a la escena" y esperar que funcione. Esta es la razón por la que paso todos los datos necesarios en el sombreador personalizado. (WebGLDeferredRenderer soporta luces de área, por supuesto. )
  5. Las sombras no son apoyar.

Tres.js r. 73

 39
Author: WestLangley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-23 17:56:10

Hm. Pregunta extraña! Parece que usted comenzó con una aproximación muy específica y ahora está trabajando su camino hacia atrás a la solución correcta.

Si nos atenemos a solo difuso y una superficie que es plana (tiene solo una normal) ¿cuál es la luz difusa entrante? Incluso si nos atenemos a cada luz entrante tiene una dirección e intensidad, y solo tomamos allin = integral (lightin) ((lightin).(normal))*light esto es duro. así que todo el problema es resolver esta integral. con luz de punto haces trampa haciendo una suma y sacando la luz. Eso funciona bien para luces de punto sin sombras, etc. ahora lo que realmente quieres hacer es resolver esa integral. eso es lo que se puede hacer con algún tipo de sondas de luz, armónicos esféricos o muchas otras técnicas. o algunos trucos para estimar la cantidad de luz de un rectángulo.

Para mí siempre ayuda pensar en el hemisferio por encima del punto que desea iluminar. Necesitas que entre toda la luz. Algunos son menos importantes, algunos mas. Para eso es lo normal. En un raytracer de producción solo podrías probar unos pocos miles de puntos y tener una buena suposición. En tiempo real tienes que adivinar mucho más rápido. Y eso es lo que hace su código de biblioteca: Una opción rápida para una buena (pero defectuosa) suposición.

Y ahí es donde creo que estás retrocediendo: Te diste cuenta de que están haciendo una suposición, y que a veces apesta (esa es la naturaleza de adivinar). Ahora, no trate de arreglar su conjetura,pero llegar a una mejor! Y tal vez tratar de entender por qué eligieron esa suposición. Una buena aproximación no se trata de ser bueno en casos de esquina, sino en degradar bien. Eso es lo que este parece para mí. (De nuevo, lo siento, soy demasiado perezoso para leer los tres.código js ahora).

Así que para responder a su pregunta:

  • Creo que lo estás haciendo de la manera equivocada. Usted está comenzando con una idea altamente optimizada, y está tratando de arreglar eso. Es mejor empezar desde el problema.
  • Resolver una cosa a la vez. Su captura de pantalla tiene una gran cantidad de especular, que no está relacionado con su problema, pero muy visual y fue probablemente una gran influencia para las personas que diseñan el modelo.
  • Usted está en el camino correcto y tiene una idea mucho mejor sobre el renderizado que la mayoría de la gente. Eso puede funcionar a favor y en contra de ti. Lea sobre algunos motores de juegos modernos y sus modelos de iluminación. Siempre encontrará una fascinante combinación de hacks y un profundo entendimiento. La comprensión profunda es lo que impulsa a elegir los hacks correctos :)

Espero que esto ayude. Podría estar totalmente equivocado aquí y divagando a alguien que solo está buscando un poco de matemáticas rápidas, en ese caso me disculpo.

 2
Author: starmole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-16 06:46:11

Acordemos que el punto de lanzamiento está siempre en el borde.

Digamos que "parte iluminada" es la parte del espacio que está representada por el cuadrante de luz extruida a lo largo de su normal.

Si el punto de superficie se encuentra en la parte iluminada, entonces necesita calcular el plano que sostiene ese punto, es un vector normal y la luz es normal. La intersección entre ese plano y la luz te daría dos puntos como opciones (solo dos, porque el punto de lanzamiento siempre está en el borde). Así que prueba a esos dos para ver cuál contribuye más.

Si el punto no está en la parte iluminada, entonces podría calcular cuatro planos, cada uno tiene punto de superficie, su normal y uno de los vértices del quad de la luz. Para cada vértice light-quad tendría dos puntos (vértice + un punto de intersección más) para probar cuál contribuye más.

Esto debería hacer el truco. Por favor, dame retroalimentación si encuentras cualquier contraejemplo.

 1
Author: Abstract Algorithm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-10 13:24:06

Http://s3.hostingkartinok.com/uploads/images/2013/06/9bc396b71e64b635ea97725be8719e79.png

Si he entendido bien:

Definir L "Luz para el punto x0"

L ~ K / S^2

S = sqrt (y^2+x0^2)

L = suma (k/(sqrt (y^2+x0^2))^2), y = 0..infinity

L = suma (k / (y^2+x0^2)), y=0..infinito, x > 0, y > 0

L = integral (k / (y^2+x0^2)), y = 0..infinito = k * Pi / (2 * x0)

Http://s5.hostingkartinok.com/uploads/images/2013/06/6dbb7b6d3babc092d3daf18bb3c6e6d5.png

Respuesta:

L = k * Pi / (2 * x0)

k depende del entorno

 1
Author: user2471050,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-10 13:33:08

Ha pasado un tiempo, pero hay un artículo en gpu gems 5 que usa" el punto más importante "en lugar del" punto más cercano " para aproximar la integral de iluminación para las luces de área:

Http://gpupro.blogspot.com/2014/03/gpu-pro-5-physically-based-area-lights.html

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-17 16:13:35