III-12. Tutoriel 12 : Pixel shaders▲
III-12-a. Résumé▲
Ce tutoriel se concentre sur le pixel shader et ses possibilités. Il y a plusieurs choses qui peuvent être accomplies avec le pixel shader, et quelques unes des fonctions les plus habituelles sont listées. Le pixel shader appliquera un environment map à l'objet.
À la fin de ce tutoriel, le modèle deviendra brillant, et il reflètera son environnement entourant.
III-12-b. Source▲
SDK root\Samples\C++\Direct3D10\Tutorials\Tutorial12
III-12-c. Pixel shader▲
Les pixel shaders sont utilisés pour manipuler la couleur finale d'un pixel avant qu'il n'atteigne l'output merger. Chaque pixel qui est affiché sera passé au travers de ce shader avant la sortie. Une fois que le pixel est passé au travers du pixel shader, les seules opérations qui peuvent être effectuées sont celles effectuées par l'output merger, telles que l'alpha blending, le test de profondeur, le stencil testing, et ainsi de suite.
Le pixel shader a évolué depuis le mapping de texture que l'on trouvait dans les hardwares précédents. Au lieu d'un simple texture de consultation, il est possible de calculer la couleur finale depuis des sources multiples, ainsi que de l'altérer selon les données du sommet. Pour des applications générales, cependant, un pixel shader effectue de multiples consultations sur différentes textures.
III-12-d. Environment Map▲
Sur des surfaces réfléchissantes, il n'est pas possible de juste faire une texture de consultation sur des coordonnées de texture fixes pour déterminer la couleur finale. Cela est du au fait que quand l'objet ou le spectateur bouge, le reflet change. Donc, nous devons mettre à jour la réflexion chaque fois que quelque chose bouge.
Une méthode efficace pour faire croire à l'observateur qu'il y a une réflexion dynamique sur l'environnement est de générer une texture spéciale qui entoure l'objet. Cette texture s'appelle un cube map. Un cube map est effectivement de placer un objet au milieu d'un cube, avec chaque face du cube devenant une texture. Un environment map est un cube map qui a des textures qui correspondent à la vue de l'environnement sur cette face. Si l'environnement est statique, ces environment maps peuvent être pré-générés. Si l'environnement est dynamique, le cubemap doit être mis à jour à la volée. Voir l'exemple CubeMapGS pour une illustration de comment effectuer ceci.
Depuis cet environment map, on peut calculer ce qu'une caméra verra comme reflet. D'abord on peut trouver la direction par laquelle la caméra voit l'objet, et de cela, reflété la normale de chaque pixel et effectuer une consultation basé sur ce vecteur reflété.
III-12-e. Configurer l'Environment Map▲
La configuration de l'environment map n'est pas le but de ce tutoriel. La procédure à suivre est très similaire à celle d'une map texture normale. Voir le Tutoriel 7, Mapping de texture et Constant Buffers, pour une explication de comment initialiser correctement une texture map et ses vues ressources associées.
// Charge l'Environment Map
ID3D10Resource *
pResource =
NULL
;
V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH,
L"Lobby
\\
LobbyCube.dds"
) );
V_RETURN( D3DX10CreateTextureFromFile( pd3dDevice, str, NULL
, NULL
,
&
pResource ) );
if
(pResource)
{
g_pEnvMap =
(ID3D10Texture2D*
)pResource;
pResource->
Release();
D3D10_TEXTURECUBE_DESC desc;
g_pEnvMap->
GetDesc( &
desc );
D3D10_SHADER_RESOURCE_VIEW_DESC SRVDesc;
ZeroMemory( &
SRVDesc, sizeof
(SRVDesc) );
SRVDesc.Format =
desc.Format;
SRVDesc.ViewDimension =
D3D10_SRV_DIMENSION_TEXTURECUBE;
SRVDesc.TextureCube.MipLevels =
desc.MipLevels;
SRVDesc.TextureCube.MostDetailedMip =
0
;
V_RETURN(pd3dDevice->
CreateShaderResourceView( g_pEnvMap, &
SRVDesc,
&
g_pEnvMapSRV ));
}
// Défini l'Environment Map
g_pEnvMapVariable->
SetResource( g_pEnvMapSRV );
III-12-f. Implémenter le Lookup▲
Nous allons parcourir l'algorithme simple décrit plus haut et effectuer un lookup propre d'un environment map. Les calculs sont effectués dans le vertex shader et ensuite interpolés vers le pixel shader pour le lookup. Il est préférable de le calculer dans le vertex shader et interpoler vers le shader de pixel car il y a moins de calculs requis.
Pour calculer le vecteur refléter pour le lookup, nous avons besoins de deux informations. D'abord, la normale du pixel en question, ensuite, la direction de l'oeil.
Car l'opération est effectuée dans l'espace vue (le view space), nous devons transformer la normale du pixel vers l'espace de vue correcte.
float3 viewNorm = mul( input.Norm, (float3x3)View );
Ensuite, nous devons trouver la direction de la caméra. Notez, cependant, que nous sommes déjà en view space, et donc, la direction de la caméra est celle-ci sur l'axe Z (0,0,-1.0), car nous regardons directement vers l'objet.
Maintenant que nous avons nos deux pièces d'informations, on peut calculer la réflexion du vecteur avec la commande reflect. La variable ViewR est utilisée pour stocker la réflexion résultante pour le traitement dans le pixel shader.
output.ViewR =
reflect( viewNorm, float3(0
,0
,-
1.0
) );
Une fois que le vecteur correcte a été calculé dans le vertex shader, on peut traiter l'environment map dans le pixel shader. Ceci est effectué en appelant une fonction toute faite pour échantillonner l'environment map et retourner la valeur couleur depuis cette texture.
// Charge la texture environment map
float4 cReflect = g_txEnvMap.Sample( samLinearClamp, viewR );
Car tout est normalisé (et les coordonnées de textures ont une portée de 0 à 1), les coordonnées x et y correspondront directement aux coordonnées de textures requises pour le lookup.
En bonus, on inclut le code pour faire la texture lookup plate original, et on calcule cela en tant que terme diffus. Pour jouer avec le taux de réflexion, vous pouvez moduler cReflect par un facteur de mise à l'échelle par rapport à cDiffuse et expérimenter avec les résultats. Vous pouvez avoir un personnage qui reflète beaucoup ou un personnage quelque peu terne.