I-7. Tutoriel 7 : Mapping de texture et Constant Buffers ▲
I-7-a. Résumé▲
Dans le tutoriel précédent nous avons introduit l'éclairage à notre projet;
maintenant nous allons construire sur tout cela en ajoutant des textures à notre cube.
En plus, nous allons introduire le concept des constant buffers et comment ils peuvent être utilisés pour accélérer le traitement en minimisant l'usage de la bande passante.
Le résultat de ce tutoriel modifiera le cube central pour qu'il y ait une texture mappée sur lui.
Ce tutoriel conclut l'introduction des concepts basiques dans Direct3D 10.
Les tutoriaux suivants seront construits à partir de ces concepts en introduisant DXUT, le chargement de mesh, ainsi qu'un exemple de chaque shader.
I-7-b. Source▲
(SDK root)\Samples\C++\Direct3D10\Tutorials\Tutorial07
I-7-c. Mapping de texture▲
Le mapping de texture se réfere à la projection d'une image 2D sur une géométrie 3D.
On peut penser à ceci comme un emballage cadeau, en plaçant un papier décoratif par-dessus une boîte fade.
Pour faire ceci, nous devons spécifier comment les points sur la surface de la géométrie correspondent avec l'image 2D.
Le soucis est de correctement aligner les coordonnées du modèle avec la texture.
Pour des modèles complexes, il est difficile de déterminer les coordonnées pour les textures à la main.
Donc, les programmes de modélisation 3D vont généralement exporter les modèles avec les coordonnées de textures correspondantes.
Comme notre exemple est un cube, il est facile de déterminer les coordonnées nécessaires pour correspondre à la texture.
Les coordonnées de texture sont définies aux vertices et interpolées pour les pixels individuels sur la surface.
I-7-d. Creation d'un Shader Resource depuis une Texture▲
La texture est une image 2D qui est obtenue depuis un fichier et utilisée pour créer une vue shader-resource, afin qu'elle puisse être lue depuis un shader.
hr =
D3DX10CreateShaderResourceViewFromFile
(
g_pd3dDevice, L"
seafloor.dds
"
, NULL
, NULL
,
&
g_pTextureRV, NULL
);
I-7-e. Définir les coordonnées▲
Avant que l'on puisse mapper l'image sur notre cube, nous devons d'abord définir les coordonnées de textures sur chacun des vertices du cube.
Comme les images peuvent être de plusieurs tailles, le système de coordonnées utilisé a été normalisé à [0, 1].
Le coin supérieur gauche de la texture correspond à (0,0) et le coin inférieur droit mappe à (1,1).
Dans cet exemple, nous avons l'entièreté de la texture qui s'étend sur chaque côté du cube.
Cela simplifie la définition des coordonnées, sans confusion.
Cependant, il est entièrement possible de spécifier la texture pour s'étendre sur toutes les 6 faces, bien qu'il soit plus difficile de définir les points, et cela apparaîtra étiré et distorsionné.
D'abord, on met à jour la structure utilisée pour définir nos vertices pour inclure les coordonnées de texture.
struct
SimpleVertex
{
D3DXVECTOR3 Pos; // Position
D3DXVECTOR2 Tex; // Texture Coordinate
}
;
Ensuite, on met à jour l'input layout des shaders pour également inclure ces coordonnées.
// Défini l'input layout
D3D10_INPUT_ELEMENT_DESC layout[] =
{
{
L"
POSITION
"
, 0
, DXGI_FORMAT_R32G32B32_FLOAT, 0
, 0
,
D3D10_INPUT_PER_VERTEX_DATA, 0
}
,
{
L"
TEXCOORD
"
, 0
, DXGI_FORMAT_R32G32_FLOAT, 0
, 12
,
D3D10_INPUT_PER_VERTEX_DATA, 0
}
,
}
;
Comme l'input layout a changé, l'input vertex shader correspondant doit aussi être modifié pour correspondre à l'ajout.
struct
VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD;
}
;
Finalement, nous sommes prêts à inclure les coordonnées de texture dans nos sommets que nous avons definies dans le tutoriel 4. Notez que le second paramètre d'input est un D3DXVECTOR2 contenant les coordonnées de texture. Chaque sommet du cube correspondra à un coin de la texture. Ceci crée un mapping simple où chaque sommet reçoit (0,0) (0,1) (1,0) ou (1,1) comme coordonnée.
// Crée le vertex buffer
SimpleVertex vertices[] =
{
{
D3DXVECTOR3
(
-
1
.0f
, 1
.0f
, -
1
.0f
), D3DXVECTOR2
(
0
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, 1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, 1
.0f
, 1
.0f
), D3DXVECTOR2
(
1
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, 1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, -
1
.0f
, -
1
.0f
), D3DXVECTOR2
(
0
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, -
1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, -
1
.0f
, 1
.0f
), D3DXVECTOR2
(
1
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, -
1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, -
1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, -
1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, 1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, 1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, -
1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, -
1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, 1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, 1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, -
1
.0f
, -
1
.0f
), D3DXVECTOR2
(
0
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, -
1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, 1
.0f
, -
1
.0f
), D3DXVECTOR2
(
1
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, 1
.0f
, -
1
.0f
), D3DXVECTOR2
(
0
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, -
1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, -
1
.0f
, 1
.0f
), D3DXVECTOR2
(
1
.0f
, 0
.0f
) }
,
{
D3DXVECTOR3
(
1
.0f
, 1
.0f
, 1
.0f
), D3DXVECTOR2
(
1
.0f
, 1
.0f
) }
,
{
D3DXVECTOR3
(
-
1
.0f
, 1
.0f
, 1
.0f
), D3DXVECTOR2
(
0
.0f
, 1
.0f
) }
,
}
;
Quand on découpe la texture, nous devons la moduler avec une couleur de materiau pour la géométrie se trouvant en dessous.
I-7-f. Attribuer une texture en tant que Shader Resource▲
Une texture est un objet comme les matrices et les vecteurs que nous avons vu dans les tutoriaux précédents. Avant qu'ils ne puissent être utilisés par le shader, ils ont besoin d'être définis dans l'effet. Ceci peut être effectué en obtenant un pointeur vers la variable, et ensuite, le définir en tant que shader resource.
g_pDiffuseVariable =
g_pEffect->
GetVariableByName
(
"
txDiffuse
"
)->
AsShaderResource
(
);
Après que le pointeur ressource ait été obtenu, il peut être utilisé pour accrocher la vue ressource texture 2D que nous avons initialisée plus tôt.
g_pDiffuseVariable->
SetResource
(
g_pTextureRV );
On continue, maintenant nous sommes prêts à utiliser la texture à l'intérieur du shader.
I-7-g. Appliquer la Texture (fx)▲
Pour actuellement mapper la texture au dessus de la géométrie, on appellera une fonction de recherche de texture à l'intérieur du pixel shader. La fonction, Sample effectuera un recherche de texture d'une texture 2D pour ensuite retourner la couleur découpée. Le pixel shader montré ci-dessous appelle cette fonction et multiplie par la couleur mesh soulignée (ou la couleur matériau) et ensuite produit la couleur finale.
- txDiffuse est l'objet stockant notre texture que l'on a passée depuis le code plus haut, quand on lui a attribué la vue resource g_pTextureRV
- samLinear sera décrit plus bas, ce sont les spécifications découpées pour la recherche de texture.
- input.Tex sont les coordonnées de la texture que nous avons spécifiées dans la source
// Pixel Shader
float4 PS
(
PS_INPUT input) : SV_Target
{
return
txDiffuse.Sample
(
samLinear, input.Tex ) *
vMeshColor;
}
La variable samLinear est une structure qui contient les informations pour dire au pixel shader comment découper la texture fournie. Dans notre cas, on a un filtre linéaire, et nos deux adresses de synthèse. Ces configurations seront utiles pour de simples textures, et une explication des filtres dépasse le but de ce tutoriel.
SamplerState samLinear
{
Filter =
MIN_MAG_MIP_LINEAR;
AddressU =
Wrap;
AddressV =
Wrap;
}
;
Une autre chose que nous devons nous rappeler de faire est de passer les coordonnées de texture dans le vertex shader, ou sinon les données sont perdues quand elles atteignent le pixel shader. . Ici, on copie juste les coordonnées d'input vers l'output, et on laisse l'hardware gérer le reste.
// 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.Tex =
input.Tex;
return
output;
}
I-7-h. Constant Buffers▲
En commençant avec Direct3D 10, une application peut utiliser un constant buffer pour définir les shader constants (shader variables).
Les constant buffers sont déclarés en utilisant une syntaxe similaire aux structures de style C.
La meilleure façon d'utiliser les constant buffers est d'organiser les shader variables dans des constant buffers basés sur leur fréquence de mise à jour.
Cela permet à une application de minimiser la bande passante nécessaire pour mettre à jour les shader constants.
Comme exemple, ce tutoriel groupe les constantes dans 3 structures : une pour les variables qui changent à chaque frame, une pour les variables qui changent seulement quand une taille de fenêtre est changée, et une pour les variables qui sont définies une seule fois et ensuite plus modifiées.
cbuffer cbNeverChanges
{
matrix View;
}
;
cbuffer cbChangeOnResize
{
matrix Projection;
}
;
cbuffer cbChangesEveryFrame
{
matrix World;
float4 vMeshColor;
}
;