Condividi tramite


Mapping diretto di texel a pixel (Direct3D 9)

Quando si esegue il rendering dell'output 2D usando vertici pre-trasformati, è necessario prestare attenzione a garantire che ogni area del texel corrisponda correttamente a una singola area in pixel, altrimenti può verificarsi una distorsione della trama. Comprendendo le nozioni di base del processo seguito da Direct3D durante la rasterizzazione e il testo dei triangoli, è possibile assicurarsi che l'applicazione Direct3D esegua correttamente il rendering dell'output 2D.

illustrazione di un display con risoluzione 6x6

Il diagramma precedente mostra i pixel modellati come quadrati. In realtà, tuttavia, i pixel sono punti, non quadrati. Ogni quadrato nel diagramma precedente indica l'area illuminata dal pixel, ma un pixel è sempre solo un punto al centro di un quadrato. Questa distinzione, anche se apparentemente piccola, è importante. Un'illustrazione migliore dello stesso display è illustrata nel diagramma seguente.

illustrazione di una visualizzazione costituita da pixel

Il diagramma precedente mostra correttamente ogni pixel fisico come punto al centro di ogni cella. La coordinata dello spazio dello schermo (0, 0) si trova direttamente nel pixel superiore sinistro e quindi al centro della cella superiore sinistra. L'angolo superiore sinistro dello schermo è quindi in corrispondenza di (-0,5, -0,5) perché è di 0,5 celle a sinistra e 0,5 celle in alto dal pixel superiore sinistro. Direct3D eseguirà il rendering di un quad con angoli a (0, 0) e (4, 4), come illustrato nella figura seguente.

illustrazione di un contorno di un quad non registrato tra (0, 0) e (4, 4)

La figura precedente mostra dove il quad matematico è in relazione allo schermo, ma non mostra l'aspetto del quad una volta che Direct3D lo rasterizza e lo invia alla visualizzazione. Infatti, è impossibile che un display raster riempia il quad esattamente come mostrato perché i bordi del quad non coincidono con i limiti tra le celle pixel. In altre parole, poiché ogni pixel può visualizzare un solo colore, ogni cella pixel viene riempita con un solo colore; se il display dovesse eseguire il rendering del quad esattamente come illustrato, le celle pixel lungo il bordo del quad dovranno mostrare due colori distinti: blu dove coperto dal quad e dal bianco dove è visibile solo lo sfondo.

Al contrario, l'hardware grafico è in grado di determinare quali pixel devono essere riempiti per approssimare il quad. Questo processo è denominato rasterizzazione ed è dettagliato in Regole di rasterizzazione (Direct3D 9).This process is called rasterization, and is detailed in Rasterization Rules (Direct3D 9). Per questo caso specifico, il quad rasterizzato è illustrato nella figura seguente.

illustrazione di un quad senza testo disegnato da (0,0) a (4,4)

Si noti che il quad passato a Direct3D ha angoli a (0, 0) e (4, 4), ma l'output rasterizzato (la figura precedente) ha angoli in corrispondenza (-0.5,-0.5) e (3.5,3.5). Confrontare le due illustrazioni precedenti per individuare le differenze di rendering. Si può notare che il rendering effettivo dello schermo è la dimensione corretta, ma è stato spostato di -0,5 celle nelle direzioni x e y. Tuttavia, ad eccezione delle tecniche di campionamento multipla, questa è la migliore approssimazione possibile per il quad. Per informazioni dettagliate sul campionamento multi-campionamento, vedere l'esempio antialias . Tenere presente che se il rasterizzatore riempie ogni cella a cui è stato incrociato il quad, l'area risultante sarà di dimensione 5 x 5 anziché quella desiderata 4 x 4.

Se si presuppone che le coordinate dello schermo abbiano origine nell'angolo superiore sinistro della griglia di visualizzazione anziché nel pixel superiore sinistro, il quad viene visualizzato esattamente come previsto. Tuttavia, la differenza diventa chiara quando al quad viene assegnata una trama. La figura seguente mostra la trama 4 x 4 che verrà mappata direttamente al quad.

illustrazione di una trama 4x4

Poiché la trama è 4 x 4 texel e il quad è 4 x 4 pixel, è possibile che il quad con trama venga visualizzato esattamente come la trama indipendentemente dalla posizione sullo schermo in cui viene disegnato il quad. Tuttavia, questo non è il caso; anche lievi variazioni di posizione influiscono sulla modalità di visualizzazione della trama. La figura seguente mostra come viene visualizzato un quad compreso tra (0, 0) e (4, 4) dopo la rasterizzazione e la trama.

illustrazione di un quad con trama disegnato da (0, 0) e (4, 4)

Il quad disegnato nella figura precedente mostra l'output con trama (con una modalità di filtro lineare e una modalità di indirizzamento di blocco) con il contorno rasterizzato sovrapposto. Il resto di questo articolo spiega esattamente perché l'output ha un aspetto simile alla trama, ma per coloro che vogliono la soluzione, ecco: i bordi del quad di input devono trovarsi sulle linee limite tra le celle pixel. Spostando semplicemente le coordinate x e y quad di -0,5 unità, le celle di texel copriranno perfettamente le celle pixel e il quad può essere ricreato perfettamente sullo schermo. L'ultima illustrazione in questo argomento mostra il quad in corrispondenza delle coordinate corrette.

I dettagli del motivo per cui l'output rasterizzato presenta solo una leggera somiglianza con la trama di input sono direttamente correlati al modo in cui gli indirizzi Direct3D e le trame di campioni. Di seguito si presuppone che si abbia una buona conoscenza dello spazio delle coordinate della trama E filtro delle trame bilineare.

Tornare all'analisi dell'output di pixel strano, è opportuno tracciare il colore di output al pixel shader: il pixel shader viene chiamato per ogni pixel selezionato per far parte della forma rasterizzata. Il quad blu tinta unita raffigurato in un'illustrazione precedente potrebbe avere uno shader particolarmente semplice:

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

Per il quad con trama, il pixel shader deve essere modificato leggermente:

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

Tale codice presuppone che la trama 4 x 4 sia archiviata in MyTexture. Come illustrato, il campionatore di trame MySampler è impostato per eseguire il filtro bilineare in MyTexture. Il pixel shader viene chiamato una volta per ogni pixel rasterizzato e ogni volta che il colore restituito è il colore della trama campionato in vTexCoord. Ogni volta che viene chiamato il pixel shader, l'argomento vTexCoord viene impostato sulle coordinate della trama in corrispondenza di tale pixel. Ciò significa che lo shader chiede al campionatore di trama il colore della trama filtrata nella posizione esatta del pixel, come descritto nella figura seguente.

illustrazione delle posizioni di campionamento per le coordinate delle trame

La trama (mostrata sovrapposta) viene campionata direttamente in posizioni pixel (mostrate come punti neri). Le coordinate della trama non sono interessate dalla rasterizzazione (rimangono nello spazio dello schermo proiettato del quad originale). I punti neri mostrano dove si trovano i pixel di rasterizzazione. Le coordinate della trama a ogni pixel sono facilmente determinate interpolando le coordinate archiviate in ogni vertice: il pixel in corrispondenza di (0,0) coincide con il vertice in corrispondenza di (0, 0); pertanto, le coordinate della trama in corrispondenza di quel pixel sono semplicemente le coordinate della trama archiviate in corrispondenza del vertice, UV (0,0, 0,0). Per il pixel in corrispondenza di (3, 1), le coordinate interpolate sono UV (0,75, 0,25) perché tale pixel si trova a tre quarti della larghezza della trama e un quarto dell'altezza. Queste coordinate interpolate sono ciò che viene passato al pixel shader.

I texel non si allineano con i pixel in questo esempio; ogni pixel (e quindi ogni punto di campionamento) viene posizionato nell'angolo di quattro texel. Poiché la modalità di filtro è impostata su Lineare, il campionatore medierà i colori dei quattro texel che condividono tale angolo. Questo spiega perché il pixel dovrebbe essere rosso è in realtà tre quarti grigio più un quarto rosso, il pixel previsto sarà verde è di un mezzo grigio più un quarto rosso più un quarto verde e così via.

Per risolvere questo problema, è sufficiente eseguire correttamente il mapping del quad ai pixel a cui verrà rasterizzato e quindi eseguire correttamente il mapping dei texel ai pixel. La figura seguente mostra i risultati del disegno dello stesso quad tra (-0,5, -0,5) e (3,5, 3,5), che è il quad previsto fin dall'inizio.

illustrazione di un quad con trama corrispondente al quad rasterizzato

La figura precedente dimostra che il quad (illustrato da (-0.5, -0.5) a (3.5, 3.5)) corrisponde esattamente all'area rasterizzata.

Riepilogo

In sintesi, i pixel e i texel sono in realtà punti, non blocchi solidi. Lo spazio dello schermo ha origine nel pixel superiore sinistro, ma le coordinate della trama hanno origine nell'angolo superiore sinistro della griglia della trama. Soprattutto, ricordarsi di sottrarre 0,5 unità dai componenti x e y delle posizioni dei vertici quando si lavora nello spazio dello schermo trasformato per allineare correttamente i texel con pixel.

Il codice seguente è un esempio di offset dei vertici di un quadrato di 256 per 256 per visualizzare correttamente una trama di 256 per 256 nello spazio dello schermo trasformato.

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

Coordinate delle trame