Partager via


Mappage direct de Texels à pixels (Direct3D 9)

Lors du rendu de la sortie 2D à l’aide de sommets prédéformés, vous devez veiller à ce que chaque zone de texel corresponde correctement à une seule zone de pixels, sinon une distorsion de texture peut se produire. En comprenant les principes de base du processus que Direct3D suit lors de la rastérisation et de la texturation des triangles, vous pouvez vous assurer que votre application Direct3D affiche correctement la sortie 2D.

illustration d’un affichage de résolution 6x6

Le diagramme précédent montre des pixels modélisés sous forme de carrés. En réalité, cependant, les pixels sont des points, pas des carrés. Chaque carré du diagramme précédent indique la zone éclairée par le pixel, mais un pixel n’est toujours qu’un point au centre d’un carré. Cette distinction, même si elle semble petite, est importante. Une meilleure illustration du même affichage est illustrée dans le diagramme suivant.

illustration d’un affichage composé de pixels

Le diagramme précédent montre correctement chaque pixel physique en tant que point au centre de chaque cellule. La coordonnée d’espace de l’écran (0, 0) se trouve directement au pixel en haut à gauche, et donc au centre de la cellule en haut à gauche. Le coin supérieur gauche de l’affichage se trouve donc à (-0,5, -0,5), car il s’agit de 0,5 cellules à gauche et de 0,5 cellules vers le haut du pixel supérieur gauche. Direct3D affiche un quad avec des coins à (0, 0) et (4, 4), comme illustré dans l’illustration suivante.

illustration d’un plan d’un quad non asstérisé compris entre (0, 0) et (4, 4)

L’illustration précédente montre où se trouve le quad mathématique par rapport à l’affichage, mais ne montre pas à quoi ressemblera le quad une fois que Direct3D le rastérise et l’envoie à l’affichage. En fait, il est impossible pour un affichage raster de remplir le quad exactement comme indiqué, car les bords du quad ne coïncident pas avec les limites entre les cellules de pixels. En d’autres termes, étant donné que chaque pixel ne peut afficher qu’une seule couleur, chaque cellule de pixel n’est remplie que d’une seule couleur ; si l’affichage devait restituer le quad exactement comme indiqué, les cellules de pixels le long du bord du quad doivent afficher deux couleurs distinctes : le bleu où le quad est couvert et le blanc où seul l’arrière-plan est visible.

Au lieu de cela, le matériel graphique est chargé de déterminer quels pixels doivent être remplis pour se rapprocher du quad. Ce processus est appelé rastérisation et est détaillé dans Règles de rastérisation (Direct3D 9). Dans ce cas particulier, le quad rastérisé est illustré dans l’illustration suivante.

illustration d’un quad non texturé tiré de (0,0) à (4,4)

Notez que le quad passé à Direct3D a des coins à (0, 0) et (4, 4), mais que la sortie rastérisée (l’illustration précédente) a des coins à (-0,5,-0,5) et (3.5,3.5). Comparez les deux illustrations précédentes pour connaître les différences de rendu. Vous pouvez voir que ce que l’affichage affiche réellement est la taille correcte, mais a été décalé de -0,5 cellules dans les directions x et y. Toutefois, à l’exception des techniques multi-échantillonnage, il s’agit de la meilleure approximation possible du quad. (Voir l’exemple Antialias pour une couverture complète du multi-échantillonnage.) Sachez que si le rastériseur a rempli chaque cellule que le quad a traversée, la zone résultante serait de dimension 5 x 5 au lieu des 4 x 4 souhaités.

Si vous partez du principe que les coordonnées de l’écran proviennent du coin supérieur gauche de la grille d’affichage au lieu du pixel supérieur gauche, le quad apparaît exactement comme prévu. Toutefois, la différence devient nette lorsque le quad reçoit une texture. L’illustration suivante montre la texture 4 x 4 que vous mapperez directement sur le quad.

illustration d’une texture 4x4

Étant donné que la texture est de 4 x 4 texels et que le quad est de 4 x 4 pixels, vous pouvez vous attendre à ce que le quad texturé apparaisse exactement comme la texture, quel que soit l’emplacement sur l’écran où le quad est dessiné. Toutefois, ce n’est pas le cas; même de légères modifications de position influencent la façon dont la texture est affichée. L’illustration suivante montre comment un quad compris entre (0, 0) et (4, 4) est affiché après avoir été rastérisé et texturé.

illustration d’un quad texturé tiré de (0, 0) et (4, 4)

Le quad dessiné dans l’illustration précédente montre la sortie texturée (avec un mode de filtrage linéaire et un mode d’adressage de pince) avec le contour rastérisé superposé. Le reste de cet article explique exactement pourquoi la sortie ressemble à ce qu’elle fait au lieu de ressembler à la texture, mais pour ceux qui veulent la solution, voici : Les bords du quad d’entrée doivent se trouver sur les lignes limites entre les cellules de pixels. En déplaçant simplement les coordonnées x et y quad de -0,5 unités, les cellules de texel couvrent parfaitement les cellules de pixels et le quad peut être parfaitement recréé à l’écran. (La dernière illustration de cette rubrique montre le quad aux coordonnées corrigées.)

Les détails de la raison pour laquelle la sortie rastérisée n’a qu’une légère ressemblance avec la texture d’entrée sont directement liés à la façon dont les adresses Direct3D et les textures d’échantillons. Ce qui suit suppose que vous avez une bonne compréhension de l’espace de coordonnées de texture et du filtrage de texture bilinéaire.

Pour revenir à notre examen de la sortie de pixel étrange, il est judicieux de tracer la couleur de sortie jusqu’au nuanceur de pixels : le nuanceur de pixels est appelé pour chaque pixel sélectionné pour faire partie de la forme rastérisée. Le quad bleu uni représenté dans une illustration précédente pourrait avoir un nuanceur particulièrement simple :

float4 SolidBluePS() : COLOR
{ 
    return float4( 0, 0, 1, 1 );
} 

Pour le quad texturé, le nuanceur de pixels doit être légèrement modifié :

texture MyTexture;

sampler MySampler = 
sampler_state 
{ 
    Texture = <MyTexture>;
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU = Clamp;
    AddressV = Clamp;
};

float4 TextureLookupPS( float2 vTexCoord : TEXCOORD0 ) : COLOR
{
    return tex2D( MySampler, vTexCoord );
} 

Ce code suppose que la texture 4 x 4 est stockée dans MyTexture. Comme indiqué, l’échantillonneur de texture MySampler est défini pour effectuer un filtrage bilinéaire sur MyTexture. Le nuanceur de pixels est appelé une fois pour chaque pixel rastérisé, et chaque fois que la couleur retournée correspond à la couleur de texture échantillonnée sur vTexCoord. Chaque fois que le nuanceur de pixels est appelé, l’argument vTexCoord est défini sur les coordonnées de texture de ce pixel. Cela signifie que le nuanceur demande à l’échantillonneur de texture la couleur de texture filtrée à l’emplacement exact du pixel, comme indiqué dans l’illustration suivante.

illustration des emplacements d’échantillonnage pour les coordonnées de texture

La texture (illustrée en superposition) est échantillonnée directement à l’emplacement des pixels (sous forme de points noirs). Les coordonnées de texture ne sont pas affectées par la rastérisation (elles restent dans l’espace d’écran projeté du quad d’origine). Les points noirs indiquent où se trouvent les pixels de rastérisation. Les coordonnées de texture à chaque pixel sont facilement déterminées en interpolant les coordonnées stockées à chaque sommet : le pixel à (0,0) coïncide avec le sommet à (0, 0) ; par conséquent, les coordonnées de texture à ce pixel sont simplement les coordonnées de texture stockées à ce sommet, UV (0,0, 0,0). Pour le pixel à (3, 1), les coordonnées interpolées sont UV (0,75, 0,25), car ce pixel se trouve aux trois quarts de la largeur de la texture et au quart de sa hauteur. Ces coordonnées interpolées sont celles qui sont transmises au nuanceur de pixels.

Les texels ne s’alignent pas avec les pixels de cet exemple ; chaque pixel (et donc chaque point d’échantillonnage) est positionné à l’angle de quatre texels. Étant donné que le mode de filtrage est défini sur Linéaire, l’échantillonneur effectue la moyenne des couleurs des quatre texels partageant ce coin. Cela explique pourquoi le pixel censé être rouge est en fait trois-quarts gris plus un quart rouge, le pixel censé être vert est un demi-gris plus un quart rouge plus un quatrième vert, et ainsi de suite.

Pour résoudre ce problème, il vous suffit de mapper correctement le quad aux pixels auxquels il sera rastérisé, et ainsi mapper correctement les texels aux pixels. L’illustration suivante montre les résultats du dessin du même quad entre (-0,5, -0,5) et (3,5, 3,5), qui est le quad prévu dès le départ.

illustration d’un quad texturé qui correspond au quad rastérisé

L’illustration précédente montre que le quad (présenté entre (-0,5, -0,5) et (3,5, 3,5)) correspond exactement à la zone rastérisée.

Récapitulatif

En résumé, les pixels et les texels sont en fait des points, pas des blocs solides. L’espace d’écran provient du pixel supérieur gauche, mais les coordonnées de texture proviennent du coin supérieur gauche de la grille de la texture. Plus important encore, n’oubliez pas de soustraire 0,5 unité des composants x et y de vos positions de vertex lorsque vous travaillez dans un espace d’écran transformé afin d’aligner correctement les texels avec des pixels.

Le code suivant est un exemple de compensation des sommets d’un carré de 256 par 256 pour afficher correctement une texture 256 par 256 dans un espace d’écran transformé.

//define FVF with vertex values in transformed screen space
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1)

struct CUSTOMVERTEX
{
    FLOAT x, y, z, rhw; // position
    FLOAT tu, tv;       // texture coordinates
};

//unadjusted vertex values
float left = 0.0f;
float right = 255.0f;
float top = 0.0f;
float bottom = 255.0f;


//256 by 256 rectangle matching 256 by 256 texture
CUSTOMVERTEX vertices[] =
{
    { left,  top,    0.5f, 1.0f, 0.0f, 0.0f}, // x, y, z, rhw, u, v
    { right, top,    0.5f, 1.0f, 1.0f, 0.0f},
    { right, bottom, 0.5f, 1.0f, 1.0f, 1.0f},
    { left,  top,    0.5f, 1.0f, 0.0f, 0.0f},
    { right, bottom, 0.5f, 1.0f, 1.0f, 1.0f},
    { left,  bottom, 0.5f, 1.0f, 0.0f, 1.0f},
    
};
//adjust all the vertices to correctly line up texels with pixels 
for (int i=0; i<6; i++)
{
    vertices[i].x -= 0.5f;
    vertices[i].y -= 0.5f;
}

Coordonnées de texture