Rendering perfetto per i pixel su un renderertarget con un quad a schermo intero


9

Ho dei problemi nel rendere un insieme di valori in un rendertarget. I valori non finiscono mai nell'intervallo esatto che desidero. Fondamentalmente uso un quad a schermo intero e un pixel shader per eseguire il rendering sulla mia texture di rendering rendertarget e quindi intendo utilizzare le coordinate della texture come base per alcuni calcoli nello shader. Le coordinate della trama del quadruplo vanno da (0,0) nell'angolo in alto a sinistra a (1,1) nell'angolo in basso a destra ... il problema è che, dopo l'interpolazione, questi valori non arrivano al pixel shader in quanto tale .

Un esempio: eseguo il rendering su una trama 4x4 e lo shader in questo caso genera semplicemente le coordinate della trama (u, v) nei canali rosso e verde:

return float4(texCoord.rg, 0, 1);

Quello che voglio uscire da esso è una trama in cui il pixel in alto a sinistra è RGB (0,0,0) e quindi nero, il pixel in alto a destra è RGB (255,0,0) e quindi un luminoso rosso e il pixel in basso a sinistra è RGB (0,255,0) - un verde brillante.

Tuttavia, invece ottengo questo qui a destra:

(rendering quad dritto, nessuna correzione)
rendering dritto del quad, nessuna correzione applicata

Il pixel in alto a sinistra è nero, ma ottengo solo un rosso relativamente scuro e un verde scuro negli altri angoli. I loro valori RGB sono (191,0,0) e (0,191,0). Sospetto fortemente che abbia a che fare con le posizioni di campionamento del quad: il pixel in alto a sinistra campiona correttamente l'angolo in alto a sinistra del quad e ottiene (0,0) come coordinate UV, ma gli altri pixel d'angolo non campionano da gli altri angoli del quad. L'ho illustrato nell'immagine a sinistra con la casella blu che rappresenta il quadratino e i punti bianchi le coordinate di campionamento superiori.

Ora so dell'offset di mezzo pixel che dovresti applicare alle tue coordinate quando esegui il rendering di quad allineati sullo schermo in Direct3D9. Vediamo che tipo di risultato ottengo da questo:

(quad renderizzato con offset di mezzo pixel di DX9)
quadrante renderizzato con offset di mezzo pixel

Il rosso e il verde sono diventati più luminosi ma non sono ancora corretti: 223 è il massimo che ottengo sul canale di colore rosso o verde. Ma ora non ho più nemmeno il nero puro, ma invece un grigio scuro, giallognolo con RGB (32,32,0)!

Ciò di cui ho effettivamente bisogno sarebbe questo tipo di rendering:

(rendering target, dimensione quadrupla ridotta)
neri puri, verdi e rossi con corretta interpolazione

Sembra che devo spostare il bordo destro e quello inferiore del mio quad esattamente di un pixel verso l'alto e verso sinistra, rispetto alla prima cifra. Quindi la colonna destra e la riga inferiore di pixel dovrebbero ottenere correttamente tutte le coordinate UV proprio dal bordo del quad:

VertexPositionTexture[] quad = new VertexPositionTexture[]
{
    new VertexPositionTexture(new Vector3(-1.0f,  1.0f, 1f), new Vector2(0,0)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X,  1.0f, 1f), new Vector2(1,0)),
    new VertexPositionTexture(new Vector3(-1.0f, -1.0f + pixelSize.Y, 1f), new Vector2(0,1)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X, -1.0f + pixelSize.Y, 1f), new Vector2(1,1)),
};

Tuttavia, ciò non ha funzionato del tutto e ha impedito il rendering dei pixel inferiore e destro. Suppongo che i centri di questi pixel non siano più coperti dal quad e quindi non vengano elaborati dallo shader. Se cambio il calcolo di pixelSize di una piccola quantità per rendere il quad molto più grande, funziona un po '... almeno su una trama 4x4. Non funziona su trame più piccole e temo che distorca sottilmente la distribuzione uniforme dei valori uv anche su trame più grandi:

Vector2 pixelSize = new Vector2((2f / textureWidth) - 0.001f, (2f / textureHeight) - 0.001f);

(Ho modificato il calcolo pixelSize di 0.001f - per trame più piccole, ad esempio tabelle di ricerca 1D, questo non funziona e devo aumentarlo a 0,01f o qualcosa di più grande)

Ovviamente questo è un esempio banale e potrei fare questo calcolo molto più facilmente sulla CPU senza dovermi preoccupare di mappare gli UV ai centri di pixel ... tuttavia, ci deve essere un modo per rendere effettivamente un completo, completo [0, 1] intervallo ai pixel su un rendertarget !?


Non che sia di alcun aiuto, ma sto avendo lo stesso identico problema.
IUsedToBeAPygmy

1
Non dimenticare di disabilitare anche il campionamento lineare.
r2d2rigo,

Non sembra che il sistema di coordinate che stai usando corrisponda ai pixel dello schermo - se lo fosse, allora sembra che stai disegnando solo 2 × 2 pixel qui. Il primo problema che risolverei è ridimensionare le matrici del progetto e del modello in modo tale che +1 nello spazio delle coordinate sia uno spostamento di 1 pixel sullo schermo.
Slipp D. Thompson,

Risposte:


4

Il tuo problema è che gli UV sono progettati per essere coordinate della trama. Una coordinata di 0,0 è l'angolo in alto a sinistra del pixel in alto a sinistra nella trama, che non è dove normalmente si desidera leggere la trama. Per il 2D vuoi leggere la trama nel mezzo di quel pixel.

Se la mia matematica è giusta, quello che devi fare per ovviare a questo è:

return float4((texCoord.rg - (0.5f/textureSize)) * (textureSize/(textureSize-1)), 0, 1);

Cioè sottrarre l'offset appropriato per ottenere il pixel in alto a sinistra a 0,0 e quindi applicare una scala per ottenere corretto in basso a destra.

La stessa cosa potrebbe essere ottenuta anche espandendo le coordinate UV per la geometria al di fuori dell'intervallo 0-1.


Per elaborare l'ultima frase: è possibile applicare questa trasformazione agli UV nel buffer dei vertici per i quattro angoli del quad, quindi nel pixel shader basta return float4(texCoord.rg, 0, 1);.
Nathan Reed,

Ho provato la formula suggerita sopra e non ha funzionato del tutto: nel mio rendering del campione 4x4 dall'alto vedo 0, 42, 127 e 212 nei canali R e G, da sinistra a destra o dall'alto verso il basso. Tuttavia, vorrei valori da 0 a 255 a passi equidistanti (per una trama 4x4 che sarebbe 0, 85, 170 e 255). Ho anche provato a modificare le coordinate UV, ma non ho ancora trovato l'offset giusto.
Mario,

0

Sembra che devo spostare il bordo destro e quello inferiore del mio quad esattamente di un pixel verso l'alto e verso sinistra, rispetto alla prima cifra.

Quasi, ma solo la metà del pixel. Basta sottrarre 0,5 dai vertici del quad. Ad esempio, vuoi un quadrato 256x256 con la stessa trama, ecco i tuoi punti:

p1 = (-0.5, -0.5)
p2 = (-0.5, 255-0.5)
p3 = (255-0.5, 255-0.5)
p4 = (255-0.5, -0.5)

Oppure, puoi invece aggiungere metà del texel nel pixel shader (texCoord.rg + 0,5 * (1.0 / texSize))

L'offset di mezzo pixel è un fatto noto in DX9. Questo e questo articolo possono anche aiutarti.


0

Ciò è certamente causato dal campionamento lineare. Se guardi i texel a destra e sotto il pixel full-black in alto a sinistra, vedrai che hanno 63 nei canali R e / o G e 2 di loro ne hanno 2 in B. Ora guarda il si ottiene fangoso-giallo scuro; è 31 in R e G e 1 in B. Questo è sicuramente il risultato della media dei texel su un gruppo 2x2, quindi la soluzione è impostare il filtro delle trame sul punto.


-2

Questo è semplicemente perché stai usando texCoords dall'output del vertice che viene interpolato dal disco rigido dopo l'elaborazione del vertex shader.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.