Pixel-perfektes Rendern zu einem Rendertarget mit einem Vollbild-Quad


9

Ich habe einige Probleme beim Rendern einer Reihe von Werten für ein Rendertarget. Die Werte landen nie genau in dem Bereich, den ich möchte. Grundsätzlich verwende ich ein Vollbild-Quad und einen Pixel-Shader, um meine Rendertarget-Textur zu rendern, und beabsichtige dann, die Texturkoordinaten als Grundlage für einige Berechnungen im Shader zu verwenden. Die Texturkoordinaten des Quad reichen von (0,0) oben links bis (1,1) unten rechts. Das Problem ist, dass diese Werte nach der Interpolation nicht als solche zum Pixel-Shader gelangen .

Ein Beispiel: Ich rendere in eine 4x4-Textur und der Shader gibt in diesem Fall einfach die Texturkoordinaten (u, v) in den roten und grünen Kanälen aus:

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

Was ich daraus machen möchte, ist eine Textur, bei der das Pixel oben links RGB (0,0,0) und damit schwarz ist, das Pixel oben rechts RGB (255,0,0) und somit hell ist rot und das Pixel unten links ist RGB (0,255,0) - ein helles Grün.

Stattdessen bekomme ich das hier rechts:

(Straight Quad Rendering, keine Korrektur)
Gerades Rendern von Quad, keine Korrektur angewendet

Das Pixel oben links ist schwarz, aber ich bekomme nur in den anderen Ecken ein relativ dunkles Rot und Dunkelgrün. Ihre RGB-Werte sind (191,0,0) und (0,191,0). Ich vermute sehr, dass dies mit den Abtastorten des Quad zu tun hat: Das obere linke Pixel tastet die obere linke Ecke des Quad korrekt ab und erhält (0,0) als UV-Koordinaten, aber die anderen Eckpixel werden nicht abgetastet die anderen Ecken des Quad. Ich habe dies im linken Bild mit dem blauen Kästchen dargestellt, das das Quad darstellt, und den weißen Punkten, die die oberen Abtastkoordinaten darstellen.

Jetzt weiß ich um den halben Pixelversatz, den Sie beim Rendern von Quads mit Bildschirmausrichtung in Direct3D9 auf Ihre Koordinaten anwenden sollten. Mal sehen, was für ein Ergebnis ich daraus bekomme:

(Quad mit DX9s halbem Pixelversatz gerendert)
Quad mit halbem Pixelversatz gerendert

Rot und Grün sind heller geworden, aber immer noch nicht korrekt: 223 ist das Maximum, das ich auf dem roten oder grünen Farbkanal bekomme. Aber jetzt habe ich nicht einmal mehr reines Schwarz, sondern ein dunkles, gelbliches Grau mit RGB (32,32,0)!

Was ich eigentlich brauche, wäre diese Art von Rendering:

(Zielrender, reduzierte Quad-Größe)
reine Schwarz-, Grün- und Rottöne mit korrekter Interpolation

Es sieht so aus, als müsste ich den rechten und den unteren Rand meines Quad im Vergleich zur ersten Figur genau um ein Pixel nach oben und links verschieben. Dann sollten die rechte Spalte und die untere Pixelreihe die UV-Koordinaten direkt vom Rand des Quadrats korrekt erhalten:

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

Dies hat jedoch nicht ganz geklappt und dazu geführt, dass die unteren und rechten Pixel überhaupt nicht gerendert wurden. Ich nehme an, dass die Zentren dieser Pixel nicht mehr vom Quad abgedeckt werden und daher nicht vom Shader verarbeitet werden. Wenn ich die pixelSize-Berechnung um einen winzigen Betrag ändere, um das Quad um einen winzigen Betrag zu vergrößern, funktioniert es irgendwie ... zumindest bei einer 4x4-Textur. Es funktioniert nicht bei kleineren Texturen und ich befürchte, dass es die gleichmäßige Verteilung der UV-Werte auch bei größeren Texturen auf subtile Weise verzerrt:

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

(Ich habe die pixelSize-Berechnung um 0,001 f geändert. Bei kleineren Texturen, z. B. 1D-Nachschlagetabellen, funktioniert dies nicht und ich muss sie auf 0,01 f oder etwas Größeres erhöhen.)

Natürlich ist dies ein triviales Beispiel, und ich könnte diese Berechnung auf der CPU viel einfacher durchführen, ohne mir Gedanken über die Zuordnung von UV-Strahlen zu Pixelzentren machen zu müssen. Dennoch muss es eine Möglichkeit geben, tatsächlich eine vollständige [0] zu rendern. 1] Bereich bis Pixel auf einem Rendertarget!?


Nicht, dass es hilfreich wäre, aber ich habe genau das gleiche Problem.
IUsedToBeAPygmy

1
Vergessen Sie nicht, auch die lineare Abtastung zu deaktivieren.
r2d2rigo

Es sieht nicht so aus, als würde das von Ihnen verwendete Koordinatensystem mit den Pixeln des Bildschirms übereinstimmen. Wenn dies der Fall wäre, würden Sie hier nur 2 × 2 Pixel zeichnen. Das erste Problem, das ich lösen würde, besteht darin, die Projekt- und Modellansichtsmatrizen so zu skalieren, dass +1 im Koordinatenraum eine Verschiebung von 1 Pixel auf dem Bildschirm darstellt.
Slipp D. Thompson

Antworten:


4

Ihr Problem ist, dass UVs als Texturkoordinaten ausgelegt sind. Eine Koordinate von 0,0 ist die obere linke Ecke des oberen linken Pixels in der Textur, in der Sie normalerweise die Textur nicht lesen möchten. Für 2D möchten Sie die Textur in der Mitte dieses Pixels lesen.

Wenn meine Mathematik stimmt, müssen Sie Folgendes tun, um das Problem zu umgehen:

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

Das heißt, Sie subtrahieren den entsprechenden Versatz, um das obere linke Pixel bei 0,0 zu erhalten, und wenden dann eine Skala an, um das untere rechte Pixel korrekt zu machen.

Dasselbe könnte auch erreicht werden, indem die UV-Koordinaten für die Geometrie außerhalb des Bereichs 0-1 erweitert werden.


Um den letzten Satz näher zu erläutern: Sie können diese Transformation auf die UVs im Scheitelpunktpuffer für die vier Ecken des Quads anwenden, dann im Pixel-Shader return float4(texCoord.rg, 0, 1);.
Nathan Reed

Ich habe die oben vorgeschlagene Formel ausprobiert und es hat nicht ganz funktioniert: In meinem 4x4-Beispiel-Rendertarget von oben erhalte ich 0, 42, 127 und 212 in den R- und G-Kanälen von links nach rechts oder von oben nach unten. Ich möchte jedoch Werte von 0 bis 255 in gleichmäßig verteilten Schritten (für eine 4x4-Textur, die 0, 85, 170 und 255 wäre). Ich habe auch versucht, die UV-Koordinaten zu ändern, aber noch nicht den richtigen Versatz gefunden.
Mario

0

Es sieht so aus, als müsste ich den rechten und den unteren Rand meines Quad im Vergleich zur ersten Figur genau um ein Pixel nach oben und links verschieben.

Fast, aber nur die Hälfte des Pixels. Subtrahieren Sie einfach 0,5 von Ihren Quad-Eckpunkten. ZB möchten Sie 256x256 Quad mit der gleichen Textur, hier sind Ihre Punkte:

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

Oder Sie können stattdessen die Hälfte des Texels in den Pixel-Shader einfügen (texCoord.rg + 0,5 * (1,0 / texSize)).

Ein halber Pixelversatz ist in DX9 eine bekannte Tatsache. Dieser und dieser Artikel können Ihnen ebenfalls helfen.


0

Dies wird sicherlich durch lineare Abtastung verursacht. Wenn Sie sich die Texel rechts und unterhalb des vollschwarzen Pixels oben links ansehen, werden Sie feststellen, dass sie 63 in den R- und / oder G-Kanälen und 2 von ihnen 2 in B haben schlammig-dunkelgelb bekommst du; Es ist 31 in R und G und 1 in B. Dies ist definitiv ein Ergebnis der Mittelung von Texeln über eine 2x2-Gruppe. Die Lösung besteht also darin, den Texturfilter auf Punkt zu setzen.


-2

Das liegt einfach daran, dass Sie die texCoords aus der Vertex-Ausgabe verwenden, die nach der Verarbeitung des Vertex-Shaders von der Festplatte interpoliert wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.