Multisampling: Unterschied zwischen den Versionen
Doj (Diskussion | Beiträge) (erste Version) |
(Umsetzungsmöglichkeit hinzugefügt) |
||
Zeile 2: | Zeile 2: | ||
Der Vorteil dieser Methode ist, dass nur an den [[Kante]]n von [[Polygon]]en der Antialiasing Effekt zum Vorschein kommt, hier also die Treppenbildung abgemildert wird. Innerhalb der Polygone werden die Pixeln jedoch herkömmlich berechnet, so dass der Schärfeverlust des [[Supersampling]]s hier nicht auftritt. | Der Vorteil dieser Methode ist, dass nur an den [[Kante]]n von [[Polygon]]en der Antialiasing Effekt zum Vorschein kommt, hier also die Treppenbildung abgemildert wird. Innerhalb der Polygone werden die Pixeln jedoch herkömmlich berechnet, so dass der Schärfeverlust des [[Supersampling]]s hier nicht auftritt. | ||
+ | |||
+ | ==Code Umsetzung== | ||
+ | |||
+ | Um beim rendern, da wir ja OpenGL benutzen, flexibel was das Betriebssystem betrifft bleiben, empfiehlt es sich beim Multisampling zunächst in ein FBO zu rendern (falls du nicht weisst was das ist, schau mal hier vorbei: [[Tutorial_Framebufferobject]]). Diesen werden wir dann anschließend einfach in den Framebuffer der OpenGL Anwendung kopieren. So und wie setzen wir das nun um? | ||
+ | |||
+ | Zunächst überprüfen wir erstmal ob Multisampling überhaupt von der Grafikkarte unterstützt wird: | ||
+ | |||
+ | |||
+ | <source lang="pascal"> | ||
+ | var GLsamples: integer; | ||
+ | [...] | ||
+ | glGetIntegerv(GL_MAX_SAMPLES_EXT, @GLsamples); | ||
+ | if (GLSamples = 0) then exit; | ||
+ | </source> | ||
+ | |||
+ | |||
+ | Wenn GL_MAX_SAMPLES_EXT = 0 ist bedeutet dies, dass Multisampling von der Grafikkarte nicht unterstützt wird. Ansonsten zeigt euch der Wert, wie der Name bereits sagt, was die maximale Anzahl an Samples ist die ihr benutzen drüft. Später also eine Überprüfung einzubauen, ob die gewünscht SampleStufe größer als die maximale Samplestufe ist, kann also auch nicht schaden. ;) | ||
+ | |||
+ | |||
+ | |||
+ | Der nächste Schritt besteht darin unser FBO zu erzeugen, dies läuft ähnlich zum Erzeugen eines normalen FBOs ab, verwendet aber statt einer Textur einen Colorbuffer. | ||
+ | |||
+ | <source lang="pascal"> | ||
+ | // FrameBuffer erstellen und aktivieren. GLFrameName ist hier wie üblich ein TGLUint. | ||
+ | glGenFramebuffers(1, @FGLFrameName); | ||
+ | glBindFramebuffer(GL_FRAMEBUFFER, FGLFrameName); | ||
+ | |||
+ | // ColorBuffer erstellen und aktivieren. | ||
+ | glGenRenderbuffers(1, @FGLColorName); | ||
+ | glBindRenderbuffer(GL_RENDERBUFFER, FGLColorName); | ||
+ | |||
+ | // Den Colorbuffer im Grafikspeicher erzeugen. | ||
+ | glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 4, GL_RGB8, Size.X, Size.Y); | ||
+ | // Parameter-Erklärung: | ||
+ | // GL_RENDERBUFFER_EXT - muss so sein | ||
+ | // 4 - Anzahl der Samples und darf maximal GL_MAX_SAMPLES_EXT entsprechen. | ||
+ | // GL_RGB8 - (könnte z.B. auch GL_RGBA8 wenn ihr einen Alpha-Kanal braucht) | ||
+ | // Size.X - X Größe des Framebuffers. Sollte der Größe des OpenGL Fensters entsprechen. | ||
+ | // Size.Y - Y Große des Framebuffers. Sollte der Größe des OpenGL Fensters entsprechen. | ||
+ | </source> | ||
+ | |||
+ | |||
+ | Der Hauptunterschied besteht in dem glRenderbufferStorageMultisampleEXT, was ich hier hoffentlich ausreichend mit Kommentaren ausgestattet habe. Danach erzeugen wir genauso den Tiefenbuffer. | ||
+ | |||
+ | |||
+ | <source lang="pascal"> | ||
+ | // Den Tiefenbuffer erzeugen | ||
+ | glGenRenderbuffers(1, @FGLDepthName); | ||
+ | glBindRenderbuffer(GL_RENDERBUFFER, FGLDepthName); | ||
+ | glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 4, GL_DEPTH_COMPONENT24, FSize.X, FSize.Y); | ||
+ | </source> | ||
+ | |||
+ | |||
+ | Hier gibts praktisch keinen Unterschied zum Colorbuffer, bis auf die Tatsache, dass es sich um einen Tiefenbuffer handelt (tatsächlich ^^) und wir daher GL_DEPTH_COMPONENT24 benutzen. Jetzt binden wir noch die beiden erzeugten Buffer an unseren Framebuffer, machen eine Fehlerüberprüfung (vgl. [[Tutorial_Framebufferobject]]) und unsere Framebuffer mit Multisampling ist bereit für den Einsatz. | ||
+ | |||
+ | |||
+ | <source lang="pascal"> | ||
+ | // Attach DepthBuffer to FrameBuffer | ||
+ | glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, FGLColorName); | ||
+ | glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, FGLDepthName); | ||
+ | |||
+ | // Check For Errors | ||
+ | CheckFBO(); | ||
+ | </source> | ||
+ | |||
+ | |||
+ | Das Rendern in unser FBO mit Multisampling funktioniert genauso wie bei normalen FBOs, man sollte vorher noch via glEnable(GL_MULTISAMPLE), Multisampling aktivieren aber sonst ändert sich nichts. Einfach binden, alles zeichnen und dann wieder Framebuffer 0 binden. Das generieren der Mipmaps fällt hier allerdings weg, da wie erstens '''keine''' Textur haben und zweitens unser Framebuffer nicht einer 2er Potenzgröße entspricht (oder habt ihr einen quadratischen Bildschirm mit der Auflösung 1024x1024 ;) ). | ||
+ | |||
+ | |||
+ | |||
+ | OK. Jetzt haben wir ein schönes Bild mit glatten Kanten in dem FBO jetzt wollen wir es auch noch sehen. Das funktionert wie folgt: | ||
+ | |||
+ | |||
+ | <source lang="pascal"> | ||
+ | glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FGLFrameName); // Unseren Framebuffer also Quelle binden | ||
+ | glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); // Den OpenGL Framebuffer als Ziel binden | ||
+ | glBlitFramebufferEXT(0, 0, FSize.X, FSize.Y, 0, 0, FSize.X, FSize.Y, GL_COLOR_BUFFER_BIT, GL_NEAREST); | ||
+ | // glBlitFramebuffer kopiert die Werte aus dem READ_FRAMEBUFFER in den DRAW_FRAMEBUFFER. | ||
+ | // Parameter | ||
+ | // Die ersten 4 Werte beschreiben das LeseRechteck im READ_FRAMEBUFFER | ||
+ | // Die nächsten 4 Werte beschreiben das SchreibeRechteck im DRAW_FRAMEBUFFER | ||
+ | // GL_COLOR_BUFFER_BIT sagt was kopiert werden soll. Um Rechenleistung zu sparen wurde hier lediglich der Colorbuffer kopiert. | ||
+ | // GL_DEPTH_BUFFER_BIT und GL_STENCIL_BUFFER_BIT könnten wenn nötig mitkopiert werden (dafür einfach mit einem OR verknüpfen) | ||
+ | // Als letztes noch GL_NEAREST da wir auf Filtering verzichten können, da die beiden Buffer die selbe größe haben. | ||
+ | </source> | ||
+ | |||
+ | |||
+ | So jetzt bekommt ihr das Ergebnis aus dem Multisampled FBO auch zu Gesicht. Wozu, werdet ihr euch jetzt fragen, muss das FBO die selbe größe wie das Fenster haben, wenn glBlitFramebuffer doch auch eine Filterfunktion bietet. Das Problem an der Sache ist, dass die Funktion häufiger mal INVALID_OPERATION ausgibt, wenn die beiden Rechtecke nicht die selbe Größe haben (genaueres auf opengl.org: [http://www.opengl.org/sdk/docs/man3/xhtml/glBlitFramebuffer.xml]). Daher einfach dem Framebuffer wie oben beschrieben die selbe Größe wie dem OpenGL Fenster zuweisen und alles funktioniert. | ||
+ | |||
+ | |||
+ | ==Andere Verwendungszwecke== | ||
+ | |||
+ | Falls ihr eines der folgenden Sachen mit eurem FBO machen wollt: | ||
+ | |||
+ | |||
+ | - Post-Processing Shader | ||
+ | |||
+ | - FBO als Textur für Objekt benutzen | ||
+ | |||
+ | - glReadPixels benutzen | ||
+ | |||
+ | |||
+ | Dann müsst ihr leider einen kleiner Workaround einbauen. Anstatt wie im letzten Codebeispiel als Ziel den OpenGL Framebuffer zu binden, bindet ihr stattdessen ein FBO '''OHNE''' Multisampling, dass ihr ganz normal mit einer Textur, statt einem Colorbuffer füllt. Mit dem könnt ihr dann wieder ganz normal Post-Processing und den sonstigen SchnickSchnack anstellen. | ||
==Siehe auch== | ==Siehe auch== | ||
[[Supersampling]] | [[Supersampling]] |
Aktuelle Version vom 4. April 2013, 12:35 Uhr
Multisampling ist eine Methode um die Aliasing Effekte zu verringern. Ähnlich dem Supersampling werden für jedes Pixel (des Zielbildes) zunächst mehrere SubPixel berechnet, diesmal jedoch zunächst nur deren Z-Werte (siehe Tiefenpuffer). Nur wenn der Unterschied zwischen den Z-Werten einen Schwellenwert übersteigt, werden die Farbwerte der Subpixel gemittelt und als Farbwert des Pixels benutzt. Ansonsten wird der Farbwert des Pixels direkt (ohne SubPixel) berechnet.
Der Vorteil dieser Methode ist, dass nur an den Kanten von Polygonen der Antialiasing Effekt zum Vorschein kommt, hier also die Treppenbildung abgemildert wird. Innerhalb der Polygone werden die Pixeln jedoch herkömmlich berechnet, so dass der Schärfeverlust des Supersamplings hier nicht auftritt.
Code Umsetzung
Um beim rendern, da wir ja OpenGL benutzen, flexibel was das Betriebssystem betrifft bleiben, empfiehlt es sich beim Multisampling zunächst in ein FBO zu rendern (falls du nicht weisst was das ist, schau mal hier vorbei: Tutorial_Framebufferobject). Diesen werden wir dann anschließend einfach in den Framebuffer der OpenGL Anwendung kopieren. So und wie setzen wir das nun um?
Zunächst überprüfen wir erstmal ob Multisampling überhaupt von der Grafikkarte unterstützt wird:
var GLsamples: integer;
[...]
glGetIntegerv(GL_MAX_SAMPLES_EXT, @GLsamples);
if (GLSamples = 0) then exit;
Wenn GL_MAX_SAMPLES_EXT = 0 ist bedeutet dies, dass Multisampling von der Grafikkarte nicht unterstützt wird. Ansonsten zeigt euch der Wert, wie der Name bereits sagt, was die maximale Anzahl an Samples ist die ihr benutzen drüft. Später also eine Überprüfung einzubauen, ob die gewünscht SampleStufe größer als die maximale Samplestufe ist, kann also auch nicht schaden. ;)
Der nächste Schritt besteht darin unser FBO zu erzeugen, dies läuft ähnlich zum Erzeugen eines normalen FBOs ab, verwendet aber statt einer Textur einen Colorbuffer.
// FrameBuffer erstellen und aktivieren. GLFrameName ist hier wie üblich ein TGLUint.
glGenFramebuffers(1, @FGLFrameName);
glBindFramebuffer(GL_FRAMEBUFFER, FGLFrameName);
// ColorBuffer erstellen und aktivieren.
glGenRenderbuffers(1, @FGLColorName);
glBindRenderbuffer(GL_RENDERBUFFER, FGLColorName);
// Den Colorbuffer im Grafikspeicher erzeugen.
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 4, GL_RGB8, Size.X, Size.Y);
// Parameter-Erklärung:
// GL_RENDERBUFFER_EXT - muss so sein
// 4 - Anzahl der Samples und darf maximal GL_MAX_SAMPLES_EXT entsprechen.
// GL_RGB8 - (könnte z.B. auch GL_RGBA8 wenn ihr einen Alpha-Kanal braucht)
// Size.X - X Größe des Framebuffers. Sollte der Größe des OpenGL Fensters entsprechen.
// Size.Y - Y Große des Framebuffers. Sollte der Größe des OpenGL Fensters entsprechen.
Der Hauptunterschied besteht in dem glRenderbufferStorageMultisampleEXT, was ich hier hoffentlich ausreichend mit Kommentaren ausgestattet habe. Danach erzeugen wir genauso den Tiefenbuffer.
// Den Tiefenbuffer erzeugen
glGenRenderbuffers(1, @FGLDepthName);
glBindRenderbuffer(GL_RENDERBUFFER, FGLDepthName);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, 4, GL_DEPTH_COMPONENT24, FSize.X, FSize.Y);
Hier gibts praktisch keinen Unterschied zum Colorbuffer, bis auf die Tatsache, dass es sich um einen Tiefenbuffer handelt (tatsächlich ^^) und wir daher GL_DEPTH_COMPONENT24 benutzen. Jetzt binden wir noch die beiden erzeugten Buffer an unseren Framebuffer, machen eine Fehlerüberprüfung (vgl. Tutorial_Framebufferobject) und unsere Framebuffer mit Multisampling ist bereit für den Einsatz.
// Attach DepthBuffer to FrameBuffer
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, FGLColorName);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, FGLDepthName);
// Check For Errors
CheckFBO();
Das Rendern in unser FBO mit Multisampling funktioniert genauso wie bei normalen FBOs, man sollte vorher noch via glEnable(GL_MULTISAMPLE), Multisampling aktivieren aber sonst ändert sich nichts. Einfach binden, alles zeichnen und dann wieder Framebuffer 0 binden. Das generieren der Mipmaps fällt hier allerdings weg, da wie erstens keine Textur haben und zweitens unser Framebuffer nicht einer 2er Potenzgröße entspricht (oder habt ihr einen quadratischen Bildschirm mit der Auflösung 1024x1024 ;) ).
OK. Jetzt haben wir ein schönes Bild mit glatten Kanten in dem FBO jetzt wollen wir es auch noch sehen. Das funktionert wie folgt:
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FGLFrameName); // Unseren Framebuffer also Quelle binden
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); // Den OpenGL Framebuffer als Ziel binden
glBlitFramebufferEXT(0, 0, FSize.X, FSize.Y, 0, 0, FSize.X, FSize.Y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// glBlitFramebuffer kopiert die Werte aus dem READ_FRAMEBUFFER in den DRAW_FRAMEBUFFER.
// Parameter
// Die ersten 4 Werte beschreiben das LeseRechteck im READ_FRAMEBUFFER
// Die nächsten 4 Werte beschreiben das SchreibeRechteck im DRAW_FRAMEBUFFER
// GL_COLOR_BUFFER_BIT sagt was kopiert werden soll. Um Rechenleistung zu sparen wurde hier lediglich der Colorbuffer kopiert.
// GL_DEPTH_BUFFER_BIT und GL_STENCIL_BUFFER_BIT könnten wenn nötig mitkopiert werden (dafür einfach mit einem OR verknüpfen)
// Als letztes noch GL_NEAREST da wir auf Filtering verzichten können, da die beiden Buffer die selbe größe haben.
So jetzt bekommt ihr das Ergebnis aus dem Multisampled FBO auch zu Gesicht. Wozu, werdet ihr euch jetzt fragen, muss das FBO die selbe größe wie das Fenster haben, wenn glBlitFramebuffer doch auch eine Filterfunktion bietet. Das Problem an der Sache ist, dass die Funktion häufiger mal INVALID_OPERATION ausgibt, wenn die beiden Rechtecke nicht die selbe Größe haben (genaueres auf opengl.org: [1]). Daher einfach dem Framebuffer wie oben beschrieben die selbe Größe wie dem OpenGL Fenster zuweisen und alles funktioniert.
Andere Verwendungszwecke
Falls ihr eines der folgenden Sachen mit eurem FBO machen wollt:
- Post-Processing Shader
- FBO als Textur für Objekt benutzen
- glReadPixels benutzen
Dann müsst ihr leider einen kleiner Workaround einbauen. Anstatt wie im letzten Codebeispiel als Ziel den OpenGL Framebuffer zu binden, bindet ihr stattdessen ein FBO OHNE Multisampling, dass ihr ganz normal mit einer Textur, statt einem Colorbuffer füllt. Mit dem könnt ihr dann wieder ganz normal Post-Processing und den sonstigen SchnickSchnack anstellen.