FAQ DirectX
FAQ DirectXConsultez toutes les FAQ
Nombre d'auteurs : 4, nombre de questions : 63, dernière mise à jour : 14 juin 2021
Une texture (au sens de DirectGraphics) n'est qu'une collection de surfaces, celles-ci représentant ses différents niveaux de mip-mapping. Une surface étant elle, en gros, une classe encapsulant un tableau de pixels.
Pour récupérer les surfaces d'une texture (généralement celle de niveau 0), il faut passer par la fonction IDirect3DTexture9::GetSurfaceLevel :
// Création de la texture
LPDIRECT3DTEXTURE9 Texture;
// ...
// Récupération de sa surface de niveau 0
LPDIRECT3DSURFACE9 Surface;
Texture->
GetSurfaceLevel(0
, &
Surface);
// Surface pointe maintenant sur la surface de niveau 0 de Texture
// On n'oublie pas de la libérer lorsqu'on n'en a plus besoin !
Surface->
Release();
Attention : une telle opération incrémente le compteur de référence de la surface, il ne faudra donc pas oublier un Surface->Release() lorsque vous n'en aurez plus besoin, sous peine de créer une fuite mémoire (voir https://jeux.developpez.com/faq/directx/?page=dgraphics_problemes#DGRAPHICS_PROBLEMES_fuites).
La mise à jour du contenu d'une texture dépend de la manière dont elle a été créée.
Si la texture fait partie du pool MANAGED, alors il faut verrouiller chaque niveau de la texture par un appel à LockRect et mettre à jour son contenu avec le CPU ; le runtime s'assurera qu'après l'appel à UnlockRect, le contenu de la texture soit mis à jour dans la copie détenue par la carte graphique (le runtime détient deux copies).
Si la texture fait partie du pool DEFAULT, alors il faut utiliser une texture intermédiaire du pool SYSTEM, la verrouiller de la même manière, puis appeler IDirect3DDevice9::UpdateTexture ou IDirect3DDevice9::UpdateSurface pour mettre à jour tout ou partie de la copie possédée par la carte graphique.
Il existe des fonctions de la bibliothèque D3DX qui pourront faire ce travail automatiquement à partir d'une copie sur disque ou préchargée en mémoire. C'est par exemple le cas de la fonction D3DXCreateTextureFromFile qui va créer à la fois un objet IDirect3DTexture9 et mettre à jour son contenu à partir d'un fichier dans un format d'image compatible (jpg, gif, bmp, dds). C'est la solution à préférer si vous ne voulez pas écrire votre propre filtre de conversion de formats d'images.
Certaines cartes graphiques sont capables de supporter les textures dont les dimensions ne sont pas des puissances de 2. Pour détecter ce support (ou son absence), il suffit de tester le cap D3DPTEXTURECAPS_POW2. S'il n'est pas activé, alors vous pouvez créer des textures de dimensions quelconques.
Attention cependant, sur certaines cartes (la plupart à l'heure actuelle) ce support implique quelques restrictions quant à ces textures :
- le seul mode d'adressage utilisable est D3DTADDRESS_CLAMP ;
- le mipmapping est inutilisable ;
- le « wrapping » est désactivé (D3DRS_WRAPn mis à 0) ;
- les formats compressés (D3DFMT_DXT1 -> D3DFMT_DXT5) sont inutilisables ;
- il existe également certaines restrictions sur les shaders (par exemple, une telle texture ne pourra pas être lue par une instruction bem ou texm3x3 - c'est ce que l'on appelle les dependent reads).
Afin de savoir si votre carte supporte les textures non puissance de 2 avec ou sans restrictions, il faut tester cette fois la valeur du cap D3DPTEXTURECAPS_NONPOW2CONDITIONAL. S'il n'est pas activé alors votre carte n'a aucun support pour les textures non puissance de 2 ; si au contraire il l'est, alors vous pourrez supporter ces textures, mais avec les restrictions énoncées précédemment.
// Récupération des caps Dx9
D3DCAPS9 Caps;
m_Device->
GetDeviceCaps(&
Caps);
if
(Caps.TextureCaps &
D3DPTEXTURECAPS_POW2)
{
// ATTENTION caps spécifique pour les textures non puissance de deux
if
(Caps.TextureCaps &
D3DPTEXTURECAPS_NONPOW2CONDITIONAL)
{
// Votre carte supporte les textures non puissance de 2, mais est soumise aux restrictions énoncées
}
else
{
// Votre carte est trop vieille et n'a aucun support pour les textures non puissance de 2
}
}
else
{
// Support complet de toutes les textures de toutes les dimensions
}
Il est cependant à noter qu'on préférera tout de même des textures en puissance de 2, voir ?« Pourquoi vaut-il mieux utiliser des textures dont les dimensions sont des puissances de 2 ? ».
Depuis que DirectDraw a plus ou moins disparu, il n'est plus possible d'utiliser les fonctions purement 2D qu'il offrait. Tout ceci a avantageusement été intégré à DirectGraphics, permettant d'une part de tirer parti de l'accélération matérielle et d'autre part de pouvoir utiliser les effets à la mode en 2D (shaders, alpha-blending, transformations…).
Il existe deux méthodes pour effectuer un rendu 2D avec DirectGraphics : travailler directement en coordonnées écran, et travailler avec une matrice de projection orthogonale.
1. Travailler en coordonnées écran
Les coordonnées écran, ou RHW (Reciprocal Homogeneous W) permettent d'envoyer à la carte graphique des vertices 2D, ne passant donc pas par la case « transformations 3D ». En l'occurrence, ils ignoreront complètement vos matrices de monde, de vue ou de projection et viendront s'afficher tels quels sur l'écran. Conséquence : cela sera plus rapide, mais vous ne pourrez pas bénéficier des transformations (rotations, translations, scaling…) qui faisaient si cruellement défaut à DirectDraw.
Pour travailler en coordonnées RHW, il suffit d'utiliser le FVF D3DFVF_XYZRHW (si vous utilisez encore les FVF) ou le flag D3DDECLUSAGE_POSITIONT (avec comme type D3DDECLTYPE_FLOAT4 puisqu'il s'agit de quatre coordonnées) si vous utilisez les déclarations de vertex. Vos vertices devront quant à eux fournir quatre coordonnées au lieu des trois habituelles : (x, y, z, w). Cependant, z devra quoi qu'il arrive valoir 0 et w vaudra lui 1, sous peine d'avoir un affichage incorrect. x et y seront quant à eux compris entre 0 et la hauteur/largeur actuelle.
2. Travailler avec une matrice de projection orthogonale
Cette fois on travaille bien avec des vertices 3D transformés, ce qui permet de récupérer les rotations et autres joyeusetés offertes par cette étape. L'astuce est cette fois d'utiliser une matrice de projection orthogonale, et non de perspective. Afin de la créer, il faudra utiliser la fonction D3DXMatrixOrthoLH, ou D3DXMatrixOrthoOffCenterLH si vous ne souhaitez pas avoir (0, 0) comme origine. Les coordonnées de vos vertices n'auront cette fois pas de composante w, la seule contrainte étant d'avoir un z toujours égal à 1.
En complément à l'une de ces deux méthodes, il faut également veiller à désactiver l'écriture dans le Z-buffer ainsi que le test de profondeur, qui sont inutiles en 2D et peuvent mener à des résultats incohérents.
Device->
SetRenderState(D3DRS_ZWRITEENABLE, false
);
Device->
SetRenderState(D3DRS_ZFUNC, D3DFUNC_ALWAYS);
La désactivation du filtrage de texture (pour du rendu 2D par exemple) se fait très simplement en mettant les états correspondants à D3DTEXF_NONE :
Device->
SetSamplerState(0
, D3DSAMP_MINFILTER, D3DTEXF_NONE);
Device->
SetSamplerState(0
, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
La désactivation du mipmapping se fait de la même manière :
Device->
SetSamplerState(0
, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
Pour les réactiver, il suffit de spécifier un filtre autre que D3DTEXF_NONE (voir la doc du SDK pour les différents types de filtres existant).