three detector descargar math 3d three.js webgl lighting

math - detector - webgl 3d



Iluminación de área mejorada en WebGL y ThreeJS (5)

Aceptemos que el punto de lanzamiento siempre está al límite.

Digamos que la "parte iluminada" es la parte del espacio que está representada por el cuadrante de la 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 contiene ese punto, es vector normal y la luz es normal. La intersección entre ese plano y la luz le daría dos puntos como opciones (solo dos, porque el punto de lanzamiento siempre está en el borde). Así que prueba esos dos para ver cuál contribuye más.

Si el punto no está en la parte iluminada, entonces puedes calcular cuatro planos, cada uno tiene un punto de superficie, es normal y uno de los vértices del cuadrante de la luz. Para cada vértice cuádruple ligero, tendría dos puntos (vértice + un punto de intersección más) para evaluar, que son los que más contribuyen.

Esto debería funcionar. Por favor, dame tu opinión si encuentras algún contraejemplo.

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 three.js fue portada del 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 principal problema con la implementación original de ArKano22 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í hay un adelanto de mi implementación actual:

Introducción

Los pasos para calcular el término difuso para cada fragmento son los siguientes:

  1. Proyecte el vértice en el plano en el que se encuentra la luz del área, de modo que el vector proyectado coincida con la dirección / normal de la luz.
  2. Verifique que el vértice esté en el lado correcto del plano de luz del área comparando el vector de proyección con la luz normal.
  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 asiente dentro del área de la luz (definida por su ancho y alto).
  5. Derive la posición mundial 3D del punto 2D proyectado y sujeto. Este es el punto más cercano en el área de luz al vértice.
  6. Realice los cálculos difusos habituales que haría para una luz de punto tomando el producto de puntos entre el vector de vértice a punto más cercano (normalizado) y el vértice normal.

Problema

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

Considere el siguiente diagrama:

La luz del área es perpendicular a la superficie y la intersecta. 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. Como los vectores de superficie normal y de vértice a luz son siempre perpendiculares, el producto de puntos entre ellos es cero. Posteriormente, el cálculo de la contribución difusa es cero a pesar de que hay una gran área de luz que se cierne sobre la superficie.

Solucion potencial

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

¡Ayuda!

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

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

  • posición del vértice
  • vértice normal (unidad de vector)
  • posición de luz, ancho y alto
  • luz normal (unidad de vector)
  • luz derecha (unidad de vector)
  • iluminarse (vector unitario)
  • punto proyectado desde el vértice hacia el plano de las luces (3D)
  • desplazamiento del punto proyectado desde el centro de las luces (2D)
  • offset con abrazadera (2D)
  • posición mundial de esta compensación fija: el punto más cercano (3D)

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

Para probar mi propuesta, necesito el punto de lanzamiento en la luz de área, representada por los puntos rojos, para poder realizar el producto de puntos entre el vértice a punto de lanzamiento (normalizado) y el vértice normal. De nuevo, esto debería producir el máximo valor de contribución posible.

¡¡¡ACTUALIZAR!!!

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

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

El código de relavent en el que debe enfocarse es la línea 318 .

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

Me imagino que la solución requeriría otro pseudo plano que se alinee con la dirección del vértice normal Y sea perpendicular al plano de la luz, ¡pero podría estar equivocado!


Hm. ¡Extraña pregunta! Parece que comenzó con una aproximación muy específica y ahora está trabajando hacia atrás en la solución correcta.

Si nos limitamos a difusa 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 simplemente tomamos allin = integral (lightin) ((lightin). (Normal)) * luz esto es difícil. entonces todo el problema es resolver esta integral. con la luz puntual se hace trampa convirtiéndola en una suma y sacando la luz. Eso funciona bien para luces puntuales sin sombras, etc. ahora lo que realmente quieres hacer es resolver esa integral. eso es lo que puede hacer con algún tipo de sondas de luz, armónicos esféricos u otras muchas técnicas. o algunos trucos para estimar la cantidad de luz de un rectángulo.

Para mí, siempre ayuda a pensar en el hemisferio por encima del punto que desea iluminar. Necesitas que entre toda la luz. Algunos son menos importantes, algunos más. Para eso es tu normalidad. En un raytracer de producción, podría simplemente muestrear algunos miles de puntos y tener una buena idea. En tiempo real, tienes que adivinar mucho más rápido. Y eso es lo que hace tu código de biblioteca: una elección rápida para una buena (pero defectuosa) suposición.

Y ahí es donde creo que retrocedes: te das cuenta de que están adivinando, y que apesta a veces (esa es la naturaleza de adivinar). Ahora, no trates de arreglar su suposición, ¡pero encuentra una mejor! Y tal vez intente comprender por qué escogieron esa suposición. Una buena aproximación no se trata de ser bueno en los casos de esquina sino en degradarse bien. Eso es lo que me parece a mí. (De nuevo, lo siento, me da pereza leer el código three.js ahora).

Entonces para responder a tu pregunta:

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

Espero que esto ayude. Podría estar totalmente equivocado aquí y divagando con alguien que solo está buscando matemática rápida, en ese caso me disculpo.


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

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

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

El punto "violeta" - el que maximiza el producto de puntos para la mitad izquierda - es el mismo que maximiza el producto de puntos para ambas mitades combinadas.

Por lo tanto, si uno fuera a usar su solución propuesta, uno 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 arroja la luz del área en un punto dado es bastante complicada, pero como referencia, puede encontrar una explicación en el artículo de 1994 The Irradiance Jacobian para Sources poliedros parcialmente ocluidos here .

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

Para hacerlo más fácil, he codificado un sombreador muy simple que implementa la solución usando el WebGLRenderer WebGLRenderer , no el diferido.

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

El núcleo del fragmento shader 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 buenas 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 mayor valor de intensidad).
  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 a lo que se dirige tu pregunta.
  2. Tendrás que implementar el componente especular usando una heurística razonable, similar a lo que ya has codificado, espero.
  3. Este simple ejemplo no maneja el caso donde la luz del área está "parcialmente debajo del horizonte", es decir, no todos los 4 vértices están por encima del plano de la cara.
  4. Como WebGLRenderer no admite luces de área, no puede "agregar la luz a la escena" y esperar que funcione. Es por eso que paso todos los datos necesarios al sombreador personalizado. ( WebGLDeferredRenderer sí admite luces de área, por supuesto).
  5. Las sombras no son compatibles.

three.js r.73



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

Si entiendo correctamente:

define 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..infinity, x> 0, y> 0

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

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

Responder:

L = k * Pi / (2 * x0)

k depende del medio ambiente