IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Apprendre OpenGL moderne

Deuxième partie : éclairage


précédentsommairesuivant

III. Matériaux

Dans le monde réel, chaque objet réagit différemment à la lumière. Les objets en acier sont souvent plus brillants qu’un vase en argile, un conteneur en bois ne réagira pas à la lumière comme un conteneur métallique. Chaque objet donnera aussi des reflets différents. Certains refléteront la lumière sans trop la disperser, ce qui donnera de petits reflets intenses, d’autres donneront des reflets plus larges. Pour simuler ces différents comportements dans OpenGL, on définit des propriétés spécifiques pour chaque objet.

Dans le chapitre précédent, nous avons défini un objet et une couleur de lumière pour obtenir une image de l’objet, combinée avec une composante diffuse et une composante spéculaire. Pour décrire des objets, on peut définir une couleur de matériau pour chacune des trois composantes, ambiante, diffuse et spéculaire. On a ainsi un contrôle très fin sur la couleur produite par les objets. Ajoutons une composante de brillance à ces trois couleurs et nous aurons toutes les propriétés pour définir un matériau :

 
Sélectionnez
#version 330 core
struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};
uniform Material material;

Dans le fragment shader, nous créons une structure pour y placer les propriétés de matériau de l’objet. On peut aussi les représenter une à une avec des variables uniformes, mais l’utilisation d’une structure permet une meilleure organisation. Nous définissons le contenu de cette structure et déclarons ensuite une variable uniforme de ce nouveau type structure.

Comme on peut le voir, on utilise une couleur pour chacune des composantes du modèle de Phong. Le vecteur matériau ambient définit quelle couleur cet objet reflétera sous un éclairage ambiant, ce qui est en général la couleur de l’objet. Le vecteur diffuse définira la couleur de l’objet sous un éclairage diffus. Cette couleur est définie par la couleur de l’objet (comme pour l’éclairage ambiant). Le vecteur specular définit la couleur de la lumière reflétée (voire une couleur de reflet spécifique de l’objet). Enfin, la composante shininess (brillance) définira la netteté et la largeur de la tache lumineuse de la composante spéculaire.

Avec ces quatre composantes qui définissent les propriétés de matériau d’un objet, on peut simuler beaucoup de vrais matériaux. Le tableau disponible sur la page devernay.free.fr donne plusieurs propriétés de matériaux pour simuler la diversité du monde réel. L’image suivante montre l’effet de quelques matériaux sur notre cube (émeraude, perle, bronze, or, plastique cyan, plastique rouge, gomme verte, gomme jaune) :

Image non disponible

Comme vous pouvez le voir, en spécifiant correctement les propriétés de matériau d’un objet, on change notre perception. Les effets sont nettement perceptibles, mais pour des effets plus réalistes, nous aurons besoin de formes plus complexes qu’un simple cube. Dans la section suivante, nous apprendrons à utiliser des formes plus complexes.

Choisir correctement les matériaux est un défi qui requiert de l’expérience et il n’est pas rare de dégrader la qualité visuelle d’un objet avec un matériau mal conçu.

Essayons maintenant de coder ces propriétés de matériau dans les shaders.

III-A. Mise en place des matériaux

Nous avons créé une structure Material dans le fragment shader, on doit donc maintenant modifier le calcul d’éclairage pour tenir compte du matériau. On peut accéder aux propriétés du matériau par la variable uniforme material :

 
Sélectionnez
void main()
{
    // ambient
    vec3 ambient = lightColor * material.ambient;
    // diffuse
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos – FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = lightColor * (diff * material.diffuse);
    // specular
    vec3 viewDir = normalize(viewPos – FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = lightColor * (spec * material.specular);
    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
}

Toutes les propriétés du matériau sont accessibles par la variable material, et nous calculons la couleur finale en tenant compte des propriétés du matériau. Chacun des attributs de matériau est multiplié par la composante d’éclairage correspondante.

On peut définir le matériau d’un objet au moyen de la variable uniforme. Une structure en GLSL n’est pas un cas particulier. Une structure intervient pour encapsuler plusieurs variables uniformes, nous pouvons les utiliser comme des variables uniformes individuelles, préfixées par le nom de la variable structure :

 
Sélectionnez
lightingShader.setVec3("material.ambient",  1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.diffuse",  1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
lightingShader.setFloat("material.shininess", 32.0f);

Nous choisissons les composantes ambiante et diffuse de la couleur de l’objet et la composante spéculaire d’une couleur moyennement claire. Nous gardons aussi la brillance à la valeur 32. On pourra facilement modifier ces valeurs dans l’application.

Vous devriez obtenir quelque chose comme ça :

Image non disponible

Ce n’est pas encore vraiment ça ?

III-B. Propriétés des sources lumineuses

L’objet est bien trop brillant, car les couleurs des composantes ambiante, diffuse et spéculaire sont reflétées avec trop d’intensité. Les sources de lumière ont aussi des intensités différentes pour chacune de leurs composantes (ambiante, diffuse, spéculaire). Dans le chapitre précédent, nous avions résolu cela par l’utilisation d’une variable d’intensité. De façon similaire, nous définirons un vecteur d’intensité pour chacune des trois composantes de la source. En visualisant lightColor comme un vec3(1.0) le code serait celui-ci :

 
Sélectionnez
vec3 ambient  = vec3(1.0) * material.ambient;
vec3 diffuse  = vec3(1.0) * (diff * material.diffuse);
vec3 specular = vec3(1.0) * (spec * material.specular);

Ainsi, chaque propriété du matériau rend pleinement l’intensité de chaque composante de la lumière. Ces vecteurs vec3(1.0) peuvent être modifiés séparément pour chacune des composantes de la source, c’est bien ce que nous voulons. Pour l’instant, la composante ambiante de l’objet est pleinement influencée par la couleur du cube, mais cette composante ambiante ne devrait pas avoir autant d’importance sur la couleur finale du cube, il faut donc la restreindre en fixant l’intensité de la lumière ambiante à une valeur plus faible :

 
Sélectionnez
vec3 ambient = vec3(0.1) * material.ambient;

On peut modifier les composantes diffuse et spéculaire de la source lumineuse de la même façon. C’est ce que nous avions fait dans le chapitre précédent ; nous avions déjà créé des propriétés différentes pour chaque composante de la source. Nous voulons créer quelque chose de similaire à la structure material, pour la source de lumière :

 
Sélectionnez
struct Light {
 vec3 position;
 vec3 ambient;
 vec3 diffuse;
 vec3 specular;
};
uniform Light light;

Une source de lumière aura différentes intensités pour chacune de ses composantes. La composante ambiante aura plutôt une intensité assez faible, car, en général, elle n’est pas dominante. Habituellement, la composante diffuse de la source prend la couleur exacte que nous voudrons donner à la source : souvent une couleur blanche brillante. La composante spéculaire est en général définie par un vec3(1.0) brillant à pleine intensité. Notez que nous avons ajouté la position de la source à la structure Light.

Comme pour la variable uniforme material, nous mettrons à jour le fragment shader :

 
Sélectionnez
vec3 ambient  = light.ambient * material.ambient;
vec3 diffuse  = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);

Nous définirons les intensités des composantes de la source dans l’application :

 
Sélectionnez
lightingShader.setVec3("light.ambient",  0.2f, 0.2f, 0.2f);
lightingShader.setVec3("light.diffuse",  0.5f, 0.5f, 0.5f); // assombri un peu la lumière pour correspondre à la scène
lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

Dès lors, nous avons modulé l’influence de la lumière sur le matériau de l’objet et nous obtenons un effet plus proche de celui du chapitre précédent. Nous avons désormais le contrôle total sur la lumière et le matériau de l’objet :

Image non disponible

Modifier l’aspect visuel de l’objet est très facile maintenant. Pimentons un peu les choses !

III-C. Différentes couleurs de sources lumineuses

Jusqu’ici nous n’avions modifié que l’intensité des composantes de la source, en choisissant des couleurs allant du blanc au noir et passant par le gris, n’affectant donc pas la couleur des objets, mais seulement leur intensité d’éclairage. Ayant accès aux propriétés de la source, on peut modifier les couleurs de ses composantes en fonction du temps pour obtenir des effets intéressants. Puisque tout est en place dans le fragment shader, changer les couleurs est facile et donne immédiatement des effets sympathiques :

Comme on le voit, la couleur de la source influence énormément la couleur de l’objet. La couleur de la source influence directement les couleurs qui sont reflétées par l’objet (rappelez-vous le chapitre 12 sur les couleurs), et la couleur perçue est donc influencée par la couleur de la source.

On peut facilement modifier les couleurs en fonction du temps au moyen des fonctions sin() et glfwGetTime() :

 
Sélectionnez
glm::vec3 lightColor;
lightColor.x = sin(glfwGetTime() * 2.0f);
lightColor.y = sin(glfwGetTime() * 0.7f);
lightColor.z = sin(glfwGetTime() * 1.3f);
glm::vec3 diffuseColor = lightColor   * glm::vec3(0.5f); // decrease the influence
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // low influence
lightingShader.setVec3("light.ambient", ambientColor);
lightingShader.setVec3("light.diffuse", diffuseColor);

Testez vous-mêmes différents éclairages et différents matériaux et voyez comment cela change l’aspect des objets. Vous trouverez le code source de l’application ici.

III-D. Exercices

  • Pourrez-vous simuler l’un des matériaux réels en définissant leurs propriétés comme nous l’avons vu au début du chapitre ? Noter que le tableau des valeurs ambiantes est différent de celui des valeurs diffuses, ils n’ont pas tenu compte des intensités de la source. Pour définir correctement ces valeurs, vous devrez définir les intensités de la source avec des vec3(1.0) pour obtenir la même sortie : solution pour un conteneur en plastique de couleur cyan.

III-E. Remerciements

Ce tutoriel est une traduction réalisée par Jean-Michel Fray dont l’original a été écrit par Joey de Vries et qui est disponible sur le site Learn OpenGL.


précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2018 Joey de Vries. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.