Eclairage par pixel et Bump Mapping

Principe

L'outil de base est le produit scalaire (dot en Anglais). Il permet de mesurer l'angle entre deux vecteurs. Justement parlons de deux vecteurs particuliers: le vecteur lumière, qui pointe vers une source de lumière à partir d'un vertex, et le vecteur normal, qui est perpendiculaire à la surface d'un objet.

Quand ces deux vecteurs pointent dans la même direction, la surface est très éclairée, et inversement. Donc le résultat du produit scalaire nous donne la quantité de lumière reçue sur un pixel.

Bon où intervient le Bump Mapping? Le but du Bump est d'altérer l'éclairage afin de simuler des détails en relief sur la surface. Donc en altérant légèrement les normales de la surface, on simule le fait que la surface n'étant pas plate, l'éclairage est modifié par du relief.

En noir, une face. En rouge ses normales et la façon dont on la voit. En haut sans bump mapping, en bas avec.

Normal mapping

Équations

Tous les vecteurs utilisés sont normalisés.

"L" est le vecteur lumière (allant du pixel à la source de lumière)
"N" est la normale
"H" est le vecteur "half-angle". H = E+L ou E+N, les 2 marchent, où E est le vecteur unité pointant du pixel à la caméra.
"." est un produit scalaire entre vecteurs

Eclairage basique: lumière diffuse
Intensité = N . L

Eclairage de Phong: lumière diffuse + lumière spéculaire
Intensité = N.L + (H.N)^n
Le nouveau terme ajoute de l'éclairage spéculaire, qui apparaît comme une zone très brillante où l'objet réfléchit directement vers la caméra la lumière provenant de la source de lumière.

Quel espace?

Évidemment tous les vecteurs doivent être traités dans le même espace: espace de la camera, du monde, d'un objet ou... l'espace tangent.

Sachez pour le moment que l'on stoque les normales dans une texture appelée une Normal Map. Voir plus loin pourquoi. Si vous voulez faire du bump mapping, sachez également que la Normal Map contient l'effet de bump, simulant l'effet de reliefs sur l'éclairage, et qu'elle est donc associée à une texture contenant la représentation en couleur (par exemple: une texture affichant des briques avec une Normal Map contenant le relief des briques).

Il y a deux espaces utilisés en pratique:

Espace Tangent

Dans la plupart des cas, on utilise l'espace tangent car il peut être utilisé dans tous les cas.
L'espace Tangent est défini par 3 vecteurs: la tangente, la binormale et la normale. La tangente est le vecteur sur la surface sur lequel les coordonnées de texture U sont incrémentés. La binormale est aussi un vecteur tangent à la surface, suivant lequel les coordonnées de texture V sont incrémentées. La normale est tout simplement la normale à la surface.
La plupart du temps ces trois vecteurs sont orthogonaux. Il arrive parfois qu'ils ne le soient pas, mais l'éclairage fonctionnera tout de même.

Bien sûr chaque vertexe a un espace tangent différent.

Pour calculer l'espace tangent de chaque vertexe, calculer au chargement:

//X est le produit vectoriel
Pour chaque face {
	(x,y,z) = (p2->x-p1->x,p2->u-p1->u,p2->v-p1->v) X (p3->x-p1->x,p3->u-p1->u,p3->v-p1->v)
	if (x!=0) {
		NORMALIZE(x,y,z)
		p1->Tx += -y/x;
		p1->Bx += -z/x;
		p2->Tx += -y/x;
		p2->Bx += -z/x;
		p3->Tx += -y/x;
		p3->Bx += -z/x;
	}
	(x,y,z) = (p2->y-p1->y,p2->u-p1->u,p2->v-p1->v) X (p3->y-p1->y,p3->u-p1->u,p3->v-p1->v)
	if (x!=0) {
		NORMALIZE(x,y,z)
		p1->Ty += -y/x;
		p1->By += -z/x;
		p2->Ty += -y/x;
		p2->By += -z/x;
		p3->Ty += -y/x;
		p3->By += -z/x;
	}
	(x,y,z) = (p2->z-p1->z,p2->u-p1->u,p2->v-p1->v) X (p3->z-p1->z,p3->u-p1->u,p3->v-p1->v)
	if (x!=0) {
		NORMALIZE(x,y,z)
		p1->Tz += -y/x;
		p1->Bz += -z/x;
		p2->Tz += -y/x;
		p2->Bz += -z/x;
		p3->Tz += -y/x;
		p3->Bz += -z/x;
	}
}
Pour chaque vertexe {
	NORMALIZE(T)
	NORMALIZE(B)
	newN = TxB
	//Vérifie la direction de la normale par rapport à celle calculée par une méthode traditionnelle.
	//De temps en temps TxB doit être inversée.
	if newN . oldN > 0
		N = newN
	else	N = -newN
}

Implémentation

D'un coté OpenGL sait déjà calculer un éclairage. Mais il n'est calculé que pour chaque vertexe, ensuite une interpolation est utilisée. C'est donc un éclairage de Goureau, avec tous ses défauts.

Faisons quelque chose de plus amusant et implémentons un vrai éclairage de Phong, c'est à dire par pixel. Vous pouvez utiliser au choix seulement l'éclairage diffusé, ou l'équation entière. La deuxième méthode ne demande que quelques passes de plus. Le principal problème est le calcul des produits scalaire.

Heureusement une extension OpenGL va nous aidez: GL_DOT3, qui calcule un produit scalaire par pixel. On stoque les vecteurs normales dans une texture appelée une Normal Map. Et on stoque les vecteurs lumières dans les couleurs des vertexes (glColor ou glColorPointer au choix). La bonne normale est choisie dans la Normal Map en utilisant les coordonnées de texture. OpenGL va interpoler le vecteur lumière en interpolant la couleur des vertexes à travers le triangle.

La carte 3D peut réaliser des produits scalaires entre couleurs. On peut stoquer des vecteurs dans les couleurs des vertexes et dans des textures et les utiliser comme des vecteurs.
Evidemment, on a besoin de vecteurs dont les composantes vont de -1 à +1, alors qu'une composante de couleur n'est stoquée qu'entre 0 et +255. La façon dont cela est résolu est que l'on mappe l'intervale -1;+1 sur 0;255. La valeur "couleur" 127 représente donc la valeur "vecteur" 0:

R = 127 + x*127
G = 127 + y*127
B = 127 + z*127
où x, y et z sont entre -1 et +1.

Donc lorsque vous calculez une image, pour chaque objet:

Configurons le DOT3. j'espère que vous savez utiliser le multitexturing. Voici la configuration de la passe DOT3 entre une couleur de texture (vecteur normal) et la couleur courante (vecteur lumière):
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_EXT,GL_DOT3_RGBA_EXT);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB_EXT,GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB_EXT,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB_EXT,GL_PREVIOUS_EXT);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB_EXT,GL_SRC_COLOR);
Et c'est tout!

Où trouver la Normal Map?

Simple choix:
Main page

email : Sly