Share via


Asignar elementos de textura directamente a píxeles (Direct3D 9)

Al representar la salida 2D mediante vértices transformados previamente, se debe tener cuidado para asegurarse de que cada área de elementos de textura se corresponda correctamente con un área de píxel único; de lo contrario, se puede producir distorsión de textura. Al comprender los conceptos básicos del proceso que sigue Direct3D al rasterizar y texturizar triángulos, puedes asegurarte de que la aplicación Direct3D representa correctamente la salida 2D.

ilustración de una pantalla de resolución 6x6

En el diagrama anterior se muestran los píxeles que se modelan como cuadrados. Sin embargo, en realidad, los píxeles son puntos, no cuadrados. Cada cuadrado del diagrama anterior indica el área iluminada por el píxel, pero un píxel siempre es un punto en el centro de un cuadrado. Esta distinción, aunque aparentemente pequeña, es importante. En el diagrama siguiente se muestra una ilustración mejor de la misma pantalla.

ilustración de una pantalla que consta de píxeles

El diagrama anterior muestra correctamente cada píxel físico como punto en el centro de cada celda. La coordenada del espacio de pantalla (0, 0) se encuentra directamente en el píxel superior izquierdo y, por tanto, en el centro de la celda superior izquierda. Por lo tanto, la esquina superior izquierda de la pantalla está en (-0,5, -0,5) porque es de 0,5 celdas a la izquierda y 0,5 celdas hacia arriba desde el píxel superior izquierdo. Direct3D representará un quad con esquinas en (0, 0) y (4, 4), como se muestra en la ilustración siguiente.

ilustración de un esquema de un cuadrángulo sinrasterizar entre (0, 0) y (4, 4)

En la ilustración anterior se muestra dónde se encuentra el cuadrante matemático en relación con la pantalla, pero no muestra el aspecto que tendrá el cuadrángulo una vez que Direct3D lo rasteriza y lo envía a la pantalla. De hecho, es imposible que una pantalla ráster rellene el cuadrante exactamente como se muestra porque los bordes del cuadrángulo no coinciden con los límites entre celdas de píxeles. En otras palabras, dado que cada píxel solo puede mostrar un solo color, cada celda de píxel se rellena con un solo color; Si la pantalla fuera a representar el quad exactamente como se muestra, las celdas de píxeles a lo largo del borde del cuadrángulo necesitarían mostrar dos colores distintos: azul donde cubierto por el quad y blanco donde solo está visible el fondo.

En su lugar, el hardware gráfico se encarga de determinar qué píxeles se deben rellenar para aproximarse al cuadrángulo. Este proceso se denomina rasterización y se detalla en Reglas de rasterización (Direct3D 9). En este caso en particular, el cuadrilátero rasterizado se muestra en la ilustración siguiente.

ilustración de un cuadrante sin texto dibujado de (0,0) a (4,4)

Tenga en cuenta que el quad pasado a Direct3D tiene esquinas en (0, 0) y (4, 4), pero la salida rasterizada (la ilustración anterior) tiene esquinas en (-0,5,-0,5) y (3,5,3,5). Compare las dos ilustraciones anteriores para ver las diferencias de representación. Puede ver que lo que realmente representa la pantalla es el tamaño correcto, pero se ha desplazado por -0,5 celdas en las direcciones x e y. Sin embargo, a excepción de las técnicas de muestreo múltiple, esta es la mejor aproximación posible al cuadrante. (Consulte el Ejemplo de antialias para obtener una cobertura exhaustiva del muestreo múltiple). Tenga en cuenta que si el rasterizador rellena todas las celdas cruzadas, el área resultante sería de la dimensión 5 x 5 en lugar de la deseada 4 x 4.

Si supone que las coordenadas de pantalla se originan en la esquina superior izquierda de la cuadrícula de pantalla en lugar del píxel superior izquierdo, el cuadrante aparece exactamente como se esperaba. Sin embargo, la diferencia se vuelve clara cuando se le asigna una textura al cuadrátrilo. En la ilustración siguiente se muestra la textura de 4 x 4 que asignará directamente al cuadrante.

ilustración de una textura de 4 x 4

Dado que la textura es de 4 x 4 elementos de textura y el cuadrante es de 4 x 4 píxeles, es posible que esperes que el cuadrante con textura aparezca exactamente igual a la textura independientemente de la ubicación en la pantalla donde se dibuja el quad. Sin embargo, esto no es el caso; incluso pequeños cambios en la posición influyen en cómo se muestra la textura. En la ilustración siguiente se muestra cómo se muestra un cuadrángulo entre (0, 0) y (4, 4) después de ser rasterizado y texturado.

ilustración de un cuadrante con textura dibujado de (0, 0) y (4, 4)

El cuadrángulo dibujado en la ilustración anterior muestra la salida con textura (con un modo de filtrado lineal y un modo de direccionamiento de fijación) con el contorno rasterizado superpuesto. En el resto de este artículo se explica exactamente por qué la salida tiene el aspecto que hace en lugar de parecerse a la textura, pero para aquellos que quieren la solución, aquí es: Los bordes del quad de entrada deben estar en las líneas de límite entre celdas de píxeles. Al simplemente cambiar las coordenadas x e y quad por -0,5 unidades, las celdas de textura cubrirán perfectamente las celdas de píxeles y el quad se puede volver a crear perfectamente en la pantalla. (En la última ilustración de este tema se muestra el cuadrante en las coordenadas corregidas).

Los detalles de por qué la salida rasterizada solo tiene una ligera similitud con la textura de entrada están directamente relacionadas con la forma en que las texturas de muestras y direcciones de Direct3D. Lo siguiente supone que tiene una buena comprensión del espacio de coordenadas de textura y el filtrado de texturas bilineal.

Volviendo a nuestra investigación de la salida de píxeles extraña, tiene sentido rastrear el color de salida de nuevo al sombreador de píxeles: se llama al sombreador de píxeles para que cada píxel seleccionado forme parte de la forma rasterizada. El cuadrado azul sólido que se muestra en una ilustración anterior podría tener un sombreador particularmente sencillo:

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

Para el quad con textura, el sombreador de píxeles debe cambiarse ligeramente:

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 );
} 

Ese código supone que la textura 4 x 4 se almacena en MyTexture. Como se muestra, el sampler de textura MySampler se establece para realizar el filtrado bilineal en MyTexture. Se llama al sombreador de píxeles una vez para cada píxel rasterizado y cada vez que el color devuelto es el color de textura muestreado en vTexCoord. Cada vez que se llama al sombreador de píxeles, el argumento vTexCoord se establece en las coordenadas de textura en ese píxel. Esto significa que el sombreador solicita al muestreador de textura el color de textura filtrado en la ubicación exacta del píxel, tal como se detalla en la ilustración siguiente.

ilustración de ubicaciones de muestreo para coordenadas de textura

La textura (mostrada superpuesta) se muestrea directamente en ubicaciones de píxeles (se muestra como puntos negros). Las coordenadas de textura no se ven afectadas por la rasterización (permanecen en el espacio de pantalla proyectado del quad original). Los puntos negros muestran dónde están los píxeles de rasterización. Las coordenadas de textura en cada píxel se determinan fácilmente interpolando las coordenadas almacenadas en cada vértice: el píxel en (0,0) coincide con el vértice en (0, 0); por lo tanto, las coordenadas de textura en ese píxel son simplemente las coordenadas de textura almacenadas en ese vértice, UV (0,0, 0,0). Para el píxel en (3, 1), las coordenadas interpoladas son UV (0,75, 0,25) porque ese píxel se encuentra en tres cuartos del ancho de la textura y un cuarto de su altura. Estas coordenadas interpoladas son las que se pasan al sombreador de píxeles.

Los elementos de textura no se alinean con los píxeles de este ejemplo; cada píxel (y, por tanto, cada punto de muestreo) se coloca en la esquina de cuatro elementos de textura. Dado que el modo de filtrado se establece en Lineal, el muestreador promediará los colores de los cuatro elementos de textura que comparten esa esquina. Esto explica por qué el píxel que se espera que sea rojo es realmente de tres cuartos gris más un cuarto rojo, el píxel que se espera que sea verde es de una mitad gris más un cuarto rojo más un cuarto verde, etc.

Para solucionar este problema, todo lo que debe hacer es asignar correctamente el cuadrante a los píxeles a los que se rasterizará y, por tanto, asignar correctamente los elementos de textura a píxeles. En la ilustración siguiente se muestran los resultados del dibujo del mismo cuadrángulo entre (-0,5, -0,5) y (3,5, 3,5), que es el quad previsto desde el principio.

Ilustración de un cuadrángulo con textura que coincide con el cuadrángulo rasterizado

En la ilustración anterior se muestra que el cuadrángulo (mostrado de (-0,5, -0,5) a (3,5, 3,5)) coincide exactamente con el área rasterizada.

Resumen

En resumen, los píxeles y los elementos de textura son realmente puntos, no bloques sólidos. El espacio de pantalla se origina en el píxel superior izquierdo, pero las coordenadas de textura se originan en la esquina superior izquierda de la cuadrícula de la textura. Lo más importante es que recuerde restar 0,5 unidades de los componentes x e y de las posiciones de vértice al trabajar en espacio de pantalla transformado para alinear correctamente los elementos de textura con píxeles.

El código siguiente es un ejemplo de desplazamiento de los vértices de un cuadrado de 256 por 256 para mostrar correctamente una textura de 256 en 256 en el espacio de pantalla transformado.

//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;
}

Coordenadas de textura