opengl
Éclairage de base
Recherche…
Modèle d'éclairage Phong
REMARQUE: Cet exemple est WIP, il sera mis à jour avec des diagrammes, des images, d'autres exemples, etc.
Qu'est ce que Phong?
Phong est un modèle de lumière très basique mais réel pour les surfaces à trois parties: éclairage ambiant, diffus et spéculaire.
Éclairage ambiant:
L'éclairage ambiant est la plus simple des trois parties à comprendre et à calculer. L'éclairage ambiant est une lumière qui inonde la scène et illumine l'objet de manière uniforme dans toutes les directions.
Les deux variables de l'éclairage ambiant sont la force de la température ambiante et la couleur de la pièce. Dans votre fragment shader, ce qui suit fonctionnera pour ambient:
in vec3 objColor;
out vec3 finalColor;
uniform vec3 lightColor;
void main() {
float ambientStrength = 0.3f;
vec3 ambient = lightColor * ambientStrength;
finalColor = ambient * objColor;
}
Éclairage diffus:
L'éclairage diffus est légèrement plus complexe que l'ambiant. L'éclairage diffus est une lumière directionnelle, ce qui signifie essentiellement que les visages dirigés vers la source de lumière seront mieux éclairés et que les visages dirigés vers l'extérieur seront plus sombres en raison de la manière dont la lumière les frappe.
Remarque: un éclairage diffus nécessitera l’utilisation de normales pour chaque face que je ne montrerai pas comment calculer ici. Si vous voulez apprendre à faire cela, consultez la page de mathématiques 3D.
Pour modéliser la réflexion de la lumière dans l’infographie, on utilise une fonction de distribution de la réflectance bidirectionnelle (BRDF). BRDF est une fonction qui donne la relation entre la lumière réfléchie le long d'une direction sortante et la lumière incidente provenant d'une direction entrante.
Une surface diffuse parfaite a un BRDF qui a la même valeur pour toutes les directions incidentes et sortantes. Cela réduit considérablement les calculs et, par conséquent, il est couramment utilisé pour modéliser les surfaces diffuses car il est physiquement plausible, même s'il n'y a pas de matériaux diffus purs dans le monde réel. Cette BRDF est appelée réflexion Lambertienne car elle obéit à la loi de cosinus de Lambert.
La réflexion de Lambert est souvent utilisée comme modèle pour la réflexion diffuse. Cette technique fait en sorte que tous les polygones fermés (tels qu'un triangle dans un maillage 3D) réfléchissent la lumière de la même manière dans toutes les directions. Le coefficient de diffusion est calculé à partir de l'angle entre le vecteur normal et le vecteur lumineux.
f_Lambertian = max( 0.0, dot( N, L )
où N
est le vecteur normal de la surface et L
le vecteur vers la source de lumière.
Comment ça marche
En général Le produit scalaire de 2 vecteurs est égal au cosinus de l'angle entre les 2 vecteurs multiplié par la grandeur (longueur) des deux vecteurs.
dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B )
Ceci fait que le produit scalaire de 2 vecteurs unitaires est égal au cosinus de l'angle entre les 2 vecteurs, car la longueur d'un vecteur unitaire est 1.
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
Si on regarde la fonction cos (x) entre les angles -90 ° et 90 °, on voit qu’elle a un maximum de 1 à un angle de 0 ° et qu’elle descend à 0 aux angles de 90 ° et -90 °.
Ce comportement est exactement ce que nous voulons pour le modèle de réflexion. Lorsque le vétor nromal de la surface et le diretion à la source lumineuse sont dans le même sens (l'angle compris entre 0 °), nous voulons un maximum de réflexion. En revanche, si les vecteurs sont orthonormés (l'angle compris entre 90 °), nous souhaitons un minimum de réflexion et nous souhaitons un fonctionnement fonctionnel régulier et continu entre les deux bordures de 0 ° et de 90 °.
Si la lumière est calculée par sommet, la réflexion est calculée pour chaque coin de la primitive. Entre les primitives, les réflexions sont interpolées en fonction de ses coordonnées barycentriques. Voir les reflets résultants sur une surface sphérique:
Ok, donc pour commencer avec notre fragment shader, nous aurons besoin de quatre entrées.
- Normales de sommet (devraient être dans le tampon et spécifiées par les pointeurs d'attribut de sommet)
- Position du fragment (doit être sortie du vertex shader dans frag shader)
- Position de la source lumineuse (uniforme)
- Couleur claire (uniforme)
in vec3 normal;
in vec3 fragPos;
out vec3 finalColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 objColor;
À l’intérieur du bâtiment principal, nous devons faire des calculs. Tout le concept d'éclairage diffus est basé sur l'angle entre la direction normale et la direction de la lumière. Plus l'angle est grand, moins il y a de lumière jusqu'à 90 ° où il n'y a pas de lumière du tout.
Avant de pouvoir commencer à calculer la quantité de lumière, nous avons besoin du vecteur de direction de la lumière. Cela peut être récupéré en soustrayant simplement la position de la lumière à la position du fragment qui renvoie un vecteur de la position de la lumière pointant vers la position du fragment.
vec3 lightDir = lightPos-fragPos;
Aussi, allez de l'avant et normalisez les vecteurs normal
et lightDir
pour qu'ils fonctionnent avec la même longueur.
normal = normalize(normal);
lightDir = normalize(lightDir);
Maintenant que nous avons nos vecteurs, nous pouvons calculer la différence entre eux. Pour ce faire, nous allons utiliser la fonction de produit scalaire. Fondamentalement, cela prend 2 vecteurs et renvoie le cos () de l'angle formé. Ceci est parfait car à 90 degrés, cela donnera 0 et à 0 degré, cela donnera 1. En conséquence, lorsque la lumière pointe directement sur l'objet, elle sera complètement allumée et vice versa.
float diff = dot(normal, lightDir);
Il y a encore une chose à faire pour le nombre calculé, nous devons nous assurer qu'il est toujours positif. Si vous y réfléchissez, un nombre négatif n'a pas de sens dans le contexte car cela signifie que la lumière est derrière le visage. Nous pourrions utiliser une instruction if ou utiliser la fonction max()
qui renvoie le maximum de deux entrées.
diff = max(diff, 0.0);
Ceci étant fait, nous sommes maintenant prêts à calculer la couleur de sortie finale du fragment.
vec3 diffuse = diff * lightColor;
finalColor = diffuse * objColor;
Éclairage spéculaire:
Travailler en cours, revenez plus tard.
Combiné
Travaux en cours, revenez plus tard.
Le code et l'image ci-dessous montrent ces trois concepts d'éclairage combinés.