Nello scorso Post vi ho raccontato i concetti base per capire come funzionano gli shader in XNA e abbiamo prodotto uno shader Ambient lighting.
Oggi vedremo invece come far interagire in maniera leggermente più complessa una semplice fonte di luce con una mesh attraverso il diffuse lighting.
Diffuse Lighting
Questo tipo di illuminazione è generato da una fonte di luce che ha unicamente informazione di direzione e intensità, quindi non è associabile a una posizione specifica nello spazio. Potrebbe essere rappresentata da infiniti raggi paralleli. La logica tramite la quale questa luce interagisce con la mesh è la seguente:
Più è piccolo l'angolo di incidenza tra la normale della superficie e il vettore della luce, maggiore sarà il riflesso della luce sulla superficie. Quindi una luce diretta perfettamente verso la normale di una superficie verrà riflessa al massimo, una perpedicolare non verrà riflessa.

Per cercare di spiegarvi meglio il concetto ho creato queste 3 semplici immagini, nella prima potete notare che l'angolo che si crea tra la normale e la luce è molto ampio, in questo caso la luce verrà riflessa molto poco, mentre nell'ultimo caso sarà quasi totalmente riflessa.
Passiamo direttamente al codice:
diffuse.fx------------------------------------------------------------
float4x4 World;
float4x4 View;
float4x4 Projection;
//1 Nuove variabili--------------------------------------------
float4x4 WorldInverseTranspose;
float3 DiffuseLightDirection = float3(1, 0.5, 0);
float4 DiffuseColor = float4(0.4, 0.5, 0.9, 1);
float DiffuseIntensity = 0.09;
//2 Variabili relative all'ambient light------------------------
float4 AmbientColor = float4(0.3,0.3,0.3,0.3);
float AmbientIntensity = 0.1;
//3-------------------------------------------------------------
struct VertexShaderInput
{
float4 Position : POSITION0;
float4 Normal : NORMAL0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float4 Color : COLOR0;
};
//4---------------------------------------------------------------
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
float4 worldPosition = mul(input.Position, World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
float4 normal = mul(input.Normal, WorldInverseTranspose);
float lightIntensity = dot(normal, DiffuseLightDirection);
output.Color = DiffuseColor * DiffuseIntensity * lightIntensity;
return output;
}
//5--------------------------------------------------------------
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
return input.Color + AmbientColor * AmbientIntensity;
}
technique Technique1
{
pass Pass1
{
VertexShader = compile vs_1_1 VertexShaderFunction();
PixelShader = compile ps_1_1 PixelShaderFunction();
}
}
Come per lo scorso tutorial ho diviso il codice in 4 blocchi, analizziamolo velocemente
1 Nuove variabili
WorldInverseTranspose;
Prima abbiamo parlato di "normale della superficie", in realtà quello che interessa non è solo la normale delle superficie, ma la normale posizionata nello spazio della nostra scena. La variabile WorldInverseTranspose servirà per definire questa informazione.float3 DiffuseLightDirection = float3(1, 0.5, 0);Sono le variabili che identificano la luce definendone la direzione, il colore e l'intensita.
float4 DiffuseColor = float4(0.4, 0.5, 0.9, 1);
float DiffuseIntensity = 0.09;
2 variabili ambientLight
Sono le variabili che abbiamo visto nella lezione scorsa utili per definire la luce ambientale. Mi sono dimenticato di dirvi che utilizzeremo anche questa luce... quindi andremo a sommare due tipi di materiali per produrre un effetto più completo.
3 definizioni della struttura vertex shader (in e out)
In questo tipo di materiale avremo bisogno di avere in ingresso oltre che l'informazione di posizione del vertice anche l'informazione della normale, che come vi spiegavo prima, verrà messa a confronto con la direzione della luce per il calcolo dell'intensità.
L'output del vertex shader riporterà informazione di posizione e colore.Infatti a seguito dei calcoli relativi all'angolo di incidenza dei due vettori, andremo a modificare il colore del vertice per garantire l'effetto di "riflessione della luce".
4 Vertex shader
All'inizio della funzione andremo a definire la posizione del vertice in base alla nostra world.
(posizioneremo il vertice nella scena)
nelle righe che riporto qui sotto andremo invece a definire l'effetto della luce sul vertice:
float4 normal = mul(input.Normal, WorldInverseTranspose);normal: moltiplichiamo la normale del vertice per la matrice che identifica la matrice inversa, trasposta della world (in realtà ho notato che basta anche l'inversa....) .Questa moltiplicazione ha lo scopo di definire la normale non solo sul modello... ma sul posizionamento della mesh nella scena, tenendo dunque conto di spostamenti e rotazioni.
float lightIntensity = dot(normal, DiffuseLightDirection);
output.Color = DiffuseColor * DiffuseIntensity * lightIntensity;
lightIntensity : effettueremo il prodotto dot tra la normale e la direzione della luce (riguardate l'immagine con le 3 situazioni....) per definire quanto sia l'incidenza tra la normale e la luce.
http://en.wikipedia.org/wiki/Dot_product#Geometric_interpretation guardare l'immagine su wikipedia.. potrebbe chiarirvi le idee riguardo questa operazione.
output.Color : otteniamo il colore del vertice moltiplicando i valori che abbiamo impostato per la luce diffuse e sopratutto aggiungiamo il fattore intensità definito tramite all'interazione tra normale e luce.
5 Pixel Shader
Non resta che definire il pixel shader utilizzando le informazioni di colore ricevute dal vertex shader e aggiungendo i parametri di luce ambientale definiti in testa al file.
return input.Color + AmbientColor * AmbientIntensity;
A questo punto dobbiamo fare qualche piccola delucidazione sul file game1.cs ... ma niente di difficile, il grosso lo avete visto sopra.
Prima di tutto modifichiamo la funzione Draw per chiamare La DrawModelDiffuse invece che la funzione ambient del tutorial precedente ( se scaricate i sorgenti a fondo pagina troverete già tutto pronto...)
Draw
protected override void Draw(GameTime gameTime)
{
device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.SlateGray, 1, 0);
DrawModelDiffuse(myModel, world);
base.Draw(gameTime);
}
DrawModelDiffuse
//Funzione per utilizzare lo shader diffuseQuesta funzione setta alcuni dei parametri che avete visto dentro l'effetto.
private void DrawModelDiffuse(Model model, Matrix world)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (ModelMeshPart part in mesh.MeshParts)
{
part.Effect = effect;
worldInverseTransposeMatrix = Matrix.Transpose(Matrix.Invert(mesh.ParentBone.Transform * world));
effect.Parameters["WorldInverseTranspose"].SetValue(worldInverseTransposeMatrix);
effect.Parameters["World"].SetValue(world * mesh.ParentBone.Transform);
effect.Parameters["View"].SetValue(fpsCam.ViewMatrix);
effect.Parameters["Projection"].SetValue(fpsCam.ProjectionMatrix);
}
mesh.Draw();
}
}
Ricordate nella loadContent di decommentare il caricamente dello shader diffuse
effect = Content.Load
Nel prossimo post vedremo come gestire lo shader con specular light.
IMPORTANTE:
Il materiale di questa introduzione agli shader è stato prodotto grazie a questo ottimo blog : http://rbwhitaker.wikidot.com/diffuse-lighting-shader
Mi sono dimenticato di segnalarlo nel post precedente! quindi colgo l'occasione per farlo ora.
Sorgenti:
imparandoxna_3d.4diffuse.hlsl.zip
3 commenti:
Ciao, davvero degli ottimi tutorial.
Ho una sola domanda da farti :
quando utilizzo lo shader standard, con un modello da me creato viene correttamente visualizzata anche la texture.
Quando invece utilizzo uno di questi shader, ad esempio il Diffuse.fx la texture "scompare" : viene quindi visualizzato il solo modello "grezzo" e l'effetto processato correttamente.
Sai per caso a cosa è dovuto ciò e come potrei risolvere?
Ciao e grazie ancora
Per utilizzare la tua texture è necessario fare uno step in più, questi shader sostituiscono il basic Effect che utilizzi normalmente con XNA (dove appunto è definito tutto il passaggio per la definizione delle texture).
Lo step in più lo trovi qui :
http://rbwhitaker.wikidot.com/texturing-shader
ti ringrazio per la dritta ^^
Posta un commento