Intro :
Dans cette quatrième partie, nous allons apprendre à intégrer une lumière afin d’éclairer notre cube.
Prérequis :
Avoir suivi la troisième partie de ce tutoriel.
Quatrième partie :
Afficher un petit cube éclairé d’une lumière.
Explications :
Nous allons utiliser cette structure de vertex :
struct SimpleVertex
{
D3DXVECTOR3 Pos;
D3DXVECTOR2 Tex;
D3DXVECTOR3 Normal;
};
Elle contient un attribut supplémentaire par rapport à la partie 3 : la normale.
La normale d’un vertex est un vecteur perpendiculaire à celui-ci ; nous l’utiliserons afin de calculer le degré de nuance de lumière à cet endroit.
C’est par ce principe que nous effectuerons globalement le calcul lumineux.
Voir cet article pour comprendre comment modéliser la lumière dans un rendu 3D.
Nous allons utiliser ce programme HLSL – partie4.fx :
//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
matrix World;
matrix View;
matrix Projection;
float4 vLightDir;
Texture2D txDiffuse;
SamplerState samLinear
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD;
float3 Norm : NORMAL;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
float3 Norm : TEXCOORD1;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output = (PS_INPUT)0;
output.Pos = mul( input.Pos, World );
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = mul( input.Norm, World );
output.Tex = input.Tex;
return output;
}
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_Target
{
float4 finalColor = (saturate( dot( (float3)vLightDir, input.Norm) )
* txDiffuse.Sample( samLinear, input.Tex ));
finalColor.a = 1;
return finalColor;
}
//--------------------------------------------------------------------------------------
technique10 Render
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_4_0, PS() ) );
}
}
Si vous ne comprenez rien à ce programme : consulter cet article.
Il faut aussi connaître la notion de matrices et le principe de transformations de coordonnées
pour comprendre ce code : voir cet article.
Dans votre fichier D3D10Renderer.h, rajoutez :
// Variables matrices en cours ID3D10EffectMatrixVariable* m_pWorldVariable; ID3D10EffectMatrixVariable* m_pViewVariable; ID3D10EffectMatrixVariable* m_pProjectionVariable; ID3D10EffectVectorVariable* m_pLightDirVariable; ID3D10EffectVectorVariable* m_pLightColorVariable; // Variable texture en cours ID3D10EffectShaderResourceVariable* m_pDiffuseVariable;
Ce sont les variables que l’on enverra au shader HLSL.
Ensuite dans votre fichier D3D10Renderer.cpp à la fonction Initialize(bool bFullScreen), rajoutez :
/******************** Partie 2 & 3 & 4 ********************/
DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
// Permet d'afficher les éventuelles erreurs de la compilation d'un shader
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif
// Pour la création du shader, on le compile
hr = D3DX10CreateEffectFromFile(L"Partie4.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0, m_pd3dDevice, NULL,
NULL, &m_pEffect, NULL, NULL);
if (FAILED(hr))
return false;
// On acquiert la technique du shader HLSL
m_pTechnique = m_pEffect->GetTechniqueByName("Render");
// On acquiert les variables du shader HLSL
// cela permettra de faire le lien avec l'application et le programme shader HLSL
m_pWorldVariable = m_pEffect->GetVariableByName("World")->AsMatrix();
m_pViewVariable = m_pEffect->GetVariableByName("View")->AsMatrix();
m_pProjectionVariable = m_pEffect->GetVariableByName("Projection")->AsMatrix();
m_pLightDirVariable = m_pEffect->GetVariableByName( "vLightDir" )->AsVector();
m_pDiffuseVariable = m_pEffect->GetVariableByName("txDiffuse")->AsShaderResource();
/******************/
// On définit la structure d'un vertex, ici 3 attributs sont déclarés
D3D10_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = sizeof( layout ) / sizeof( layout[0] );
D3D10_PASS_DESC PassDesc;
m_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
hr = m_pd3dDevice->CreateInputLayout(layout, numElements, PassDesc.pIAInputSignature,
PassDesc.IAInputSignatureSize, &m_pVertexLayout);
if (FAILED(hr))
return false;
m_pd3dDevice->IASetInputLayout(m_pVertexLayout);
On créé le vertex buffer d’un cube : il est composé de la position, des coordonées de la texture et des normales de chaque vertex.
Les voici :
// Déclaration des vertices
SimpleVertex vertices[] =
{
{ D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },
{ D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },
{ D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },
{ D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },
{ D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },
{ D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
{ D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
{ D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
{ D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
};
// Déclaration des indices
DWORD indices[] =
{
3,1,0,
2,1,3,
6,4,5,
7,4,6,
11,9,8,
10,9,11,
14,12,13,
15,12,14,
19,17,16,
18,17,19,
22,20,21,
23,20,22
};
Toujours dans votre fichier D3D10Renderer.cpp à la fonction Initialize(bool bFullScreen), rajoutez :
D3DXMatrixIdentity(&m_worldMatrix); D3DXVECTOR3 Eye(0.0f, 3.0f, -6.0f); D3DXVECTOR3 At(0.0f, 0.0f, 0.0f); D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f); D3DXMatrixLookAtLH(&m_viewMatrix, &Eye, &At, &Up); // Initialize the projection matrix D3DXMatrixPerspectiveFovLH( &m_projectionMatrix, ( float )D3DX_PI * 0.25f, width / ( FLOAT )height, 0.1f, 100.0f ); // Update Variables that never change m_pViewVariable->SetMatrix( ( float* )&m_viewMatrix ); m_pProjectionVariable->SetMatrix( ( float* )&m_projectionMatrix ); m_pDiffuseVariable->SetResource( m_pTextureRV );
Toujours dans votre fichier D3D10Renderer.cpp :
void D3D10Renderer::Render()
{
// Couleur du fond de rendu
static float afClearColor[4] = {0.0f, 0.125f, 0.3f, 1.0f};
// Efface la surface de rendu
m_pd3dDevice->ClearRenderTargetView(m_pRenderTargetView, afClearColor);
/*********** Donnée d'une variable de temps ***********/
static float t = 0.0f;
static DWORD dwTimeStart = 0;
DWORD dwTimeCur = GetTickCount();
if( dwTimeStart == 0 )
dwTimeStart = dwTimeCur;
t = ( dwTimeCur - dwTimeStart ) / 1000.0f;
/*******************************************************/
// Direction de la lumière
D3DXVECTOR4 vLightDirs( -1.577f, 0.577f, -0.577f, 1.0f );
D3DXMATRIX mRotate;
D3DXVECTOR4 vOutDir;
D3DXMatrixRotationZ( &mRotate, -2.0f * t );
D3DXVec3Transform( &vLightDirs, ( D3DXVECTOR3* )&vLightDirs, &mRotate );
// On met à jour les variables du shader HLSL
m_pWorldVariable->SetMatrix((float*)&m_worldMatrix);
m_pLightDirVariable->SetFloatVectorArray((float*)vLightDirs, 0, 2);
/**********************/
// Affiche le cube
D3D10_TECHNIQUE_DESC techDesc;
m_pTechnique->GetDesc(&techDesc);
for (UINT p = 0; p < techDesc.Passes; ++p)
{
m_pTechnique->GetPassByIndex( p )->Apply( 0 );
m_pd3dDevice->DrawIndexed( 36, 0, 0 );
}
/**********************/
// On affiche le front buffer
m_pSwapChain->Present( 0, 0 );
}
Résumé :
Nous avons intégré une lumière près de notre cube afin de l’éclairer.
Voici l’archive du code complet pour cette partie : DirectX 10 Tutoriel – Partie 4.zip


