15/17
8. Illumination
Jusqu’à présent, les pixels appartenant à des objets possèdent une couleur homogène. Ainsi, un objet 3D apparait comme un domaine de couleur uniforme sur l’image. Ce type d’apparence ne permet pas de percevoir la forme 3D de l’objet. Dans le monde réel, la couleur d’un objet vue par un observateur varie en fonction de l’orientation de celui-ci et de l’illumination.
L'illumination (ou le shading) indique la modélisation de l’apparence d’un matériau en fonction de sa géométrie et par rapport à la position de la lumière ainsi que de l’observateur. Un modèle simple d’illumination est celui de Gouraud, et consiste en trois composantes.
-
Ambiante (ambient): Corresponds à la couleur de base uniforme. Permets de détecter la présence d’un objet sans illumination directe, mais ne donne pas d’indication visuelle sur sa forme locale.
-
Diffuse: Variation lumineuse liée à l’orientation de la géométrie locale par rapport à la lumière. Modélise le fait qu’un objet est clairement illuminé lorsqu’il est (localement) face à la lumière, et apparait plus sombre lorsqu’il est (localement) orthogonal à celle-ci. Cette composante permet de rendre visuellement l’apparence de la géométrie locale.
-
Speculaire (specular): Prends en compte le comportement de réflexion de certains matériaux qui apparaissent très brillants lorsque la lumière se reflète dans les yeux de l’observateur. Cette composante permet de traduire l’effet de réflexion de certaines matières tel que le métal en modélisant l’effet d’un point brillant sous certains angles de vues.
Considérons un point \(p\) sur une forme 3D associée à la normale unitaire \(n\). Ce point reçoit de la lumière depuis une direction \(u\) (vecteur unitaire entre \(p\) et la source de lumière).
-
La composante diffuse est calculée comme \(n\cdot u\). Pour ne prendre en compte que la lumière provenant de face, seule la composante positive de \(n\cdot u\) est considéré (attention à l’orientation de la normale des objets).
-
La composante spéculaire est calculée comme \((r\cdot t)^\alpha\), où \(t\) est la direction de vue, et \(r\) est le symétrique de \(u\) par rapport à la normale \(n\). \(\alpha\) est un exposant arbitraire modélisant la concentration du halo lumineux. On considère typiquement \(\alpha=2^{6-8}\). De manière similaire au cas diffus, seule la composante positive de \(r\cdot t\) est considéré pour des surfaces orientées.
En pratique, ces calculs d’illuminations sont réalisés dans les shaders. Ainsi, en plus des informations de positions (et potentiellement de couleurs), il est nécessaire de donner, pour chaque sommet, des informations de normales au shader. L’une des implémentations possibles appelées Phong shading est la suivante
-
Envoi au vertex shader des informations de positions et normales par sommet.
-
Le fragment shader reçoit les valeurs de position et normales interpolées pour chaque fragment.
-
L’illumination de Gouraud est calculée pour chaque fragment dans le shader.
Application
Le code suivant implémente la méthode de Phong shading en utilisant l’illumination de Gouraud.
Vertex shader
#version 330 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
layout (location = 2) in vec4 normal;
// Can pass a struct of data to fragment shader
out struct fragment_data
{
vec4 position;
vec4 color;
vec4 normal;
} fragment;
uniform mat4 perspective;
void main()
{
fragment.color = color;
fragment.normal = normal;
fragment.position = position;
gl_Position = perspective * position;
}
Fragment shader
#version 330 core
in struct fragment_data
{
vec4 position;
vec4 color;
vec4 normal;
} fragment;
out vec4 FragColor;
// light position
vec3 light = vec3(0.0, 0.0, 5.0);
// light color
vec3 light_color = vec3(1.0); // white color
void main()
{
vec3 n = normalize(fragment.normal.xyz);
vec3 u = normalize(light-fragment.position.xyz);
vec3 r = reflect(-u,n); // see documentation of reflect
vec3 t = normalize(vec3(0.0, 0.0, 0.0)-fragment.position.xyz); // camera position is at (0,0,0)
float ambiant = 0.2;
float diffuse = 0.8 * clamp( dot(u,n), 0.0, 1.0);
float specular = 0.5 * pow( clamp( dot(r,t), 0.0, 1.0), 128.0);
vec3 c = (ambiant+diffuse)*fragment.color.rgb*light_color + specular*light_color;
FragColor = vec4(c, 1.0);
}

-
Modifiez la position de la source de lumière dans le shader et observez l’effet sur l’illumination de la forme.