IX. Tutoriel 7. Éclairage avec relief▲
Ce tutoriel suppose que vous connaissiez les rudiments d'éclairage en OpenGL, ce que lumière ambiante, diffuse et spéculaire veulent dire et les mathématiques derrière. Il ne couvre pas la théorie de l'éclairage Phong, ni comment les facteurs ambiant, diffus et spéculaire sont calculés. Il y a un grand nombre d'articles et de documentations sur le Net, suivez ce lien pour plus de détails.
Ce tutoriel montre comment réaliser un éclairage par point sur un shader avec les reliefs. Le bump mapping ajoute des détails à la scène en perturbant les normales de la surface des objets, que l'on utilise ensuite dans le pixel shader pour le calcul de l'éclairage. Il en résulte une surface irrégulière. La capture d'écran ci-dessous montre la même scène rendue avec et sans bumps (dans l'exemple de code fourni, vous pouvez basculer entre les deux scènes en utilisant le bouton switch).
IX-A. Code de la procédure▲
Du point de vue CPU, les positions de la lumière et de la caméra sont envoyées au shader dans le système de coordonnées monde.
Tout d'abord, nous obtenons l'inverse de la matrice modèle de l'objet comme suit :
//Obtention de la matrice modèle
mat4f &
modelBumpMeshMat =
mesh->
GetTransfromationMatrix();
mat4f modelInv =
modelBumpMeshMat.inverse();
Puis nous multiplions la position de la lumière par l'inverse de la matrice modèle pour obtenir sa position dans l'espace modèle :
//Position de la lumière dans l'espace monde
vec4f lightPosition =
modelInv *
lightPos;
// Envoi des uniformes au shader
m_pActiveShader->
SetUniform3fv("lightPosModel"
, 1
, &
lightPosition.x);
En ce qui concerne la position de la caméra, elle se situe toujours à l'origine (0, 0, 0). La matrice de vue transforme chaque objet depuis l'espace monde vers l'espace caméra, donc si nous multiplions la position de la caméra située à (0, 0, 0, 1) par l'inverse de la matrice de vue, nous obtenons la position de la caméra dans l'espace monde. Puis nous multiplions cette position par l'inverse de la matrice modèle pour obtenir la position de la caméra dans l'espace objet :
vec4f cameraPos =
inv( modelMatrix ) *
(inv(ViewMatrix) *
vec4(0
, 0
, 0
, 1
)
Pour une expression compacte :
vec4f cameraPos=
modelInv *
m_pCamera->
GetPosition();
m_pCamera->GetPosition() calcule l'inverse de la matrice de vue, que nous multiplions par le vecteur homogène vec4f(0, 0, 0, 1.f). Notez qu'effectuer la multiplication d'une matrice « M » 4x4 par un vecteur « v » homogène 4x1 extrait la partie translation de la matrice (M[12] M[13] M[14]).
Maintenant que nous avons la position de la caméra dans l'espace monde, nous l'envoyons au shader.
m_pActiveShader->
SetUniform3fv("camPosModel"
, 1
, &
cameraPos.x);
De plus, nous devons envoyer les paramètres de la lumière et les propriétés du matériau définis par les vecteurs ambiant, diffus et spéculaire.
Pour les paramètres de la lumière :
m_pActiveShader->
SetUniform4fv("lightColorAmbient"
, 1
, &
lightAmbient.x);
m_pActiveShader->
SetUniform4fv("lightColorDiffuse"
, 1
, &
lightDiffuse.x);
m_pActiveShader->
SetUniform4fv("lightColorSpecular"
, 1
, &
lightSpecular.x);
Pour les propriétés du matériau, l'envoi se fait dans la fonction Render de l'objet CMeshEntity comme suit :
// Envoi du paramètre ambiant
if
(shader->
matColorAmbient !=
-
1
)
{
glUniform4fv(shader->
matColorAmbient, 1
, material->
ambient);
}
// Envoi du paramètre diffus
if
(shader->
matColorDiffuse !=
-
1
)
{
glUniform4fv(shader->
matColorDiffuse, 1
, material->
diffuse);
}
// Envoi du paramètre spéculaire
if
(shader->
matColorSpecular !=
-
1
)
{
glUniform4fv(shader->
matColorSpecular, 1
, material->
specular);
}
// Envoi du paramètre de brillance
if
(shader->
matShininess !=
-
1
)
{
glUniform1f(shader->
matShininess, material->
shininess);
}
Passons ensuite au vertex shader. Nous commençons par construire la matrice tangente utilisée pour passer tous les vecteurs dans le même système de coordonnées tangent (espace tangent). Le vecteur direction de la lumière et demi-angle sont convertis vers l'espace tangent.
// Calcul du vecteur bitangent (ceci peut être fait sur le CPU)
vec3
bitangent =
cross
(
normal, tangent);
// Création de la matrice de l'espace tangent
mat3
tangentSpace =
mat3
(
tangent, bitangent, normal);
// Obtention du vecteur direction de la lumière du vertex actuel
v_lightVector =
lightPosModel -
position.xyz ;
// Conversion du vecteur direction de la lumière vers l'espace tangent
v_lightVector =
v_lightVector *
tangentSpace;
// Normalisation
v_lightVector =
normalize
(
v_lightVector);
// Obtention de la direction de la caméra du vertex actuel
v_halfVector =
camPosModel -
position.xyz ;
// Conversion vers l'espace tangent
v_halfVector =
v_halfVector *
tangentSpace;
// Normalisation
v_halfVector =
normalize
(
v_halfVector);
// Calcul du vecteur demi-angle
v_halfVector =
(
v_halfVector +
v_lightVector) /
2
.0
;
// Normalisation
v_halfVector =
normalize
(
v_halfVector) ;
Dans le pixel shader, nous créons une normale par pixel, perturbée à partir de la normal map. Puis nous calculons la couleur finale du pixel par la somme des contributions ambiante, diffuse et spéculaire de la lumière.
// Obtention de la couleur diffuse par vertex
vec4
color =
texture2D
(
texture0, texCoord) ;
// Obtention de la normale par pixel
vec3
bump =
texture2D
(
textureBump, texCoord).rgb *
2
.0
-
1
.0
;
// Inversion de la normale pour les faces arrière (si le face culling est activé, cette étape peut être supprimée)
if
(!
gl_FrontFacing)
bump =
-
bump;
// Calcul de la contribution de la lumière
// 1- lamber ou facteur diffus
float
lamber =
max
(
0
.0
, dot
(
normalize
(
v_lightVector), bump) );
//2- facteur spéculaire
float
specular =
0
.0
;
if
(
dot
(
bump, v_lightVector) <
0
.0
)
specular =
0
.0
;
else
specular =
max
(
0
.0
, pow
(
dot
(
normalize
(
v_halfVector), bump), matShininess)) ;
// Obtention des couleurs finales ambiantes, diffuses et spéculaires
vec4
finalAmbientContrib =
lightColorAmbient *
color /**
matColorAmbient.xyz
*/
;
vec4
finalDiffuseContrib =
lightColorDiffuse *
color *
lamber *
matColorDiffuse;
vec4
finalSpecularContrib =
lightColorSpecular *
specular *
matColorSpecular;
// La couleur finale est la somme des composantes ambiante, diffuse et spéculaire
gl_FragColor =
finalAmbientContrib +
(
finalDiffuseContrib +
finalSpecularContrib) ;