(6) Fortgeschrittene GPU- Programmierung Vorlesung Computergrafik II Stefan Müller Dank an Niklas Henrich
Beleuchtung Vertex void main() { N Eckpunkt Lichtquelle lightvec vec4 vertex = gl_modelviewmatrix * gl_vertex; vec3 normal = normalize(gl_normalmatrix * gl_normal); vec3 lightvector = normalize(gl_lightsource[0].position.xyz - vertex.xyz); vec3 reflectvector = reflect(lightvector, normal); float phi = max( dot(lightvector, normal), 0.0; float psi = max( dot(normalize(vertex.xyz), reflectvector), 0.0) gl_frontcolor = gl_frontmaterial.diffuse * gl_lightsource[0].diffuse * phi + gl_frontmaterial.specular * gl_lightsource[0].specular * pow(psi, gl_frontmaterial.shininess); gl_position = gl_projectionmatrix * vertex; } Thorsten Grosch - 2 -
Texturen Im Rahmen der GPU-Programmierung nicht nur zur Speicherung von Farbinformationen gedacht Normalen (Bump-Mapping) Tiefeninformationen (Depth-Peeling) Strahlen (Ray-Tracing) Eigener Datentyp in GLSL sampler1d, sampler2d, sampler3d (1,2 und 3D-Texturen) samplercube (Cube Maps) sampler1dshadow, sampler2dshadow (Depth Maps) Textur wird per uniform-variable an Shader übergeben Niklas Henrich - 3 -
Zugriff auf Texturen Texturzugriff in Shader an Position gl_texcoord[0].st uniform sampler2d simpletexture; void main() { vec3 value = texture2d(simpletexture, gl_texcoord[0].st).xyz; } (0,1) (1,1) t (0,0) (1,0) s Niklas Henrich - 4 -
Texturen an Shader übergeben Textur generieren und ggf. mit Daten füllen (Applikation) glgentextures(1, &texture); glbindtexture(gl_texture_2d, texture); gltexparameterf(gl_texture_2d,gl_texture_wrap_s,gl_clamp); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); Textur binden und an Shader übergeben (Applikation) glbindtexture(gl_texture_2d, texture); GLint location = glgetuniformlocation(shaderprogram, "simpletexture"); gluniform1i(location, 0); Sieht komisch aus Niklas Henrich - 5 -
Texture Units Was bedeutet die 0 im vorherigen Beispiel? Sie gibt die Texture Unit an, aus der OpenGL die Textur nehmen soll Standardmäßig ist Texture Unit 0 aktiv glbindtexture( ) bindet Textur immer an aktive Texture Unit Beispiel mit zwei Texturen glactivetexture(gl_texture0); glbindtexture(gl_texture_2d, moontex); GLint location = glgetuniformlocation(shaderprogram, "moontex "); gluniform1i(location, 0); glactivetexture(gl_texture1); glbindtexture(gl_texture_2d, startex); location = glgetuniformlocation(shaderprogram, "startex"); gluniform1i(location, 1); Niklas Henrich - 6 -
Cube Mapping Eine Form des Environment Mappings Umgebung wird durch 6 Texturen beschrieben Kamera sieht nach vorne, hinten, oben, unten, links und rechts Texturen aus Media-Ordner der DirectX SDK March 2008 Microsoft Corp. Niklas Henrich - 7 -
Environment Mapping Simulation von Objekten, die deren Umgebung reflektieren Gespiegelten oder gebrochenen Strahl als look-up in die Cube Map benutzen i n r Vertex Shader r = reflect(i, n); Fragment Shader envcolor = texturecube(envmap,r); Cube Map Niklas Henrich - 8 -
Environment Mapping - Ergebnisse r = reflect(i, n) ersetzt durch r = refract(i, n, 1.1) Niklas Henrich - 9 -
Multi-Pass Rendering Viele Algorithmen benötigen Zugriff auf die Nachbarn eines Pixels Filter in der Bildverarbeitung Post-Processing Effekte (z.b. Glow-Effekt, siehe Übung) Zugriff auf benachbarte Pixel des Framebuffers innerhalb eines Fragment-Programs nicht möglich Lösung: Multi-Pass Rendering Ergebnis eines Rendering-Schrittes als Eingabe des folgenden Schrittes verwenden Das endgültige Bild entsteht erst nach mehreren Durchläufen Niklas Henrich - 10 -
Multi-Pass Rendering Szene in Textur rendern Textur an Shader-Program übergeben Viewport auf Größe der Textur setzen Bildschirmfüllendes Rechteck zeichnen glbegin(gl_quads); gltexcoord2f(0,0); glvertex2f(-1,-1); gltexcoord2f(1,0); glvertex2f( 1,-1); glend() (-1,1) (0,1) (1,1) (1,1) (-1,-1) (0,0) (1,0) (1,-1) Für jedes Texel der Textur wird genau ein Fragment generiert Zugriff auf Nachbarpixel nun über Texturzugriff Niklas Henrich - 11 -
Full-Screen Quad - Details gldisable(gl_depth_test); glpushattrib(gl_viewport_bit); glviewport(0, 0, width, height); glmatrixmode(gl_modelview); glpushmatrix(); glloadidentity(); glmatrixmode(gl_projection); glpushmatrix(); glloadidentity(); glbegin(gl_quads); gltexcoord2f(0,0); glvertex3f(-1, -1, 0.0f); gltexcoord2f(1,0); glvertex3f(1, -1, 0.0f); gltexcoord2f(1,1); glvertex3f(1, 1, 0.0f); gltexcoord2f(0,1);glvertex3f(-1, 1, 0.0f); glend(); glpopmatrix(); glmatrixmode(gl_modelview); glpopmatrix(); glenable(gl_depth_test); glpopattrib(); Niklas Henrich - 12 -
Mehr Details Textur-Zugriff Die aktuelle Koordinate eines Fragments in Viewport- Koordinaten ([0,Fensterbreite] x [0,Fensterhöhe]) erhält man über gl_fragcoord.xy Bei neueren Grafikkarten können Texel auch direkt mit Integer-Koordinaten im Bereich [0,Texturebreite] x [0, Texturhöhe] angesprochen werden #extension GL_EXT_gpu_shader4 : enable texelfetch2d(texture,ivec2(gl_fragcoord.xy),0); Niklas Henrich - 13 -
Nachbarpixel Wie bestimmt man die Texturkoordinate z.b. des rechten benachbarten Pixels? Falls man texelfetch2d benutzen kann vec4 r = texelfetch2d(texture, ivec2(gl_fragcoord.xy + vec2(1,0)),0); Falls nicht, braucht man die Breite der Textur, z.b. über eine Uniform-Variable uniform float texturewidth; float texturedelta = 1.0 / texturewidth; vec4 r = texture2d(texture, gl_texcoord[0].st + vec2(texturedelta,0)); Niklas Henrich - 14 -
Neulich beim CV-Studenten Brandneue Grafikkarte gekauft und eingebaut GLUT Demoprogramm von den CG-Seiten runtergeladen Programm so geändert, dass genau ein Dreieck gezeichnet wird Frames-per-second (fps) gemessen Niklas Henrich - 15 -
Umfrage FPS für ein Dreieck? A B C 100-1000 fps 1000 3000 fps 60 fps Niklas Henrich - 16 -
Der Schock! VSync: Ein ca. 60 fps Vertikale Synchronisation (VSync) Treiber wartet, bis der Monitor das Bild komplett aufgebaut hat Für mehr Geschwindigkeit VSync ausschalten Windows (GLEW) wglswapintervalext(0) VSync: Aus Niklas Henrich - 17 -
Geometry Shader Neue programmierbare Einheit der Grafikpipeline Direkt nach Vertex Shader (vor Clipping etc.) Mit DirectX 10 eingeführt (Shader Model 4.0) Funktionalität in OpenGL über Extension GL_EXT_geometry_shader4 verfügbar Dynamisch neue Geometrie erzeugen Geometrie löschen Niklas Henrich - 18 -
Geometry Shader - Eingabe Arbeitet immer auf gesamten Eingabeprimitiv Mögliche Eingabeprimitive Punkt Linie Dreieck Shader hat Zugriff auf Alle Punkte des Eingabeprimitives Alle direkt benachbarten Punkte Zugriff möglich q2 p2 q1 p1 Benachbartes Dreieck p0 q0 Niklas Henrich - 19 -
Geometry Shader - Ausgabe Ausgabe (beliebig* viele) Punkte Linien (line strips) Dreiecke (triangle strips) Es ist auch möglich, keine Geometrie auszugeben Maximale Anzahl der ausgegebenen Primitive pro Shaderaufruf muss zur Compile-Zeit bekannt sein Je größer diese Zahl, umso stärker sinkt die Performance Ein- und Ausgabetyp können verschieden sein Dreieck als Eingabe Linienzug als Ausgabe Wireframe *beliebig bedeutet bis zum Hardwarelimit Niklas Henrich - 20 -
Geometry Shader neue Befehle EmitVertex() Eckpunkt dem aktuellen Ausgabeprimitiv (z.b. Dreieck) hinzufügen gl_position = vec4(1.0, 0.5, 0.3, 1.0); EmitVertex(); EndPrimitive() Aktuelles Ausgabeprimitiv abschließen Neues Ausgabeprimitiv vom gleichen Typ wird begonnen Ähnlich wie OpenGL-Befehlsreihenfolge: glend(); glbegin( ); Niklas Henrich - 21 -
Beispiel aus Eins mach Drei Geometry Shader: Aus Geometry Shader: Ein Niklas Henrich - 22 -
Beispiel aus Eins mach Drei #version 120 #extension GL_EXT_geometry_shader4 : enable void main() { // 1. Dreieck erzeugen und verschieben for(int i = 0; i < gl_verticesin; i++){ gl_frontcolor = vec4(0,1,0,1); gl_position = vec4(gl_positionin[i].x * 0.5, (gl_positionin[i].y * 0.5) + 0.5, 0, 1); EmitVertex(); } EndPrimitive(); // 2. Dreieck erzeugen und verschieben // 3. Dreieck erzeugen und verschieben. Anzahl der Eckpunkte des Eingabeprimitives Array von Eckpunkten des Eingabeprimitives Niklas Henrich - 23 -
Geometry Shader Ausgabelimit Ein Geometry Shader kann momentan maximal ca. 256 Eckpunkte ausgeben Soll mehr Geometrie erzeugt werden, muss dies in mehreren Schritten geschehen Folgende Funktionalität wird dazu benötigt: Bereits erzeugte Geometrie muss zwischengespeichert werden können Erzeugte Geometrie muss wieder abgerufen werden können Diese Funktionalität wird durch das sogenannte Transform Feedback bereitgestellt Niklas Henrich - 24 -
Transform Feedback Ermöglicht es, Geometrie in Buffer-Objekte zu rendern Fragment Shader wird ausgeschaltet Geometrie kann somit zwischengespeichert werden Inhalt des Buffers kann zu einem späteren Zeitpunkt erneut gerendert werden Niklas Henrich - 25 -
Transform Feedback Vertex Shader und Geometry Shader können Geometrie beliebig verändern Neue Geometrie hinzufügen (GS) Vorhandene Geometrie verschieben (VS + GS) Geometrie löschen (GS) Nachdem gewünschte Geometrie erzeugt wurde Fragment Shader einschalten Geometrie rendern Niklas Henrich - 26 -
Transform Feedback Demo http://developer.download.nvidia.com/sdk/10.5/opengl/samples.html#transform_feedback_fractal Niklas Henrich - 27 -
Framebuffer Objects (FBOs) Ermöglichen es direkt in Texturen zu rendern Multiple Render Targets (MRT) In einem Schritt in verschiedene Texturen rendern Ohne MRTs müsste die Szene für jede Information (Position, Normale, etc.) neu gerendert werden Render Target 1 (Textur) Fragment Program Position Render Target 2 (Textur) Render Target 3 (Textur) Niklas Henrich - 28 -
FBO Attachments Ein FBO bietet Platz für eine Reihe von Attachments, die an das FBO geknüpft werden können Color Attachment (= Render Target) Texturen In diese wird gerendert Renderbuffer Objects Depth Attachment Falls OpenGL beim Rendern in das FBO einen Tiefentest durchführen soll Stencil Attachment Falls OpenGL beim Rendern in das FBO einen Stenciltest durchführen soll Niklas Henrich - 29 -
FBO Attachments = wird gebunden an Framebuffer Object Color Color Color Color Attachment Textur Color Color Color Color Color Attachment Depth Attachment Stencil Attachment Renderbuffer Object Depth Buffer Stencil Buffer Niklas Henrich - 30 -
Framebuffer Object erstellen Framebuffer Object erzeugen und aktivieren GLuint fbo; glgenframebuffersext(1, &fbo); glbindframebufferext(gl_framebuffer_ext, fbo); Leere Texturen erstellen (wie gehabt, siehe CG1) glgentextures(1, &textureposition); glbindtexture(gl_texture_2d, textureposition); gltexparameteri(gl_texture_2d,gl_texture_mag_filter, GL_NEAREST); gltexparameteri(gl_texture_2d,gl_texture_min_filter, GL_NEAREST); glteximage2d(gl_texture_2d, 0, GL_RGBA32F_ARB, width, height, 0, GL_RGBA, GL_FLOAT, NULL); // Textur texturenormal erzeugen // Niklas Henrich - 31 -
Framebuffer Object erstellen Textur textureposition an ersten Color Attachment Point hängen glframebuffertexture2dext( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, // Erster AttachmentPoint GL_TEXTURE_2D, // 2D-Textur soll angehangen werden textureposition, //.. und zwar diese 0); // MipMap Level ist 0 Textur texturenormal an zweiten Attachment Point hängen glframebuffertexture2dext( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, // Erster AttachmentPoint GL_TEXTURE_2D, // 2D-Textur soll angehangen werden texturenormal, //.. und zwar diese 0); // MipMap Level ist 0 Niklas Henrich - 32 -
In Framebuffer Object rendern OpenGL mitteilen, dass in beide Color Attachments gerendert werden soll glbindframebufferext(gl_framebuffer_ext, fbo); GLenum buffers[2]; buffers[0] = GL_COLOR_ATTACHMENT0_EXT; buffers[1] = GL_COLOR_ATTACHMENT1_EXT; gldrawbuffers(2, buffers); Der Framebuffer wurde nun durch das FBO ersetzt Anschließend Szene rendern Eine glclear( ) löscht nun die Inhalte des aktuell gebundenen FBOs! Niklas Henrich - 33 -
FBO Fragment Shader Im Pixel-Shader kann das Render Target mit gl_fragdata[n] ausgewählt werden Position geht in Color Attachment 0 Normale geht in Color Attachment 1 varying vec4 position; varying vec3 normal; void main() { gl_fragdata[0] = position; gl_fragdata[1] = vec4(normal,0); } Niklas Henrich - 34 -
Anmerkung: Das vorhergehende Beispiel verwendet keinen Tiefenpuffer und somit wird beim Zeichnen auch kein Tiefentest durchgeführt Folgender Code generiert einen Tiefenpuffer und hängt ihn an das FBO (siehe http://www.gamedev.net/reference/articles/article2331.asp) GLuint depthbuffer; glbindframebufferext(gl_framebuffer_ext, fbo); GLuint depthbuffer; glgenrenderbuffersext(1, &depthbuffer); glbindrenderbufferext(gl_renderbuffer_ext, depthbuffer); glrenderbufferstorageext(gl_renderbuffer_ext, GL_DEPTH_COMPONENT, windowwidth, windowheight); glframebufferrenderbufferext(gl_framebuffer_ext, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer); Niklas Henrich - 35 -
Alles OK? Gebundenes FBO prüfen void checkfbo() { // See http://www.opengl.org/registry/specs/ext/framebuffer_object.txt // for more information GLenum status; status = glcheckframebufferstatusext(gl_framebuffer_ext); switch(status) { case GL_FRAMEBUFFER_COMPLETE_EXT: // Everything's OK break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: cout << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: cout << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: cout << "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: cout << "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: cout << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: cout << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT\n"; break; case GL_FRAMEBUFFER_UNSUPPORTED_EXT: cout << "GL_FRAMEBUFFER_UNSUPPORTED_EXT\n"; break; default: cout << "Unknown ERROR\n"; } } Niklas Henrich - 36 -
Beispielhafter Ablauf: // Framebuffer binden und überprüfen glbindframebufferext(gl_framebuffer_ext, fbo); checkfbo(); Demo Drehender Teapot // FBO Shader verwenden gluseprogram(fboshader); // Inhalt von FBO löschen glclear(gl_color_buffer_bit GL_DEPTH_BUFFER_BIT); // Geometrie rendern draw(); // Nicht mehr in das FBO rendern, sondern wieder in den normalen Framebuffer FramebufferglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Framebuffer löschen glclear(gl_color_buffer_bit GL_DEPTH_BUFFER_BIT); // Display-Shader verwenden gluseprogram(displayshader); // Texturen an Shader weitergeben GLint htexture = glgetuniformlocation(displayshader,"texture"); glbindtexture(gl_texture_2d,textureposition); gluniform1i(htexture,0); // Display-Shader aufrufen um FBO Inhalt darzustellen DrawFullScreenQuad(); Niklas Henrich - 37 -
Deferred Shading Ein sehr unscharfer Begriff Im allgemeinen Fall versteht man darunter* Szene in verschiedene Texturen rendern In einem zweiten Schritt auf / mit dem Inhalt der Texturen arbeiten Praktisch jede moderne GPU-Anwendung benutzt irgendeine Form des deferred rendering / shading Im Folgenden wird das Szenario (Beleuchtung von Objekten in einer Szene) vorgestellt, für welches der Begriff am häufigsten verwendet wird *meiner Meinung nach! Niklas Henrich - 38 -
Objekte in Szene beleuchten (ohne Deferred Shading) Eine Möglichkeit Für jede Lichtquelle in der Szene Lichtquelle aktivieren Für jedes Objekt in der Szene Objekt zeichnen Jedes Objekt muss somit #Lichtquellen #Objekte mal gezeichnet (und beleuchtet) werden Manche Objekte werden beleuchtet, obwohl sie vielleicht in einem späteren Schritt von einem anderen Objekt verdeckt werden Niklas Henrich - 39 -
Objekte in Szene beleuchten (ohne Deferred Shading) Eine andere Möglichkeit Für jedes Objekt in der Szene Für jede Lichtquelle in der Szene Lichtquelle aktivieren und Objekt zeichnen Jedes Objekt muss wieder #Lichtquellen #Objekte mal gezeichnet und beleuchtet werden Warum nicht mehrere Lichtquellen gleichzeitig anwenden? Funktioniert bei wenigen Lichtquellen Manche Anwendungen (Spiele) können über hunderte von Lichtquellen verfügen, die gleichzeitig aktiv sind Zu viele Argumente an einen Shader Niklas Henrich - 40 -
Deferred Shading Für jedes Objekt in der Szene Rendere Weltkoordinate, Normale, Farbe etc. in MRTs Für jede Lichtquelle in der Szene Beleuchtung als Postprocess auf den erstellten Texturen Vorteile Jedes Objekt wird genau einmal gezeichnet Es werden nur die tatsächlich sichtbaren Pixel beleuchtet Aufwand: #Objekte + #Lichtquellen anstatt #Lichtquellen #Objekte Niklas Henrich - 41 -
Deferred Shading Objekte rendern Binde ein FBO mit mehreren Texturen (Color Attachments) Schreibe z.b. die folgenden Eigenschaften in die Texturen Texturen werden auch als G-buffer bezeichnet Tiefe Normale Farbe Phong Exponent Niklas Henrich - 42 -
Deferred Shading Weltkoordinate? Falls genügend Platz vorhanden ist, kann die Weltkoordinate in einer eigenen Textur gespeichert werden Ansonsten kann die Position (sprich: Weltkoordinate) aus dem Tiefenwert sowie der Position des Pixels im Framebuffer und der Kamera errechnet werden Trade-off: Speicherverbrauch vs. Rechenlast Anmerkung Normalen: Falls die Normalen normalisiert sind, müssen nur zwei Werte gespeichert werden Der dritte kann aus diesen errechnet werden Das Vorzeichen des dritten Wertes muss bei einem anderen Wert mit abgespeichert werden Z.B. Tiefenwert (ist immer positiv) Niklas Henrich - 43 -
Deferred Shading Beleuchtung Approximiere Lichtkegel durch konvexes Polygon Aktiviere Back-Face Culling Rendere konvexes Polygon Für jedes Pixel, das durch den Lichtkegel überdeckt wird, wird der Lichtquellen- Pixel Shader aktiviert Pixel Shader liest Daten aus G-Buffer aus (Position, Normale, Material etc.) und führt Lichtberechnungen für aktuelles Pixel durch Anmerkung: Wenn kein Back-Face Culling aktiviert ist, wird jedes Pixel doppelt beleuchtet. Der Pixel Shader würde einmal für die Vorderseite und einmal für die Rückseite des Polygons aktiviert. Niklas Henrich - 44 -
Deferred Shading Für die roten Pixel wird der Beleuchtungs -Pixel Shader aktiviert Niklas Henrich - 45 -
Deferred Shading Pixel Shader liest an seiner Position die Informationen aus dem G-Buffer aus und führt Beleuchtungsberechnung durch Position liest Daten Normale Material Niklas Henrich - 46 -
Deferred Shading Probleme Speicherverbrauch des G-Buffers Hohe Bandbreitenausleistung (viele Texturzugriffe) Kein Antialiasing / Multisampling Wird nicht auf den Color Attachments durchgeführt Transparenzen Pro Pixel wird nur das vorderste Objekt gespeichert Niklas Henrich - 47 -
Deferred Shading Demo http://www.humus.name/index.php?page=3d&id=74 Niklas Henrich - 48 -
Links Vertex Arrays http://www.songho.ca/opengl/gl_vertexarray.html VBO http://www.ozone3d.net/tutorials/opengl_vbo.php http://www.devmaster.net/forums/showthread.php?t=360 Geometry Shader http://developer.download.nvidia.com/sdk/10.5/opengl/samples.html#simple_geometry_program http://cirl.missouri.edu/gpu/glsl_lessons/glsl_geometry_shader/index.html http://graphics.cs.uiuc.edu/agora.php/geometry+shader+hello+world http://cvit.iiit.ac.in/index.php?page=resources http://appsrv.cse.cuhk.edu.hk/~ymxie/geometry_shader/ Transform Feedback http://cvit.iiit.ac.in/index.php?page=resources http://developer.download.nvidia.com/sdk/10/opengl/samples.html#transform_feedback_fractal FBO http://www.gamedev.net/reference/articles/article2331.asp http://www.gamedev.net/reference/articles/article2333.asp Niklas Henrich - 49 -