OpenGL: Mehrfache Passes für Fragment Shader?
-
Folgende Situation:
Für ein Rechteck berechnet mir ein Shader für jeden Pixel float-Werte. Ich will nun von diesen Max und Min berechnen und die Werte von [Min,Max] in [0.0,1.0] linear interpolieren. Dazu müsste ich drei Fragmentshader haben, einen der die floats berechnet und dann einen, der Min/Max von diesen berechnet, und zuletzt einen, der die zwischen 0.0 und 1.0 interpolierten Werte in Farben umsetzt. Geht das und wie?
-
Wenn du breit bist nur Opengl 4.2 zu unterstützen geht das relativ simpel:
Erst renderst du die Szene in einen Offscreen FBO mit einen Shader, der dir den Wert berechnet und mit http://www.opengl.org/registry/specs/EXT/shader_image_load_store.txt imageAtomicMin imageAtomicMax in ein Image (1px x 2px zB.) das Minimum und Maximum schreibt (Musst du nur eine für deinen Fall ausreichende "Bijektion" Int <-> Float finden für die gilt x < y <=> f(x) < f(y))
In einem 2. Pass gehst du in einem Fullscreenpass über den FBO und wendest mit den gewonnenen Werten einfach deine Interpolation oder Lineare Abbildung oder was auch immer du willst an.
Falls du nicht bereit bist 4.2 einzusetzen (Willkommen in der hässlichen Realität!) muss du minimum und Maximum anders gewinnen.
Das geht zum Beispiel das du immer wieder in halbiert große FBO's renderst und dabei in einem Shader jeweils die 4 umliegenden Pixel liest und das Minimum berechnest und als Farbe rausschreibst. Das muss du dann machen bis du bei einem 1px x 1px fbo angekommen bist und kannst diesen dann nutzen.
Ein anderer grausam langsamer Weg wäre die Komplette Textur auf den CPU zurückzulesen und Minimum und Maximum dort von Hand (gegebenenfalls mit SEE Tricks) zu bestimmen.
Alternativ wäre ein Blick Richtung OpenCL interessant. Vielleicht kann man das mit der OpenGL Interoperabilität lösen. Ich kenn mich in dem Bereich allerdings nicht aus.
(Es gibt übrigens auch andere dreckige Hacks um ein HDR Tonemapping zu machen die weniger Stress machen, wenn es das ist was du machen willst; ein f(x) = x/(x+c) kann Wunder wirken wenn man c aus 20 Samples auf den Bildschirm berechnet und eine Beschleunigte Änderung von c pro Frame nutzt)
-
Wie werden diese Werte denn berechnet? Hast du wirklich keine Möglichkeit, das Minimum und Maximum irgendwie direkt zu bestimmen? Falls nein, würde ich wohl zu der von blard beschriebenen Variante mit dem Downsampling raten, wobei du, wenn du Shader benutzt, auch einfach mehr Pixel pro Pass lesen und z.B. in jeder Dimension vierteln oder achteln kannst. Es würde mich zwar interessieren, wie gut die imageAtomicMin() Variante genau läuft, aber man kann wohl davon ausgehen, dass die Performance dabei spätestens ab einer gewissen Größe des Rechteckts im Keller ist...