OpenGL-Rasterungsproblem



  • 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?


  • Mod

    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?



  • Nexus schrieb:

    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?

    Was hier passiert ist offenbar nicht das was du erwartest, aber es ist dennoch zu 100% korrekt. Es macht eben einen Unterschied ob du den Mittelpunkt oder die "Ecke" eines Texels auf den Mittelpunkt eines Pixels mappest. Also nein, das Verhalten lässt sich nicht ändern, niemand würde es ändern wollen denn alles andere wär einfach falsch. Kann es sein dass du davon ausgehst dass die Screenspace-Koordinaten vor der Rasterisierung auf ganze Pixel gerundet werden!? Das ist nämlich nicht der Fall.

    Wie kommen die "falschen" Koordinaten denn überhaupt zustande?



  • dot schrieb:

    Es macht eben einen Unterschied ob du den Mittelpunkt oder die "Ecke" eines Texels auf den Mittelpunkt eines Pixels mappest.

    Das schon. Ich habe mal meine ASCII-Künste bemüht:

    Texel      Pixel     Abbildung
     t-1  t     p-1  p
    +---+---+  +---+---+ 
    |   | X |  | X |   |   t -> p-1
    +---+---+  +---+---+
    
    +---+---+  +---+---+ 
    |   | X |  |  X|   |   t -> p-1
    +---+---+  +---+---+
    
    +---+---+  +---+---+ 
    |   | X |  |   |X  |   t -> p
    +---+---+  +---+---+
    
    +---+---+  +---+---+ 
    |   | X |  |   X   |   t -> p,    t-1 -> p-1 !?
    +---+---+  +---+---+
    

    Im letzten Fall verstehe ich nicht, wieso jetzt plötzlich das vorherige Texel (Koordinate t-1 ) genommen wird und auf das Pixel p-1 abgebildet wird, obwohl t-1 gar nicht mehr zu den Texturkoordinaten gehört? Und das nur in dem einen Fall mit dem X auf der Kante?

    Oder anders ausgedrückt: Fall 4 ist im Prinzip die Grenze zwischen Fall 2 und 3. Daher finde ich es unintuitiv, dass weder 2 noch 3 sich gleich wie 4 verhalten.

    dot schrieb:

    Wie kommen die "falschen" Koordinaten denn überhaupt zustande?

    Zum einen, wenn logische Objektpositionen direkt auf Koordinaten wie 0.5 sind (und 1:1 auf Pixel gemappt werden), was aber durch Runden leicht umgangen werden kann.

    Etwas komplizierter wird es bei Mechanismen wie "setze Mittelpunkt auf diese Koordinate" (wobei das ganze Objekt eine Texturlänge mit ungerader Anzahl Pixel hat). Nicht dass es nicht möglich wäre, aber es kommt mir halt etwas wie ein Workaround vor, für Screenkoordinaten jeweils Fallunterscheidungen einzuführen, nur um diesen singulären Punkt auf der Kante nicht zu treffen 🙂



  • Nexus schrieb:

    Im letzten Fall verstehe ich nicht, wieso jetzt plötzlich das vorherige Texel (Koordinate t-1 ) genommen wird und auf das Pixel p-1 abgebildet wird, obwohl t-1 gar nicht mehr zu den Texturkoordinaten gehört?

    Überleg mal was "gehört nichtmehr zu den Texturkoordinaten" eigentlich bedeuten soll!? Für dich macht das natürlich Sinn, aber das liegt daran dass du ja weißt was du willst. Ich glaub du denkst grad verkehrt rum: Es werden nicht Texel auf Pixel sondern Pixel auf Texel gemapped. Die Texturkoordinaten die zählen sind die Texturkoordinaten an der Position an der gesampled wird, also am Mittelpunkt des jeweiligen Pixles. Überleg dir mal wo genau die Texturkoordinaten der Pixelmittelpunkte hinfallen (schwarz sind die Pixel, blau die Texel, dunkelblau markiert dein Texturrechteck, grüne Linien zeigen welche Texel genommen werden): Bsp. 1, Bsp. 2. Und jetzt der Fall mit 0.5. Wie du siehst ist es genau und nur genau in diesem Fall nicht eindeutig, es gibt 4 Möglichkeiten. Hast du das schonmal auf verschiedenen Grafikkarten gestestet? Ist das Ergebnis überall identisch?


Anmelden zum Antworten