59 votes

Éclairage de zone amélioré dans WebGL et ThreeJS

J'ai été de travailler sur la zone d'éclairage de mise en œuvre de WebGL similaire à cette démo:

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

Ci-dessus la mise en œuvre de three.js a été porté à partir du travail de ArKano22 plus sur gamedev.net:

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

Bien que ces solutions sont très impressionnants, ils ont tous les deux un peu de limitations. Le principal problème avec ArKano22 origine de la mise en œuvre est que le calcul de la diffus terme ne prend pas en compte les normales de la surface.

J'ai été augmentant cette solution pour quelques semaines maintenant, en travaillant avec les améliorations apportées par redPlant pour remédier à ce problème. Actuellement, j'ai la normale calculs intégrés dans la solution, MAIS le résultat est aussi défectueuse.

Voici un petit aperçu de mon actuel de mise en œuvre:

area lighting teaser

Introduction

Les étapes de calcul de la diffus terme pour chaque fragment est comme suit:

  1. Projet le sommet sur le plan de l'éclairage de l'espace de siège, de sorte que la projection du vecteur coïncide avec la lumière normale/direction.
  2. Vérifiez que le sommet est sur le bon côté de l'éclairage de l'espace de l'avion par la comparaison de la projection du vecteur avec la lumière normale.
  3. Calculer la 2D décalage de ce point projeté sur le plan de la lumière du centre/de position.
  4. Pince 2D décalage vecteur de sorte qu'il se trouve à l'intérieur de la lumière de la zone (définie par sa largeur et sa hauteur).
  5. Tirer le monde de la 3D de la position de la projection et serré 2D point. C'est le point le plus proche sur la zone de lumière vers le sommet.
  6. Effectuer l'habitude diffuse les calculs que vous le feriez pour un point de lumière en prenant le produit scalaire entre le sommet-de-plus-proche-point du vecteur (normalisé) et le sommet de la normale.

Problème

Le problème avec cette solution est que l'éclairage calculs sont effectués à partir du point le plus proche et ne tiennent pas compte d'autres points sur les lumières de la surface qui pourrait être éclairant le fragment d'autant plus. Permettez-moi d'essayer d'expliquer pourquoi...

Considérons le schéma suivant:

problematic area lighting situation

La zone de lumière est à la fois perpendiculaire à la surface et croise. Chacun des fragments sur la surface toujours en revenir à un point plus proche sur la zone de lumière, lorsque la surface et la lumière se croisent. Depuis la normale à la surface et le sommet à la lumière des vecteurs sont toujours perpendiculaires, le produit scalaire entre eux est égal à zéro. Par la suite, le calcul de diffuser la contribution est égale à zéro malgré l'existence d'une vaste zone de lumière qui plane au-dessus de la surface.

Solution Potentielle

Je propose que, plutôt que de calculer la lumière à partir du point le plus proche sur la zone de lumière, nous calculons à partir d'un point sur la zone de la lumière qui donne le plus grand produit scalaire entre le vertex à la lumière de vecteur (normalisé) et le sommet de la normale. Dans le diagramme ci-dessus, ce serait le violet dot, plutôt que le point bleu.

À l'aide!

Et donc, c'est là que j'ai besoin de votre aide. Dans ma tête, j'ai une assez bonne idée de la façon dont ce point peut être dérivée, mais n'ont pas la compétence mathématique pour arriver à la solution.

Actuellement, j'ai les informations suivantes disponibles dans mon fragment shader:

  • vertex position
  • vertex normal (vecteur unitaire)
  • la position de la lumière, de la largeur et de la hauteur
  • la lumière normale (vecteur unitaire)
  • la lumière (vecteur unitaire)
  • la lumière (vecteur unitaire)
  • projeté point à partir du sommet sur les lumières de l'avion (3D)
  • projeté décalage du point de feux center (2D)
  • serré offset (2D)
  • mondiale de la position de cette serrées offset – le plus proche point (3D)

Pour mettre toutes ces informations dans un contexte visuel, j'ai créé ce schéma (j'espère que ça aide):

available lighting information

Pour tester ma proposition, j'ai besoin de la coulée point sur la zone de lumière représenté par les points rouges, afin que je puisse effectuer le produit scalaire entre le vertex-à-casting-point (normalisé) et le sommet de la normale. Là encore, le rendement maximum de la contribution possible de la valeur.

Mise à JOUR!!!

J'ai créé un interactif de dessin sur sur CodePen permettant de visualiser les mathématiques que j'ai actuellement mis en œuvre:

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

codepen

Le relavent code que vous devez vous concentrer sur est ligne 318.

castingPoint.location est une instance de l' THREE.Vector3 et est la pièce manquante du puzzle. Vous devez également remarquer qu'il y a 2 valeurs dans la partie inférieure gauche de l'esquisse – ces sont mis à jour dynamiquement pour afficher le produit scalaire entre les vecteurs.

J'imagine que la solution nécessiterait un autre pseudo avion qui s'aligne avec la direction de la normale de sommet ET qui est perpendiculaire à la lumière de l'avion, mais je peux me tromper!

De toute façon, j'espère que certains de compassion génie peut m'aider à résoudre ce!

Merci beaucoup d'avance :D

40voto

WestLangley Points 28444

La bonne nouvelle est qu'il existe une solution; mais d'abord la mauvaise nouvelle.

Votre approche de l'utilisation du point qui maximise le produit scalaire est viciée à la base, et non pas physiquement plausible.

Dans votre première illustration ci-dessus, supposons que votre zone de lumière se composait de seulement la moitié gauche.

Le "purple" -- celui qui maximise le produit scalaire pour la moitié gauche -- est le même que le point qui maximise le produit scalaire de deux moitiés réunies.

Par conséquent, si l'on devait utiliser votre solution proposée, on pourrait conclure que la moitié gauche de la zone de lumière émet de la même rayonnement que l'ensemble de la lumière. Évidemment, c'est impossible.

La solution pour le calcul de la quantité totale de lumière que la zone jette la lumière sur un point donné est plutôt compliqué, mais pour la référence, vous pouvez trouver une explication dans l'1994 papier , L'Éclairement énergétique Jacobien Partiellement Occlus Polyédriques Sources ici.

Je vous suggère de regarder à la Figure 1, et quelques paragraphes de la Section 1.2 -- et puis s'arrêter. :-)

Pour faire simple, j'ai codé un très simple shader qui implémente la solution à l'aide de la three.js WebGLRenderer -- pas reportés.

Voici un violon: http://jsfiddle.net/Us54P/

enter image description here

La base du fragment shader est assez 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 );

Plus De Bonnes Nouvelles:

  1. Cette approche est physiquement correct.
  2. L'atténuation est géré automatiquement. ( Sachez que les petites lumières exigera une plus grande valeur d'intensité. )
  3. En théorie, cette approche devrait travailler avec l'arbitraire des polygones, et pas seulement rectangulaires.

Mises en garde:

  1. J'ai seulement mis en œuvre la composante diffuses, parce que c'est ce que votre question adresses.
  2. Vous aurez à mettre en œuvre le composant spéculaire à l'aide d'un raisonnable heuristique -- semblable à ce que vous avez déjà codé, j'attends.
  3. Ce simple exemple ne gère pas le cas où la zone de la lumière est "partiellement au-dessous de l'horizon" -- c'est à dire pas tous les 4 sommets sont au-dessus du plan de la face.
  4. Depuis WebGLRenderer ne prend pas en charge les feux de zone, vous ne pouvez pas ajouter de la lumière à la scène" et espérer qu'il fonctionne. C'est pourquoi je passe toutes les données nécessaires dans le shader personnalisée. ( WebGLDeferredRenderer prend en charge les feux de zone de, bien sûr. )
  5. Les ombres ne sont pas pris en charge.

three.js r.58

2voto

starmole Points 2160

Hm. Étrange question! Il semble que vous avez commencé avec un très spécifique rapprochement et sont maintenant à travailler votre chemin vers l'arrière vers la bonne solution.

Si nous nous en tenons à ne diffuser et d'une surface à plat (n'a qu'un normal) qu'est-ce que les entrants de diffuser de la lumière? Même si nous nous en tenons à tous la lumière a une direction et d'intensité, et nous venons de prendre allin = intégrale(lightin) ((lightin).(normal))*lumière c'est dur. donc tout le problème est la résolution de cette intégrale. avec la lumière de point de vous tricher en faisant une somme et en tirant la lumière. Cela fonctionne bien pour les points de lumières sans ombres etc. maintenant, ce que vous voulez vraiment, c'est pour résoudre cette intégrale. c'est ce que vous pouvez faire avec une sorte de lumière, des sondes, des harmoniques sphériques ou de nombreuses autres techniques. ou quelques astuces pour estimer la quantité de lumière à partir d'un rectangle.

Pour moi, il est toujours utile de penser à l'hémisphère au-dessus de l'endroit où vous voulez à la lumière. Vous avez besoin de toute la lumière venant. Certains sont moins importantes, un peu plus. C'est ce normal de votre est. Dans une production raytracer vous pouviez juste de prélever un échantillon de quelques milliers de points et avoir une bonne estimation. En temps réel, vous devez deviner beaucoup plus vite. Et c'est ce que votre bibliothèque de code: Un choix rapide pour une bonne (mais erronée) suppose.

Et c'est là que je pense que vous allez à l'envers: Vous avez réalisé qu'ils font une supposition, et qu'il suce (qui est la nature de deviner). Maintenant, n'essayez pas de réparer leurs deviner, mais venir avec une meilleure! Et peut-être essayer de comprendre pourquoi ils ont choisi que deviner. Une bonne approximation est pas sur d'être bon au coin des cas, mais en matière de dégradation du bien. C'est à quoi ça ressemble pour moi. (Encore une fois, désolé, je suis trop paresseux pour lire la three.js actuellement, le code).

Donc, pour répondre à votre question:

  • Je pense que vous allez à ce sujet dans le mauvais sens. Vous commencez avec une très optimisé idée, et tentent de résoudre ce problème. Il vaut mieux commencer par le problème.
  • Résoudre une chose à la fois. Votre capture d'écran a beaucoup de spéculaire, qui n'est pas lié à votre problème, mais très visuelle, et a probablement été une grande influence pour les gens de la conception du modèle.
  • Vous êtes sur la bonne voie et d'avoir une meilleure idée sur le rendu que la plupart des gens. Qui peuvent travailler pour ou contre vous. Lire sur certains modernes et les moteurs de jeu et de leurs modèles d'illumination. Vous trouverez toujours une combinaison fascinante de hacks et de compréhension profonde. La compréhension profonde est ce qui motive le choix de la bonne hacks :)

Espérons que cette aide. Je suis peut-être totalement faux et de randonnée à quelqu'un qui est simplement à la recherche pour certains petit calcul rapide, dans ce cas je m'en excuse.

1voto

Abstract Algorithm Points 2525

Admettons que la coulée est toujours sur le bord.

Disons que "lit part" est la partie de l'espace qui est représenté par extrudé lumière du quad le long de sa normale.

Si le point de la surface se trouve dans la partie éclairée, alors vous avez besoin pour calculer l'avion qui détient ce point, il est normal de vecteur et de la lumière normale. Intersection entre l'avion et de la lumière serait vous donner deux points en option (seulement deux, parce que la coulée est toujours sur le bord). Afin de tester les deux pour voir lequel contribue plus.

Si le point n'est pas dans le lit de la partie, alors vous pouvez calculer les quatre avions, a chaque point de la surface, il est normal et l'un des sommets de la lumière du quad. Pour chaque lumière-quad sommet, vous disposez de deux points (vertex + un de plus point d'intersection) pour tester ce qui contribue le plus.

Ceci devrait faire l'affaire. Merci de me donner vos commentaires si vous rencontrez un contre-exemple.

1voto

user2471050 Points 1

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

Si je comprends bien:

définir L "Lumière pour le point x0"

L ~ K/S^2

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

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

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

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

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

Réponse:

L = k*Pi/(2*x0)

k dépend de l'environnement

1voto

racarate Points 310

cela fait longtemps, mais il existe un article dans gpu gems 5 qui utilise "le point le plus important" plutôt que le "point le plus proche" pour se rapprocher de l'intégrale de l'éclairage pour les lumières de zone:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X