OpenGL-Rasterungsproblem
-
Hallo zusammen,
Es geht um eine 2D-Szene mit OpenGL. Wenn ich ein Objekt mit x-/y-Koordinaten zeichne, die eine 0.5 als Nachkommastelle haben (bei entsprechender Projektion also genau "zwischen" zwei Pixel zu liegen kommen), stimmt das Textur-Rechteck nicht mehr. Genauer gesagt wird am Rand ein Texel zu viel genommen, dadurch sieht man z.B. in Bitmaps bereits einen Teil des nächsten Objekts, obwohl die Texturkoordinaten dies ausschliessen.
Als Workaround kann ich zwar einen kleinen Epsilonwert zu den Zielkoordinaten hinzufügen, sodass ich kein 0.5 mehr habe. Aber gibt es eine Lösung, welche die richtigen Texturkoordinaten nimmt, ohne die Modelview-Positionierung zu verändern?
-
Was für einen Texturenfilter hast du drauf?
-
GL_NEAREST
für Mag- und Min-Filter.
-
Nexus schrieb:
[...] obwohl die Texturkoordinaten dies ausschliessen.
Inwiefern?
-
Damit meinte ich nur, dass das nächste Objekt im Bitmap ausserhalb meines rechteckigen Texturbereiches liegt.
-
Wie genau bestimmst du deine Pixel- und Texturkoordinaten? Bedenke dass 1.0 nicht der Mittelpunkt des letzten Texels sondern der Rand der Textur ist.
-
Welches Ergebnis erwartest Du denn, wenn Du eine Bitmap ohne Filter um einen halben Pixel verschiebst?
-
dot schrieb:
Wie genau bestimmst du deine Pixel- und Texturkoordinaten?
Zielkoordinaten entsprechen der logischen Objektposition, die Texturkoordinaten berechne ich folgendermassen (Skalierung auf [0,1] herunter):
bitmap = Rechteck des gesamten Bitmaps (Pixel) rect = Rechteck des gewünschten Teils (Pixel) src = Rechteck in Texturkoordinaten (float in [0,1]) src.left = rect.left / bitmap.width; src.right = rect.right / bitmap.width; src.top = rect.top / bitmap.height; src.bottom = rect.bottom / bitmap.height;
hellihjb schrieb:
Welches Ergebnis erwartest Du denn, wenn Du eine Bitmap ohne Filter um einen halben Pixel verschiebst?
Ich finde etwas merkwürdig, dass sich bei gewissen Modelview-Koordinaten plötzlich die Texturkoordinaten ändern. Bei
GL_NEAREST
wäre es meiner Meinung nach intuitiver, wenn immer die gleiche Anzahl Texel genommen wird. Modelview-Koordinaten mit 0.5 würden dann aufgerundet (d.h. es wird beim nächsten Pixel gezeichnet), aber ohne dabei andere Texel zu nehmen.
-
Beschreibe ich mein Problem zu ungenau, oder besteht das gängige Vorgehen einfach darin, ganzzahlige Zielkoordinaten zu haben?
Oder findet ihr es nicht unintuitiv, dass das Texturrechteck unabhängig von den Zeichenkoordinaten immer gleich bleibt, ausser bei einer einzigen Koordinaten-Nachkommastelle (nämlich 0.5)?
-
Wenn dein Rechteck an ganzzahlige Pixelkoordinaten gerendert wird, die Texturkoordinaten dabei aber auf halbzahlige Texelkoordinaten verweisen dann ist das was du beobachtest exakt das was man auch erwarten würde, der Rand deines Quad fällt dann nämlich exakt auf die Mittelpunkte der entsprechenden Texel. Ich kann hier nichts ungewöhnliches erkennen, das Programm verhält sich genau so wie du es programmiert hast.
EDIT: Ok ich hatte dich ursprünglich falsch verstanden, deine Pixelkoordinaten fallen also genau zwischen zwei Pixel und deine Texelkoordinaten sind weiterhin ganzzahlig. Ich würde mir in dem Fall erwarten dass die Probleme am linken und oberen Rand auftreten!?
-
dot schrieb:
Wenn dein Rechteck an ganzzahlige Pixelkoordinaten gerendert wird, die Texturkoordinaten dabei aber auf halbzahlige Texelkoordinaten verweisen dann ist das was du beobachtest exakt das was man auch erwarten würde
Es ist aber gerade umgekehrt: Texturkoordinaten referenzieren ganzzahlige Texel, Pixelkoordinaten enden mit 0.5. Sorry falls ich Verwirrung gestiftet habe.
-
Nein sry, ich hab nicht genau genug gelesen. Ich würde mir in dem Fall erwarten dass die Probleme am linken und oberen Rand auftreten und nicht rechts oder unten?
-
dot schrieb:
Ich würde mir in dem Fall erwarten dass die Probleme am linken und oberen Rand auftreten und nicht rechts oder unten?
Ja, bei meinen Beispielen war es glaube ich vor allem der linke Rand, bei welchem die Textur überschritten wurde.
-
Ja, das ist was ich erwarten würde. Grund: Der Screenspace geht in OpenGL von der linken unteren zur rechen oberen Ecke. Daraus folgt dass die Pixelmittelpunkte genau an halbzahligen Koordinaten liegen. Mit den Texturkoordinaten schauts genau gleich aus, 0 ist da genau die linke bzw. untere und 1 genau die rechte bzw. obere Kante der Textur (und nicht die Texelmittelpunkte). All das zusammen bedeutet folgendes: Bei halbzahligen Pixelkoordinaten gehen die Kanten deines Polygons exakt durch die entsprechenden Pixelmittelpunkte. Aufgrund der Top-Left Fillconvention werden diese Pixel auf der linken und oberen Kante gefüllt und rechts und unten nicht. Deine Texturkoordinaten an der linken und oberen Kante fallen exakt auf den Rand der Textur (und nicht auf die Mittelpunke der ersten Texel). Ob GL_NEAREST auf oder abrundet wenn das Sample exakt zwischen zwei Texeln liegt ist vermutlich Implementierungsabhängig und du hast wohl eine Grafikkarte erwischt die abrundet. Bei halbzahligen Pixelkoordinaten mit 0..1 Texturkoordinaten samplest du also exakt den Rand der Textur, bei ganzzahligen Pixelkoordinaten exakt die Texelmittepunkte.
-
Vielen Dank für die Erklärung.
Du sprichst ja von der Konvention, bei halbzahligen Pixelkoordinaten die Pixel am linken und oberen Rand auszuwählen. Wäre es nicht sinnvoll, bei Texeln genau das Umgekehrte zu tun (also aufrunden), um diesen Effekt aufzuheben?
Oder gibt es eine Möglichkeit, das Texel-Auswahlverhalten zu ändern?
-
dot schrieb:
Ob GL_NEAREST auf oder abrundet wenn das Sample exakt zwischen zwei Texeln liegt ist vermutlich Implementierungsabhängig und du hast wohl eine Grafikkarte erwischt die abrundet.
nein, alles ist klar definiert.wenn du wissen willst, ob du richtig pixel auf texel mapst, mussst du nur GL_LINEAR setzen und es duerfte keinen unterschied zu GL_NEAREST geben (solange man die positionen nicht verschiebt).
-
rapso schrieb:
wenn du wissen willst, ob du richtig pixel auf texel mapst, mussst du nur GL_LINEAR setzen und es duerfte keinen unterschied zu GL_NEAREST geben (solange man die positionen nicht verschiebt).
Mit
GL_LINEAR
führen halbzahlige Pixel zu unscharfer (interpolierter) Darstellung, aber immer noch mit Teilen ausserhalb der Textur.Soll ich daraus schliessen, dass die "richtige" Darstellung nur möglich ist, wenn ich direkt ganzzahlige Pixelkoordinaten verwende? Ich habe gedacht,
GL_NEAREST
würde mir das Runden abnehmen (was es ja auch ausser bei 0.5 immer richtig tut).
-
rapso schrieb:
dot schrieb:
Ob GL_NEAREST auf oder abrundet wenn das Sample exakt zwischen zwei Texeln liegt ist vermutlich Implementierungsabhängig und du hast wohl eine Grafikkarte erwischt die abrundet.
nein, alles ist klar definiert.Rein aus Interesse: Wie genau ist es denn definiert? Ich hab jetzt nicht den ganzen OpenGL Standard durchwühlt aber ich hab dazu nur folgendes gefunden:
OpenGL 4.1. Spec schrieb:
When the value of TEXTURE_MIN_FILTER is NEAREST, the texel in the image array of level levelbase that is nearest (in Manhattan distance) to (u; v; w) is obtained.
Und da ist eben nicht genau definiert was passiert wenn der Abstand zu beiden Nachbarn gleich ist. Die OpenGL Spezifikation ist bei solchen Dingen offenbar nicht immer so genau, z.B. ist afaik auch nirgendwo explizit eine Top-Left-Fillconvention gefordert sondern lediglich irgendeine Fillconvention. Und in GLSL ist auch nicht definiert ob round() bei exakt 0.5 auf- oder abrundet.
Nexus schrieb:
Soll ich daraus schliessen, dass die "richtige" Darstellung nur möglich ist, wenn ich direkt ganzzahlige Pixelkoordinaten verwende? Ich habe gedacht,
GL_NEAREST
würde mir das Runden abnehmen (was es ja auch ausser bei 0.5 immer richtig tut).GL_NEAREST nimmt dir das Runden ab. Aber das hilft dir nichts wenn deine Texturkoordinaten und deine Pixelkoordinaten nicht zusammenpassen. Wenn du halbzahlige Pixelkoordinaten willst dann brauchst du auch Texturkoordinaten die entsprechend die Mittelpunkte der Texel treffen.
Btw: Wenn ich mir deinen Code nochmal anschau:
Nexus schrieb:
bitmap = Rechteck des gesamten Bitmaps (Pixel) rect = Rechteck des gewünschten Teils (Pixel) src = Rechteck in Texturkoordinaten (float in [0,1]) src.left = rect.left / bitmap.width; src.right = rect.right / bitmap.width; src.top = rect.top / bitmap.height; src.bottom = rect.bottom / bitmap.height;
Sollte das nicht eigentlich immer (bitmap.width - 1) bzw. (bitmap.height - 1) sein!?
-
dot schrieb:
Wenn du halbzahlige Pixelkoordinaten willst dann brauchst du auch Texturkoordinaten die entsprechend die Mittelpunkte der Texel treffen.
Aber dann hätte ich wieder das gleiche Problem bei ganzzahligen Pixelkoordinaten, oder?
dot schrieb:
Sollte das nicht eigentlich immer (bitmap.width - 1) bzw. (bitmap.height - 1) sein!?
Ich bin mir nicht sicher, aber -1 macht insofern Sinn, dass die Texturkoordinate
1.f
auf den Anfang und nicht das Ende des letzten Texels abgebildet wird. Andererseits hat man so theoretisch ein Problem mit leeren Texturrechtecken (Grösse 0), doch diesen Fall könnte man auch separat behandeln. Das Problem tritt bei diesem Ansatz aber weiterhin auf, nur dass die Texturüberschreitung nun am unteren Ende passiert.
-
Sorry dass ich nochmals nerve
Nur um sicher zu gehen: Die einzige Möglichkeit, die falsche Auswahl von Texturkoordinaten zu verhindern, besteht darin, die Pixelkoordinaten entsprechend anzupassen (sodass keine 0.5-Nachkommastellen entstehen)? Das Verhalten lässt sich nicht ändern?
Denn so muss ich alle Objekte mit problematischen Koordinaten vor dem Zeichnen leicht verschieben... Ist das die Standardherangehensweise?