Rendu parfait au pixel près pour une cible de rendu avec un quadruple plein écran

9

J'ai du mal à restituer un tas de valeurs à une cible de rendu. Les valeurs ne se retrouvent jamais dans la plage exacte que je souhaite. Fondamentalement, j'utilise un quadruple plein écran et un pixel shader pour effectuer le rendu sur ma texture de rendu cible, puis j'ai l'intention d'utiliser les coordonnées de texture comme base pour certains calculs dans le shader. Les coordonnées de texture de la plage quadruple de (0,0) en haut à gauche à (1,1) dans le coin inférieur droit ... le problème est, après interpolation, ces valeurs n'arrivent pas au pixel shader en tant que tel .

Un exemple: je rend une texture 4x4 et le shader dans ce cas produit simplement les coordonnées de texture (u, v) dans les canaux rouge et vert:

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

Ce que je veux en retirer, c'est une texture où le pixel en haut à gauche est RGB (0,0,0) et donc noir, le pixel en haut à droite est RGB (255,0,0) et donc un brillant rouge et le pixel en bas à gauche est RVB (0,255,0) - un vert brillant.

Cependant, au lieu de cela, je le trouve ici à droite:

(rendu quadruple droit, pas de correction)
rendu droit du quad, aucune correction appliquée

Le pixel supérieur gauche est noir, mais je n'obtiens qu'un rouge et un vert foncé relativement foncés dans les autres coins. Leurs valeurs RVB sont (191,0,0) et (0,191,0). Je soupçonne fortement que cela a à voir avec les emplacements d'échantillonnage du quad: le pixel supérieur gauche échantillonne correctement le coin supérieur gauche du quad et obtient (0,0) en coordonnées UV, mais les autres pixels d'angle ne sont pas échantillonnés à partir de les autres coins du quad. J'ai illustré cela dans l'image de gauche avec la boîte bleue représentant le quad et les points blancs les coordonnées d'échantillonnage supérieures.

Maintenant, je connais le décalage d'un demi-pixel que vous devez appliquer à vos coordonnées lors du rendu des quadrilatères alignés à l'écran dans Direct3D9. Voyons quel genre de résultat j'obtiens:

(rendu quad avec le décalage d'un demi-pixel de DX9)
rendu quad avec décalage d'un demi-pixel

Le rouge et le vert sont devenus plus lumineux mais ne sont toujours pas corrects: 223 est le maximum que j'obtiens sur le canal de couleur rouge ou vert. Mais maintenant, je n'ai même plus de noir pur, mais plutôt un gris foncé et jaunâtre avec RGB (32,32,0)!

Ce dont j'ai réellement besoin serait ce type de rendu:

(rendu cible, taille quad réduite)
noirs, verts et rouges purs avec une interpolation correcte

Il semble que je doive déplacer le bord droit et le bord inférieur de mon quad exactement d'un pixel vers le haut et vers la gauche, par rapport à la première figure. Ensuite, la colonne de droite et la rangée inférieure de pixels devraient toutes obtenir correctement les coordonnées UV directement à partir de la bordure du 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)),
};

Cependant, cela n'a pas tout à fait fonctionné et a empêché le rendu des pixels inférieur et droit. Je suppose que les centres de ces pixels ne sont plus couverts par le quad et ne seront donc pas traités par le shader. Si je modifie le calcul de pixelSize par une petite quantité pour agrandir le quad, il fonctionne un peu ... au moins sur une texture 4x4. Cela ne fonctionne pas sur les textures plus petites et je crains que cela ne déforme subtilement la distribution uniforme des valeurs UV sur les textures plus grandes:

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

(J'ai modifié le calcul pixelSize de 0,001f - pour les textures plus petites, par exemple les tables de recherche 1D, cela ne fonctionne pas et je dois l'augmenter à 0,01f ou quelque chose de plus grand)

Bien sûr, ceci est un exemple trivial et je pourrais faire ce calcul beaucoup plus facilement sur le CPU sans avoir à se soucier de mapper les UV aux centres de pixels ... encore, il doit y avoir un moyen de rendre un rendu complet et complet [0, 1] plage de pixels sur une cible de rendu!?

Mario
la source
Ce n'est pas utile, mais j'ai exactement le même problème.
IUsedToBeAPygmy
1
N'oubliez pas de désactiver également l'échantillonnage linéaire.
r2d2rigo
Il ne ressemble pas au système de coordonnées que vous utilisez correspond aux pixels de l'écran - s'il l'était, il semble que vous ne dessiniez que 2 × 2 pixels ici. Le premier problème que je résoudrais consiste à mettre à l'échelle les matrices de projet et de modèle de sorte que +1 dans l'espace de coordonnées soit un décalage de 1 pixel à l'écran.
Slipp D. Thompson

Réponses:

4

Votre problème est que les UV sont conçus pour être des coordonnées de texture. Une coordonnée de 0,0 est le coin supérieur gauche du pixel supérieur gauche de la texture, qui n'est pas l'endroit où vous souhaitez normalement lire la texture. Pour 2D, vous voulez lire la texture au milieu de ce pixel.

Si mes calculs sont exacts, ce que vous devez faire pour contourner c'est:

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

Autrement dit, vous soustrayez le décalage approprié pour obtenir le pixel supérieur gauche à 0,0, puis appliquez une échelle pour obtenir le coin inférieur droit correct.

La même chose pourrait également être obtenue en augmentant les coordonnées UV pour la géométrie en dehors de la plage 0-1.

Adam
la source
Pour élaborer sur la dernière phrase: vous pouvez appliquer cette transformation aux UV dans le tampon de sommet pour les quatre coins du quad, puis dans le pixel shader, faites-le return float4(texCoord.rg, 0, 1);.
Nathan Reed,
J'ai essayé la formule suggérée ci-dessus et cela n'a pas vraiment fonctionné: dans mon échantillon de rendu 4x4 ci-dessus, j'obtiens 0, 42, 127 et 212 dans les canaux R et G, de gauche à droite ou de haut en bas. Cependant, je voudrais des valeurs de 0 à 255 par pas régulièrement espacés (pour une texture 4x4 qui serait 0, 85, 170 et 255). J'ai également essayé de modifier les coordonnées UV, mais je n'ai pas encore trouvé le bon décalage.
Mario
0

Il semble que je doive déplacer le bord droit et le bord inférieur de mon quad exactement d'un pixel vers le haut et vers la gauche, par rapport à la première figure.

Presque, mais seulement la moitié du pixel. Il suffit de soustraire 0,5 de vos sommets quadruples. Par exemple, vous voulez un quad 256x256 avec la même texture, voici vos points:

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

Vous pouvez également ajouter la moitié du texel dans le pixel shader (texCoord.rg + 0.5 * (1.0 / texSize))

Le décalage d'un demi-pixel est un fait connu dans DX9. Ceci et ces articles peuvent également vous aider.

alariq
la source
0

Cela est certainement dû à l'échantillonnage linéaire. Si vous regardez les texels à droite et en dessous du pixel noir en haut à gauche, vous verrez qu'ils ont 63 dans les canaux R et / ou G, et 2 d'entre eux en ont 2 dans B. Maintenant, regardez le boueux-jaune foncé vous obtenez; c'est 31 en R et G et 1 en B. C'est certainement le résultat de la moyenne des texels sur un groupe 2x2, donc la solution est de régler votre filtre de texture au point.

Maximus Minimus
la source
-2

C'est simplement parce que vous utilisez les texCoords de la sortie de vertex qui sont interpolés par le disque dur après le traitement du vertex shader.

Jason Huang
la source