Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
Hallo Leute, in einem Programm habe ich momentan die Situation, dass ich sehr viele Fragmente pro Pixel rendere. Bei teuren Fragmentshadern ist das natürlich eine wesentliche Verschwendung von Rechenzeit. Also dachte ich mir, baue ich halt einen Z-Prepass ein:
Im DepthPass habe ich für den Fragmentshader natürlich was triviales genommen:
Code:
#version 330
outvec4 outColor;
void main(void){
outColor =vec4(1.0);
}
Vertex- und ggf. Geometryshader sind hingegen exakt gleich. Leider war das Ergebnis aufgrund von massivem Z-Fighting nicht zu gebrauchen.
Daraufhin habe ich dann die üblichen Tricks versucht:
mehr Bits für den Z-Buffer
gl_Position im Shader als invariant deklariert. Hier ist mir ehrlich gesagt auch nicht ganz klar, woran der Grafiktreiber erkennt, in welchen Vertexshadern die Positionen exakt gleich berechnet werden sollen. Ich bekomme aber selbst dann Z-Fighting, wenn ich Depth- und Color-Pass mit dem selben Program ausführe.
glDepthFunc(GL_LEQUAL) hatte ich sowieso eingestellt
glPolygonOffset mit verschiedensten Parametern vor dem Z-Prepass aufrufen (das glEnable/glDisable habe ich nicht vergessen). Neben viel herumprobieren (mit teils extremen Werten) habe ich auch versucht, logische Werte als Parameter anzugeben, wobei ich mich nach dieser Erklärung gerichtet habe (ganz unten, letzter Abschnitt).
die ProjectionMatrix mittels gluPerspective so modifizieren, dass glPolygonOffset emuliert wird. Angeblich soll es dazu mal einen Artikel in einem "Game Programming Gems"-Teil gegeben haben. Ich habe einfach mal ohne viel Mathe die Near Clipping Plane hin- und her geschoben.
Leider hat das alles nicht geklappt. Was ist denn eigentlich der übliche Weg, mehrere Passes der selben Geometrie ohne Z-Fighting zu rendern? Oder verwenden hier alle Deferred Shading, um gerade diesem Problem aus dem Weg zu gehen?
Vielen Dank schonmal für eure Hilfe!
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
Ja, ich verwende in beiden Passes GL_LEQUAL. Dies sollte auch meiner Meinung nach in Ordnung sein. Schließlich ist der einzige Effekt, den GL_LEQUAL im Vergleich zu GL_LESS hat, dass in einige Pixel während des Tiefenpasses mehrfach der selbe Wert geschrieben wird. Habe es gerade auch nochmal ausprobiert: GL_LESS für den Z-Pass ändert nichts.
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Also ich würde mich unabhänig von dem Problem mal mit Dingen in Richtung des Deferred Shadings beschäftigen. Der Z-Prepass verdoppelt mal eben die Anzahl Drawcalls und ist damit eine recht aufwendige Lösung. Ich weiß nicht, ob du das wirklich willst. (Kommt aber natürlich auf den Fall an.)
Eigentlich gibt es keinen Grund, warum hier Z-Fighting auftreten müsste. Ich würde an deiner Stelle man das Verhältnis der Near-/Farplane überprüfen und ggf. dann anpassen. Erfahrungsgemäß ist das der häufigste Fehler.
Bei "GL_LESS" müsste eigentlich gar nichts mehr zu sehen sein, weil im Z-Buffer ja die kleinsten Werte stehen. Der "GL_LESS"-Test sollte deshalb immer schief gehen. Ich würde das eher mal mit "GL_EQUAL" bzw. Andere zum Überprüfen verwenden. "GL_EQUAL" müsste das gleiche Ergebnis wie "GL_LEQUAL" liefern. "GL_LESS" hingegen nicht. Wenn du sagst, es ändert sich nichts, dann stimmt noch etwas anderes mit dem Z-Buffer nicht.
Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
OpenglerF hat geschrieben:
Also ich würde mich unabhänig von dem Problem mal mit Dingen in Richtung des Deferred Shadings beschäftigen. Der Z-Prepass verdoppelt mal eben die Anzahl Drawcalls und ist damit eine recht aufwendige Lösung.
Momentan sind nicht die DrawCalls das Problem, sondern ein teurer Fragmentshader, der zu oft aufgerufen wird, sowie unter Umständen die Speicherbandbreite. Die Sparsamkeit mit API-Calls wird meiner Meinung nach sowieso etwas überbewertet. Derzeit habe ich weniger als 100 pro Frame - davon ca. 10-20% DrawCalls. Neulich habe ich mit apitrace einen kommerziellen OpenGL-Titel untersucht. Ergebnis: >30000 (dreißigtausend) API-Aufrufe pro Frame, darunter auch jede Menge deprecated Zeug wie Fullscreen-Quads im Immediate Mode und vieles, von dem ich lange weg bin. Aber das Spiel läuft trotzdem größtenteils flüssig.
Deferred Shading mache ich vielleicht irgendwann demnächst mal, weil es nebenbei auch schöne neue Möglichkeiten bietet, wie z.B. Screenspace Reflections. Aber derzeit glaube ich, dass ein Z-Prepass die passendere Lösung wäre (wenn er denn funktionieren würde), zumal Deferred Shading neue Probleme mit sich bringt (Multisampling) und z.B. transparente Objekte sowieso weiterhin forward gerendert werden müssten.
OpenglerF hat geschrieben:
Ich würde an deiner Stelle man das Verhältnis der Near-/Farplane überprüfen und ggf. dann anpassen. Erfahrungsgemäß ist das der häufigste Fehler.
Selbst wenn ich das Verhältnis Far/Near auf 10 reduziere sehe ich massives Z-Fighting (bei 24 Bits für den Depthbuffer).
OpenglerF hat geschrieben:
Bei "GL_LESS" müsste eigentlich gar nichts mehr zu sehen sein, weil im Z-Buffer ja die kleinsten Werte stehen.
Ich hab mich wohl nicht deutlich genug ausgedrückt. Wenn ich GL_LESS für den Z-Pass und weiterhin GL_LEQUAL für den Color-Pass nehme, ist keine Änderung gegenüber GL_LEQUAL in beiden Passes zu sehen. Im Color-Pass hat GL_EQUAL wie erwartet die gleiche Wirkung wie GL_LEQUAL.
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Zuletzt geändert von glAwesome am Di Feb 18, 2014 22:57, insgesamt 1-mal geändert.
Selbst wenn ich das Verhältnis Far/Near auf 10 reduziere sehe ich massives Z-Fighting (bei 24 Bits für den Depthbuffer).
Das kann halt gar nicht sein. Hast du mal ein Bild?
Außerdem: Z-Prepassing verändert an der Genauigkeit des Depthbuffers nichts, deshalb kann es alleine dadurch auch nicht mehr oder weniger Z-Fighting geben.
Möglicherweise ist die Geometry beim Prepass eine Andere als beim Rendern?
Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
Ich habe drei Bilder angehängt, wo man den Unterschied deutlich sieht. Der blaue Hintergrund ist halt einfach die Hintergrundfarbe (glClearColor). Das weiße ist die Geometrie, denn ich verwende sowohl beim Z-Prepass als auch beim Color-Pass den selben Fragment- und Vertexshader. Den Fragmentshader habe oben schon gepostet. Ein Geometryshader ist in diesem Beispiel nicht dabei. Es werden haargenau die gleichen Vertices gerendert, aber trotzdem schlägt der Z-Test bei einigen Fragmenten im Color-Pass fehl, wenn vorher der z-Pass aktiviert war.
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Ist das wirklich Z-Fighting? Deine Bilder lassen sich für mich sehr schwer einordenen, nachdem alles in der gleichen Farbe ist. Irgendwie sieht mir das eher nach Genauigkeitsproblemen beim Vergleich aus. Möglicherweise irgendwo numerische Ungenauigkeiten bei Gleitkommazahlen. Ich würde mal "GL_LEQUAL" beim Rendern anstellen und ein kleines Z-Offset beim Prepass hinzufügen.
Die Depthmask würde ich dann auch generell weglassen. Wenn der Offset "großzügig" gewählt ist, kommen sonst möglicherweise in einer echten Szene ein paar falsche Fragmente durch.
Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
Ich werd' bekloppt. Ich habe nun das
Code:
glDepthMask(GL_FALSE);
am Anfang des Color-Passes auskommentiert und siehe da: Es funktioniert! Ich verstehe nur nicht, warum... Warum sollte das Schreiben in den Tiefenpuffer während des Color-Passes erlaubt sein?
OpenglerF hat geschrieben:
Irgendwie sieht mir das eher nach Genauigkeitsproblemen beim Vergleich aus. Möglicherweise irgendwo numerische Ungenauigkeiten bei Gleitkommazahlen.
Nennt man nicht genau das Z-Fighting?
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Warum sollte das Schreiben in den Tiefenpuffer während des Color-Passes erlaubt sein?
Die Frage ist für mich eher, warum es nicht erlaubt sein sollte... Das das Problem ausgelöst hat, wundert mich aber doch.
Zitat:
Nennt man nicht genau das Z-Fighting?
Unter Z-Fighting versteht man, wenn mehrere Fragmente in unterschiedlicher Tiefe den gleichen Z-Wert zugeordnet bekommen. Dann ist die Zeichenreihenfolge nicht mehr durch den Z-Buffer definiert und es kommt zu den charakteristischen Zacken am Rand. Bei war aber die Genauigkeit des Z-Buffers absolut ausreichend: Nur durch numerische Ungenauigkeiten haben Fragmente die eigentlich in der gleichen Tiefe liegen, teilweise unterschiedliche Z-Werte zugeordnet bekommen. (So habe ich das zumindest gedeutet.)
Ich würde die jetzige Lösung übrigens gründlich auf anderen Grafikkarten testen. Möglicherweise nehmen es manche Hersteller auch nicht so genau mit der Genauigkeit und es kommt wieder zu Darstellungsschwierigkeiten. Dann braucht es doch noch ein "Sicherheitoffset", damit die Werte nicht genau exakt übereinstimmen müssen.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
OpenglerF hat geschrieben:
Zitat:
Nennt man nicht genau das Z-Fighting?
Unter Z-Fighting versteht man, wenn mehrere Fragmente in unterschiedlicher Tiefe den gleichen Z-Wert zugeordnet bekommen. Dann ist die Zeichenreihenfolge nicht mehr durch den Z-Buffer definiert und es kommt zu den charakteristischen Zacken am Rand. Bei war aber die Genauigkeit des Z-Buffers absolut ausreichend: Nur durch numerische Ungenauigkeiten haben Fragmente die eigentlich in der gleichen Tiefe liegen, teilweise unterschiedliche Z-Werte zugeordnet bekommen. (So habe ich das zumindest gedeutet.)
Das ist äquivalent, führt zum gleichen Phänomen und ist ununterscheidbar von der Anwendungsseite. Ob nun die Numerik rauscht oder die Quantisierung in den Bits vom Z-Buffer ist egal, es kommt zum Fighting.
grüße
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Naja, das ist doch deutlich unterscheidbar. Z-Fighting tritt nämlich immer dann auf, wenn Fragmente nahe hintereinander in Z-Richtung liegen. Ein Ungenauigkeitsproblem kann prinzipiell immer auftreten, auch bei nur einen einzigen Polygon.
"Z-Fighting" ist doch als "Z-Kampf" zu übersetzen, also wenn mehrere Fragmente "um das Vorrecht kämpfen", am Ende auf den Bildschirm zu kommen. Das liegt aber nicht mehr vor.
Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
OpenglerF hat geschrieben:
Die Frage ist für mich eher, warum es nicht erlaubt sein sollte...
Naja, theoretisch spart es etwas Bandbreite, wenn man sich das Überschreiben von sowieso gleichen Werten spart. Den z-Prepass macht man ja, damit es schneller läuft. Edit: Wobei, wenn man den Z-Pass mit Offset durchführt, ist es natürlich sinnvoll, das Schreiben in den Z-Buffer zu erlauben.
OpenglerF hat geschrieben:
"Z-Fighting" ist doch als "Z-Kampf" zu übersetzen, also wenn mehrere Fragmente "um das Vorrecht kämpfen", am Ende auf den Bildschirm zu kommen.
Aber das tun sie doch hier auch. Nur, dass das eine Fragment nicht in den Farbpuffer, sondern nur in den Depthbuffer geschrieben wurde.
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Theoretisch eben. In der Praxis kommt kann ich mir gut vorstellen, dass der Treiber darauf nicht optimiert ist.
Zitat:
Aber das tun sie doch hier auch. Nur, dass das eine Fragment nicht in den Farbpuffer, sondern nur in den Depthbuffer geschrieben wurde.
Nein, wenn das Problem durch Genauigkeit ausgelöst wird. Dann sind beim zweiten Pass einfach bei den gleichen Polygonen die Z-Werte minimal größer als beim Ersten. Da Kämpft also nichts, sondern es wird einfach an einigen Stellen nicht gerendert.
Registriert: Mi Nov 30, 2011 21:41 Beiträge: 136 Wohnort: Bad Vilbel
Programmiersprache: Delphi 7
Hallo,
ich habe bei mir in der Engine auch einen Z-Pass eingebaut, und ich kann ohne Probleme glDepthMask(false); vor dem Rendern der eigentlichen Szene aufrufen (ich arbeite ohne Offset). Mich würde mal interessieren, wovon dieses Problem dann eigentlich abhängig ist? (Treiber / Grafikkarte an sich / ...)
Mein Engine habe ich auch auf diversen anderen Rechner getestet, Z-Fighting habe ich da niemals bemerkt, obwohl jede weitere Graka an meinen Shadern genörgelt hat^^.
P.S. ich habe sogar mit GL_EQUAL als DepthFunc gearbeitet und trotzdem keine Probleme bei mir bemerkt.
Mitglieder in diesem Forum: 0 Mitglieder und 8 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.