OpenGL-Rasterungsproblem
-
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 Pixelp-1
abgebildet wird, obwohlt-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 Pixelp-1
abgebildet wird, obwohlt-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?
-
mal so als grundlage, die rasterisierung kann man sich etwas so vorstellen:
-pro pixel auf dem screen berechnet man die barizentrischen koordinaten auf dem dreieck
-mit diesen berechnet man die UVs, anhand der gewichtung
-mit diesen liest man aus der texturfloat2 UV; float3 Weight for(float y=[b]0.5f[/b];y<width;y++) for(float x=[b]0.5f[/b];x<width;x++) if(Triangle.Coordinates(x,y,Weights)) Texture->Pixel(Triangle.UVfor(Weights,UV));
entsprechend, wie ich sagte, wenn du richtige UVs hast und dein dreieck pixel aligned ist, wirst du _keinen_ unterschied zwischen point und linear filtering sehen. dann wirst du wohl auch keine probleme mit dem verschieben von objekten mehr haben.
mit falschen UVs, wirst du die von dir beobachteten probleme haben.
-
Vielen Dank für eure Erklärungen und Darstellungen!
dot schrieb:
Wie du siehst ist es genau und nur genau in diesem Fall nicht eindeutig, es gibt 4 Möglichkeiten.
Genau, aber verwirrend finde ich, dass das Resultat trotzdem anders ist als wenn der Punkt in einem der 4 Quadranten statt auf ihrem Schnittpunkt läge.
rapso schrieb:
entsprechend, wie ich sagte, wenn du richtige UVs hast und dein dreieck pixel aligned ist, wirst du _keinen_ unterschied zwischen point und linear filtering sehen.
Aus dem pixel-aligned schliesse ich, dass nur gannzahlige Pixelkoordinaten in dem Sinne richtig sind (so dass man
GL_LINEAR
undGL_NEAREST
austauschen könnte). Trotzdem rundet jaGL_NEAREST
bis auf den einen Fall immer vernünftig... Oder soll man solche Fälle generell vermeiden?
-
Nexus schrieb:
Genau, aber verwirrend finde ich, dass das Resultat trotzdem anders ist als wenn der Punkt in einem der 4 Quadranten statt auf ihrem Schnittpunkt läge.
Ich versteh nicht inwiefern das Resultat da "anders" sein soll!? Wie sollte es deiner Meinung nach denn "richtig" sein!?
Nexus schrieb:
Aus dem pixel-aligned schliesse ich, dass nur gannzahlige Pixelkoordinaten in dem Sinne richtig sind (so dass man
GL_LINEAR
undGL_NEAREST
austauschen könnte).Exakt. Genauer gesagt: Ganzzahlige Pixel und Texelkoordinaten oder halbzahlige Pixel und Texelkoordinaten oder Koordinaten an n/m Pixeln und n/m Texeln. Der Punkt ist dass Screenspace und Texturespace aligned sein müssen.
Nexus schrieb:
Trotzdem rundet ja
GL_NEAREST
bis auf den einen Fall immer vernünftig... Oder soll man solche Fälle generell vermeiden?GL_NEAREST rundet immer vernünftig. Dein Problem ist dass du eigentlich kein runden willst. Du willst Texel auf Pixel mappen.
-
Nexus schrieb:
rapso schrieb:
entsprechend, wie ich sagte, wenn du richtige UVs hast und dein dreieck pixel aligned ist, wirst du _keinen_ unterschied zwischen point und linear filtering sehen.
Aus dem pixel-aligned schliesse ich, dass nur gannzahlige Pixelkoordinaten in dem Sinne richtig sind (so dass man
GL_LINEAR
undGL_NEAREST
austauschen könnte). Trotzdem rundet jaGL_NEAREST
bis auf den einen Fall immer vernünftig... Oder soll man solche Fälle generell vermeiden?ja, wie der zufall so will siehst es zufaellig fast immer so aus wie du es moechtest, trotz vermutlich falscher koordinaten.
oder kannst du mit "ganzzahligen" pixelkoordinaten zwischen GL_LINEAR und GL_NEAREST hin und her schalten ohne einen unterschied zu sehen?
-
dot schrieb:
Ich versteh nicht inwiefern das Resultat da "anders" sein soll!? Wie sollte es deiner Meinung nach denn "richtig" sein!?
Richtig fände ich, wenn das Abbildungsverhalten auf dem Grenzpunkt gleich wäre wie in einem der vier Quadranten (von mir aus willkürlich ein Quadrant ausgewählt, aber immer der gleiche).
raps schrieb:
ja, wie der zufall so will siehst es zufaellig fast immer so aus wie du es moechtest, trotz vermutlich falscher koordinaten.
Okay, dann werde ich versuchen, einfach die Koordinaten "richtig" (d.h. ganzzahlig) zu haben. Zwar schade, dass ich wieder manuell runden muss. Das macht doch
GL_NEAREST
ziemlich nutzlos, oder?raps schrieb:
oder kannst du mit "ganzzahligen" pixelkoordinaten zwischen GL_LINEAR und GL_NEAREST hin und her schalten ohne einen unterschied zu sehen?
Soweit ich das beobachten konnte, ja (also kein Unterschied).
-
Nexus schrieb:
dot schrieb:
Ich versteh nicht inwiefern das Resultat da "anders" sein soll!? Wie sollte es deiner Meinung nach denn "richtig" sein!?
Richtig fände ich, wenn das Abbildungsverhalten auf dem Grenzpunkt gleich wäre wie in einem der vier Quadranten (von mir aus willkürlich ein Quadrant ausgewählt, aber immer der gleiche).
Genau das ist doch was passiert!?
raps schrieb:
Zwar schade, dass ich wieder manuell runden muss. Das macht doch
GL_NEAREST
ziemlich nutzlos, oder?Sind Fußbälle nutzlos weil du damit nicht Radfahren kannst?
-
dot schrieb:
Genau das ist doch was passiert!?
Hmm. Ich habs jetzt nochmals an einem Beispiel genau angeschaut und du hast Recht. Sorry, mein Denkfehler war, dass ich annahm, das abgebildete Texturrechteck würde sich im 0.5-Fall vergrössern. Mir fiel nämlich nur auf, dass an der linken Kante plötzlich ein zusätzliches Texel von einem benachbarten Objekt gezeichnet wird, aber nicht, dass rechts ein Texel verschwindet. Tut mir leid, vielen Dank für die Geduld
Eine Frage hätte ich aber dennoch: Du hast ja von der Top-Left-Fillconvention gesprochen. Hätte man das nicht mit dem Rundungsverhalten von
GL_NEAREST
im Grenzfall konsistent machen können, sodass die Abbildung trotzdem richtig abläuft?dot schrieb:
Sind Fußbälle nutzlos weil du damit nicht Radfahren kannst?
Ein Fussball ist nutzlos, wenn er bei jedem hundertsten Tritt explodiert
Dass
GL_NEAREST
nur in 99% der Fälle so rundet, dass das korrekte Texturrechteck ausgewählt wird, macht es effektiv nutzlos, weil man trotzdem die Fallunterscheidung auf User-Seite für das 1% braucht und somit gleichGL_LINEAR
auf ganzzahligen Koordinaten nehmen könnte. Oder?
-
Nexus schrieb:
Eine Frage hätte ich aber dennoch: Du hast ja von der Top-Left-Fillconvention gesprochen. Hätte man das nicht mit dem Rundungsverhalten von
GL_NEAREST
im Grenzfall konsistent machen können, sodass die Abbildung trotzdem richtig abläuft?Es gibt hier nichts was man irgendwie konsistent machen müsste, im Gegenteil: Top-Left-Convention heißt dass im Zweifelsfall nach links oben gerundet wird. Wenn man das mit den Texturkoordinaten gleich macht dann passiert genau was du nicht haben willst. Aber natürlich hätte man die Fillconvention und das exakte Verhalten von GL_NEAREST irgendwie festlegen können. Hat man eben nicht. Selbst wenn man hätte, es gäbe keinen besonderen Grund sich ausgerechnet für eine Variante zu entscheiden die hier das von dir favorisierte Ergebnis liefert. Nur weil es dir im Kontext mit einem Spezialfall deines speziellen Problems unter Annahme bestimmter Dinge die du für "richtig" hältst als "richtig" erscheint heißt das noch lange nicht dass es eine generelle Wahrheit sein muss. Aufgabe des Rasterizers ist die bestmögliche Pixel-Approximation deines Dreiecks zu finden. Und genau das tut er. Und zwar ohne Ausnahme in jedem Fall zu 100% korrekt. Nur weil das was passiert nicht das ist was du erwartest heißt das noch lange nicht dass es nicht richtig ist
Nexus schrieb:
Dass
GL_NEAREST
nur in 99% der Fälle so rundet, dass das korrekte Texturrechteck ausgewählt wird, macht es effektiv nutzlos, weil man trotzdem die Fallunterscheidung auf User-Seite für das 1% braucht und somit gleichGL_LINEAR
auf ganzzahligen Koordinaten nehmen könnte. Oder?Dein Problem ist dass du aus irgendeinem Grund (was auch immer das sein mag) davon ausgehst dass GL_NEAREST genau dafür gedacht sein sollte wofür du es verwenden willst, was aber nicht der Fall ist...
-
Okay, anders gefragt: Wie löst du das Problem dieses Threads, wenn du kontinuierliche Pixelkoordinaten hast?
Falls du jetzt sagst, ich solle zuerst runden, in welchen Fällen bringt mir
GL_NEAREST
dann einen Vorteil gegenüberGL_LINEAR
?
-
Nexus schrieb:
Okay, anders gefragt: Wie löst du das Problem dieses Threads, wenn du kontinuierliche Pixelkoordinaten hast?
Dazu müsste ich erstmal wissen was genau das Problem dieses Threads ist, also was du eigentlich genau erreichen willst. Wie schon mehrmals gesagt liegt das Problem ja nicht an den kontinuierlichen Koordinaten sondern daran dass du eben zu den kontinuierlichen Koordinaten passende Texturkoordinaten brauchst...
Nexus schrieb:
Falls du jetzt sagst, ich solle zuerst runden, in welchen Fällen bringt mir
GL_NEAREST
dann einen Vorteil gegenüberGL_LINEAR
?GL_NEAREST ist der einfachste Texturfilter. Er bringt genau dann einen Vorteil wenn du nichts Besseres brauchst. Auf moderner Hardware ist der Geschwindigkeitsunterschied zwischen GL_NEAREST und GL_LINEAR aber normal kaum messbar.
-
dot schrieb:
Dazu müsste ich erstmal wissen was genau das Problem dieses Threads ist, also was du eigentlich genau erreichen willst.
Zunächst mal habe ich kontinuierliche logische Koordinaten und möchte Texturen 1:1 abbilden, ohne dass bei gewissen Punkten (hier .5) plötzlich ein anderes Texturrechteck verwendet wird.
dot schrieb:
Wie schon mehrmals gesagt liegt das Problem ja nicht an den kontinuierlichen Koordinaten sondern daran dass du eben zu den kontinuierlichen Koordinaten passende Texturkoordinaten brauchst...
Aber was wäre dann passend? Wenn ich die Texturkoordinaten anders wähle, verschiebt sich das Problem lediglich. Oder soll ich die Texturkoordinaten vor jedem Zeichnen an die Pixelkoordinaten anpassen? Ich bin bisher eigentlich davon ausgegangen, dass die Texturkoordinaten bei gleichbleibendem Bild konstant bleiben.
-
Nexus schrieb:
dot schrieb:
Dazu müsste ich erstmal wissen was genau das Problem dieses Threads ist, also was du eigentlich genau erreichen willst.
Zunächst mal habe ich kontinuierliche logische Koordinaten und möchte Texturen 1:1 abbilden, ohne dass bei gewissen Punkten (hier .5) plötzlich ein anderes Texturrechteck verwendet wird.
Mit den richtigen Texturkoordinaten kein Problem.
Nexus schrieb:
Oder soll ich die Texturkoordinaten vor jedem Zeichnen an die Pixelkoordinaten anpassen?
Ja z.B., die letzten 3 Seiten hier beschäftigen sich genau mit dem warum.
Nexus schrieb:
Ich bin bisher eigentlich davon ausgegangen, dass die Texturkoordinaten bei gleichbleibendem Bild konstant bleiben.
Du mappest die gleichen Texel an immer andere Positionen und erwartest wirklich dass das keinen Unterschied macht?
-
dot schrieb:
Ja z.B., die letzten 3 Seiten hier beschäftigen sich genau mit dem warum.
Ja. Nur kann ich, wenn ich jedes Mal die Texturkoordinaten vor jedem Zeichnen neu setzen muss, gerade so gut gleich die Pixelkoordinaten anpassen. Von daher hoffte ich, dass es eine bessere Möglichkeit gäbe, aber dafür ist OpenGL wohl zu wenig abstrakt.
dot schrieb:
Du mappest die gleichen Texel an immer andere Positionen und erwartest wirklich dass das keinen Unterschied macht?
Wenn man
GL_NEAREST
verwendet, macht das für die meisten Fälle keinen Unterschied. Hätte ja sein können, dass man den Rest auch noch irgendwie hinbekommt.
-
Nexus schrieb:
]Ja. Nur kann ich, wenn ich jedes Mal die Texturkoordinaten vor jedem Zeichnen neu setzen muss, gerade so gut gleich die Pixelkoordinaten anpassen. Von daher hoffte ich, dass es eine bessere Möglichkeit gäbe, aber dafür ist OpenGL wohl zu wenig abstrakt.
Wie gesagt, das hat nichts mit zu weng abstrakt oder sonst irgendeiner Unzulänglichkeit von OpenGL zu tun, aber egal. Nur so als Tip: Du könntest das Runden auf ganze Pixel einfach im VertexShader machen
-
dot schrieb:
Wie gesagt, das hat nichts mit zu weng abstrakt oder sonst irgendeiner Unzulänglichkeit von OpenGL zu tun, aber egal.
Ich muss meine häufig benötigte Funktionalität manuell im User-Code implementieren -> OpenGL ist dafür nicht zuständig bzw. zu low-level.
Mit Vertex-Shadern kenne ich mich nicht gut aus, das könnte ich mir mal anschauen.
Jedenfalls vielen Dank für die vielen Antworten und Erläuterungen!