Lumière Diffuse
Adapté à XNA 4.0 par A. Nadif
Bonjour et bienvenue pour ce second tutorial de ma série sur le programmation de shaders dans XNA. Aujourd'hui, nous allons travailler sur le premier tutorial, afin de mettre en place un éclairage à l'aide d'une équation un peu plus intéressante, en implémentant un éclairage diffus.
Lumière Diffuse
Voici l'équation de la lumière ambiante :
I = Aintensity * Acolor
// A => Ambiante
L'éclairage diffus est écrit selon cette équation, qui ajoute une lumière directionnelle :
I = Aintensity x Acolor + Dintensity x Dcolor x N.L (2.1)
// D => Diffuse
Avec cette équation, vous pouvez voir que nous utilisons toujours la lumière ambiante, mais on a besoin de deux autres variables pour décrire la couleur et l'intensité de la lumière diffuse, et deux vecteurs N et L pour décrire la direction de la lumière, L, et la normale à la surface, N.
On peut imaginer la lumière diffuse comme une valeur indiquant combien la surface réfléchit la lumière. La lumière qui est réfléchie sera plus forte et plus visible quand l'angle entre la normale N et la direction de la lumière L tend à diminuer.
I = Aintensity * Acolor
// A => Ambiante
L'éclairage diffus est écrit selon cette équation, qui ajoute une lumière directionnelle :
I = Aintensity x Acolor + Dintensity x Dcolor x N.L (2.1)
// D => Diffuse
Avec cette équation, vous pouvez voir que nous utilisons toujours la lumière ambiante, mais on a besoin de deux autres variables pour décrire la couleur et l'intensité de la lumière diffuse, et deux vecteurs N et L pour décrire la direction de la lumière, L, et la normale à la surface, N.
On peut imaginer la lumière diffuse comme une valeur indiquant combien la surface réfléchit la lumière. La lumière qui est réfléchie sera plus forte et plus visible quand l'angle entre la normale N et la direction de la lumière L tend à diminuer.
Si L et N sont colinéaires, la lumière sera réfléchie au maximum, et si L est orthogonal à N, la lumière réfléchie sera minimum.
Pour obtenir l'angle entre L et N, on peut utiliser le produit vectoriel ou le produit scalaire. Cette règle est utilisée pour trouver l'angle entre deux vecteurs donnés et peut être définit comme ceci :
N.L = |N| x |L| x cos(a)
où |N| est la norme de N, |L| celle de L et a est l'angle entre les deux vecteurs.
Pour obtenir l'angle entre L et N, on peut utiliser le produit vectoriel ou le produit scalaire. Cette règle est utilisée pour trouver l'angle entre deux vecteurs donnés et peut être définit comme ceci :
N.L = |N| x |L| x cos(a)
où |N| est la norme de N, |L| celle de L et a est l'angle entre les deux vecteurs.
Implémentation du shader
On a besoin de trois variables globales :
float4x4 matWorldViewProj; float4x4 matInverseWorld; float4 vLightDirection;
On a toujours la matrice worldviewprojection du premier tutorial, mais en plus, on utilise la matrice InverseWorld pour calculer une normale correcte en relation avec la matrice world, et une direction de la lumière vLightDirection qui explique quelle est la direction de la lumière.
On a aussi besoin de définir la structure OUT pour notre vertex shader, afin de réaliser l'équation correcte de la lumière dans le pixel shader :
On a aussi besoin de définir la structure OUT pour notre vertex shader, afin de réaliser l'équation correcte de la lumière dans le pixel shader :
struct OUT { float4 Pos: POSITION; float3 L: TEXCOORD0; float3 N: TEXCOORD1; };
Nous avons ici la position Pos, la direction L de la lumière et la normale N stockée dans différents registres. TEXCOORDn peut être utilisé pour n'importe quelles valeurs, et comme nous n'utilisons pas de coordonnées de texture, on peut facilement utiliser ces registres comme stockage pour nos deux vecteurs.
Ok, il est temps de passer au vertex shader :
Ok, il est temps de passer au vertex shader :
OUT VertexShaderGo( float4 Pos: POSITION, float3 N: NORMAL ) { OUT Out = (OUT) 0; Out.Pos = mul(Pos, matWorldViewProj); Out.L = normalize(vLightDirection); Out.N = normalize(mul(matInverseWorld, N)); return Out; }
Nous prenons la position du fichier model, ainsi que la normale, et nous les donnons au shader. En se basant sur ces données et sur nos variables globales, on peut transformer la position Pos, normaliser la direction de la lumière et transformer ainsi que normaliser la normale à la surface.
Ensuite, dans le pixel shader, on prend les valeurs de TEXCOORD0 et on les met dans L, et les valeurs de TEXCOORD1 et on les met dans N. Ces registres sont remplis par le vertex shader. Puis, on implémente l'équation 2.1 dans le pixel shader :
Ensuite, dans le pixel shader, on prend les valeurs de TEXCOORD0 et on les met dans L, et les valeurs de TEXCOORD1 et on les met dans N. Ces registres sont remplis par le vertex shader. Puis, on implémente l'équation 2.1 dans le pixel shader :
float4 PixelShaderGo(float3 L: TEXCOORD0, float3 N: TEXCOORD1) : COLOR { float Ai = 0.8f; float4 Ac = float4(0.075, 0.075, 0.2, 1.0); float Di = 1.0f; float4 Dc = float4(1.0, 1.0, 1.0, 1.0); return Ai * Ac + Di * Dc * saturate(dot(L, N)); }
Voici la technique pour ce shader :
technique DiffuseLight { pass P0 { VertexShader = compile vs_2_0 VertexShaderGo(); PixelShader = compile ps_2_0 PixelShaderGo(); } }
Ok, ça y est pour la lumière diffuse ! Téléchargez le source et jouez un peu avec, pour le mieux le comprendre :) J'espère que ce début vous montre le pouvoir des shader maintenant, et aussi comment les utiliser dans vos propres applications !
Téléchargez le code source
Téléchargez le code source