Docstoc

Vertex-Buffer-Objects und Shader

Document Sample
Vertex-Buffer-Objects und Shader Powered By Docstoc
					                          VBOs und Shader




Vertex Buffer Objects
         &
       Shader



Computergraphik 2-Übung
     im WS 09/10




                                            1
                                                              VBOs und Shader




Vertex-Buffer-Objects

Motivation von VBOs sind Nachteile von Vertex-Arrays und Displaylisten:

Vertex-Arrays:
• Speicherbereich auf dem Client
• muß bei jedem Zeichnen auf den Grafikspeicher übertragen werden -> Bandbreite!


Displaylisten:
• Speicherbereich ist auf dem Grafikspeicher (schnell)
• Aber: dynamische Änderungen der Daten sind nicht möglich!




                                                                                2
                                                                          VBOs und Shader


Besser: Vertex-Buffer-Objects

Praktisch dasselbe wie Vertex-Arrays, jedoch liegen die Vertexdaten auf dem Server
(auf der Grafikkarte).

Wenige neue Befehle:
- glGenBuffers – Erzeugen von Namen für Buffer-Objekte (ähnlich wie bei glGenTextures())
- glBufferData – Speicherbereich auf Grafikkarte allozieren und ggf. mit Werten initialisieren
                 (ähnlich wie glTexImage[1/2/3]D())
- glDeleteBuffers – Buffer löschen (Speicherbereich freigeben)
                    (ähnlich wie glDeleteTextures())
- glBindBuffer – Buffer „binden“ (setzt Status „aktiver Buffer“ für weitere Befehle)
                 (ähnlich wie glBindTexture())


VBOs, oder generell „Buffer Objects“, sind näher an der Hardware, da sie direkt das
Manipulieren des Speichers der Grafikkarte erlauben.
(Info: Es gibt noch weitere „Interpretationen“ dieses Speicherbereichs als *nur* zum
Zeichnen von Vertexdaten, z.B.: „Texture Buffer Objects“, „Pixel Buffer Objects“)



                                                                                                 3
                                                              VBOs und Shader


Beispiel: Allozieren eines Speicherbereichs


GLuint vboId;
glGenBuffersARB(1, &vboId);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, 2*sizeof(GLfloat)*1024,
                NULL, GL_STATIC_DRAW_ARB);



Obiger Code erzeugt ein neues Buffer-Objekt für 1024 2D-Float-Vektoren auf dem
Grafikspeicher.
Der Speicherbereich ist nicht mit Werten vorbelegt.
Speicherbereich ist bisher uninterpretierte Menge von Bytes.
Interpretierung findet später beim „Zeichnen“ der Daten durch das VBO statt.




                                                                                 4
                                                                  VBOs und Shader


Wie füllt man ein VBO mit Daten?


Überhaupt nicht so, wie bei Vertex Arrays…
1. Holen einer Referenz auf den Speicherbereich in der Grafikkarte
2. Direkt auf diesem Speicherbereich (Typ: void*) Daten manipulieren
3. Speicherbereich wieder zurückgeben


Neue Befehle:

- glMapBuffer – Speicherbereich auf der Grafikkarte als (void*) holen
                (anschließend kann darauf gelesen und geschrieben werden)
- glUnmapBuffer – Danach gemappten Buffer zurückgeben (Änderungen werden committed)




                                                                                      5
                                                               VBOs und Shader


Beispiel (Füllen eines VBOs mit Daten):

GLuint vboId;
int i;
GLfloat* gpuData;
#define PI 3.0f
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
gpuData = (GLfloat*) glMapBufferARB(GL_ARRAY_BUFFER_ARB,
                                    GL_WRITE_ONLY);
for (i = 0; i < 1024; i++) {
  gpuData[2*i+0] = (GLfloat)i / 1023.0f;
  gpuData[2*i+1] = (GLfloat)sin((float)i/1023.0f*2.0f*PI);
}
glUnmapBuffer(GL_ARRAY_BUFFER_ARB);

Obiger Code füllt den vorher erzeugten Buffer „vboId“ mit einer Sinus-Kurve.
Es werden jeweils Paare (x, y) geschrieben



                                                                                 6
                                                                       VBOs und Shader


Wie zeichnet man ein VBO?


(Fast) genauso wie bei Vertex Arrays


Bekannter Befehl:

- glVertexPointer(size, type, stride, offset)
                                            (wie bei Vertex-Arrays, nur gibt jetzt offset den
                                            Byte-Offset im gerade gebundenen Buffer Object an!)

Hier findet jetzt Interpretation der Daten im Vertex-Buffer-Object statt!
Es handelt sich um Daten des Typs „type“ und jeder Vertex besteht aus „size“
Komponenten.




                                                                                            7
                                                             VBOs und Shader


Beispiel (Zeichnen mit VBOs):


GLuint vboId;
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_LINE_STRIP, 0, 1024);


Obiger Code zeichnet alle 1024 Vertizes im vorher erzeugten und gefüllten VBO.
Der letzte Parameter von glVertexPointer(…) gibt dabei an, daß die zu zeichnenden
Vertizes gleich am Anfang des VBO stehen.
Das Zeichnen mit Indexbuffer ist ähnlich. Es muss ein weiteres Buffer-Object
erzeugt werden, das die Indexdaten enthält. Statt GL_ARRAY_BUFFER_ARB als
Binding zu benutzen, muß GL_ELEMENT_ARRAY_BUFFER_ARB verwendet
werden.




                                                                                 8
                                                              VBOs und Shader


Shader – am Beispiel der OpenGL Shading Language (GLSL)


Was sind Shader?

• „kleine“ Programme, die auf der Grafikkarte laufen
• generell Einteilung in Vertex-, Fragment- und neuerdings auch Geometrie-Shader
  (letztere werden in der Übung nicht benötigt)

Motivation:

• für moderne und flexibel gestaltbare Grafikeffekte ohne Performanceeinbrüche ist

 die bisher verwendete „Fixed-Function-Pipeline“ nicht ausreichend
• Beispiele: eigenes Beleuchtungsmodell (anisotropes Metall), „Bumpmapping“,
  effiziente dynamische Oberflächen (Wasserwellen) etc.
• seit OpenGL 2.0 (verabschiedet am 7.9.2004) ist die OpenGL Shading Language
  (kurz: GLSL) Bestandteil von OpenGL




                                                                                 9
                                                            VBOs und Shader


Neue OpenGL-Befehle:


• leider viele!
• sind aber nicht Bestandteil dieser Übung
• dienen nur der Verwaltung von Shader-Objekten und -Programmen
• Funktionalität zum Laden eines Shaders aus einer Datei, sowie zum Setzen von
  Variablen im Shader wird als „Framework“ bereitgestellt




                                                                                 10
                                                            VBOs und Shader


Shader machen zwei Schritte der Grafikpipeline flexibel:

• „Vertex Operation“ – Was passiert mit den ankommenden Vertizes (üblicherweise
  Transformation von Model-Space in Clipping-Space über OpenGL-Matrixstack)
• „Fragment Operation“ – Festlegen der Farbe (und des Tiefenwertes) jedes
  generierten Fragmentes (Pixels) (üblicherweise OpenGL-Beleuchtungsmodell)




                                                                              11
                                                             VBOs und Shader


Vorteile eines Vertex Shaders:

• völlige Freiheit, wie Vertizes verarbeitet werden sollen
• Beispiel (einfaches Wellenmodell):
     • Anwendung zeichnet uniformes Grid auf x-z-Ebene
     • Shader verschiebt y-Koordinate auf Basis einer Kombination aus Sinus-
       Funktionen
     • Animation über die „Zeit“
    • Vorteil: dynamische Vertexdaten müssen nicht immer neu in den
 Grafikspeicher geladen werden


Vorteile eines Fragment Shaders:

• Implementation eines eigenen Beleuchtungsmodells
• Samplen und Mischen beliebiger Texturen
• pixelgenaue Beleuchtungsberechnung!



                                                                               12
                                                               VBOs und Shader




Einschränkungen eines Vertex Shaders:

• keine Informationen über andere Vertizes als den aktuell bearbeiteten Vertex
• kein Erzeugen oder Vernichten eines Vertex möglich
• kein Ändern der Konnektivität (GL_TRIANGLES, GL_LINES, …)


Einschränkungen eines Fragment Shaders:
• keine Information über andere Fragmente als das aktuell bearbeitete Fragment
• kein „Verschieben“ eines Fragmentes möglich (Fragmentposition ist fest!)
• kein Erzeugen neuer Fragmente möglich (ergäbe auch keinen Sinn, da die
  Bildschirmauflösung sich ja nicht stellenweise ändern kann), das Löschen
  eines Fragmentes hingegen ist möglich! (Schlüsselwort: „discard“)




                                                                                 13
                                                                  VBOs und Shader


Beispiel eines einfachen „Wellenshaders“ (Vertex Shader):

uniform float time;
varying vec3 pos;
void main(void) {
  pos = gl_Vertex.xzy;
  pos.y = sin(time + pos.x);
  gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1.0);
}
• Eingabe dieses Shaders ist der aktuell gezeichnete Vertex, sowie eine globale
  Variablen
• Position des Vertex ist als „globale“ Variable „gl_Vertex“ sichtbar
• Datentyp „vec3“ ist ein 3-dimensionaler float-Vektor und besitzt x, y und z-
  Koordinaten, die über den Punkt-Operator angesprochen werden können
• Verschieben der y-Koordinate des Vertex mittels Sinus-Funktion
• „globale“ Variable „gl_Position“ ist Ausgabevariable für transformierte
  Vertexposition (bereits im Clipping-Space)
• „gl_ModelViewProjectionMatrix“ ist glob. Variable und enthält aktuelle P * MV-Matrix
• „varying“ Variablen werden zum Informationsaustausch zwischen Vertex- und
  Fragmentshader genutzt, die Werte für die Fragmente werden dabei interpoliert

                                                                                    14
                                                                VBOs und Shader


Beispiel eines einfachen „Wellenshaders“ (Fragment Shader):
uniform vec3 lightDir;
uniform vec4 waterColor;
varying vec3 pos;
void main(void) {
  vec3 normal = normalize(cross(dFdx(pos), dFdy(pos)));
  float diffuse = max(0.0, dot(lightDir, normal));
  gl_FragColor = waterColor * diffuse;
}
• Eingabe dieses Shaders sind interpolierte Werte vom Vertexshader, sowie globale
  Variablen
• außerhalb des Shaders wurde die Lichtrichtung, sowie die Wasserfarbe definiert
• dFdx/dFdy berechnen den Differenzenquotient aus dem aktuellen und dem
  Nachbarfragment in positiver x- bzw. y-Richtung (in Fensterkoordinaten)
• in diffuse wird die Berechnung des diffusen Lichtanteil für jeden Pixel auf dem
  Wasser geschrieben
• „gl_FragColor“ ist Ausgabewert für Fragmentfarbe



                                                                                    15
                                                                VBOs und Shader


Lesen einer Textur im Fragment Shader:
Vertex Shader:
varying vec2 texCoord;
void main(void) {
  vec3 pos = gl_Vertex.xyz;
  texCoord = pos.xy;
  gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1.0);
}
Fragment Shader:
uniform sampler2D textur;
varying vec2 texCoord;
void main(void) {
  vec4 texel = texture2D(textur, texCoord);
  gl_FragColor = texel;
}
• projiziert eine Textur linear in Richtung der x-y-Ebene mit Modellkoordinaten
• „sampler2D“ ist die Textureinheit, an die die Textur gebunden ist
• „texture2D“ sampled einen gegebenen Sampler an der gegebenen 2D-Position

                                                                                  16
                                                               VBOs und Shader


Lesen einer Textur im Vertex Shader:

• seit Shader Model 3.0 (GeForce 6-Serie, Intel GMA 950)
• Vertex Shader kann Texturen als zusätzliche Eingabedaten (interpretiert als Array)
  benutzen

Vertex Shader:
uniform sampler1D array;
void main(void) {
  vec3 pos = gl_Vertex.xyz;
  pos.y = texture1DLod(array, pos.x, 0.0);
  gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1.0);
}

• „texture[1/2/3]DLod“ liest aus dem angegebenen Sampler (1. Parameter) an der
  gegebenen Texturkoordinate (2. Parameter) im angegebenen Mipmap-Level einen
  Texel (ohne den Zusatz „Lod“ würde automatisches Mipmapping stattfinden! Nicht
  erlaubt im Vertexshader!)
• Hier wird der R-Kanal der Textur benutzt (z.B. bei GL_LUMINANCE-Texturformat)


                                                                                 17
                                                                VBOs und Shader


Lesen einer Textur im Vertex Shader:

• einige Grafikkarten können im Vertex Shader nur Texturen im 32-bit Floating-Point
  Format lesen, z.B.: GL_LUMINANCE32F_ARB
• dieses Format sollte für beliebige Daten (keine Farbwerte) genommen werden!

Erzeugen solch einer Textur in C (1D-Array als Textur):
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_1D, texture);
glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE32F_ARB, width, 0,
             GL_LUMINANCE, GL_FLOAT, floatArray);

• obiger Code reserviert und füllt einen Speicherbereich für eine 1D-Textur.
• Textur besitzt gegebene Breite „width“
• enthält 32-bit Floating-Point Werte
• ein Kanal (LUMINANCE = R-Kanal), nicht 3 oder 4, wie bei RGB oder RGBA
• eine solche Textur muss die „Nearest neighbour“-Filterung verwenden!

                                                                                  18
                                                                 VBOs und Shader


Normalmaps, Bumpmapping und Tangentialräume:

• Normalmap: Textur, in der Normalen einer Oberfläche gespeichert sind
• Normalmap ist höher aufgelöst als die Vertizes eines Modells
• Normalen werden für Beleuchtungsberechnungen benutzt
• XYZ-Koordinaten der Normalen in RGB-Kanälen der Textur kodiert
• Normalen in der Normalmap liegen im „Tangentialraum“ (engl.: „Tangent space“)
• Tangentialraum ist ein Vektorraum, in dem die z-Achse orthogonal zur
  Oberfläche des Modells steht (z-Achse = Oberflächennormale)



                                             Die Normale ist das Kreuzprodukt aus
                                             Tangente und Binormale/Bitangente:
                                                            
                                                          n= t×b



                                                                                   19
                                                              VBOs und Shader


Transformation vom Tangent space in Model space:

• Transformation von Tangent space in Model space erforderlich
• Basistransformation als Matrix, die die Vektoren im Tangent space in den Model
  space transformiert:

                             tx     bx    nx 
                                             
                        T =  ty     by    ny 
                            t       bz    nz 
                             z               
• Spaltenvektoren sind „Tangente“, „Bitangente“ und „Normale“ an dem Objektpunkt,
  für den eine Transformation durchgeführt werden soll.
• Multiplikation des Vektors im Tangentialraum (der „Bump“-Normale in der
  Normalmap) mit der Transformationsmatrix ergibt Vektor im „Model space“:


                                v m = T ⋅ vt

                                                                                   20
                                                             VBOs und Shader


Ermittlung der Tangente, Bitangente und Normale an einem Punkt:

• Eingabewerte durch Anwendung selbst (Teil der Daten im Vertex-Array)
• Ist bei dynamischen Modellen schwierig (Berechnung der Ableitungsfunktion)
• Kann im Shader dynamisch berechnet werden (Funktionen: „dFdx“, „dFdy“)




                                                                               21
                                                             VBOs und Shader


Multitexturing:

• mehrere Texturen auf ein Primitiv auftragen, ohne es mehrmals zu zeichnen.




                           +                  =




                                                                               22
                                                               VBOs und Shader


Beispiel in OpenGL:


GLuint texture1;
GLuint texture2;
// Erste Textureinheit aktivieren
glActiveTextureARB(GL_TEXTURE0_ARB);
// Erste Textur binden
glBindTexture(GL_TEXTURE_2D, texture1);
// Zweite Textureinheit aktivieren
glActiveTextureARB(GL_TEXTURE1_ARB);
// Zweite Textur binden
glBindTexture(GL_TEXTURE_2D, texture2);

• Binden der vorhandenen Texturen an unterschiedliche Textureinheiten
• ohne den Aufruf von „glActiveTextureARB()“ ist die erste Textureinheit
  (GL_TEXTURE0_ARB) standardmäßig aktiviert




                                                                                 23
                                                VBOs und Shader


Beispiel in OpenGL:


Aktivierung für den Shader:
glUniform1iARB(glGetUniformLocationARB(progId, „texture1“),
               0);
glUniform1iARB(glGetUniformLocationARB(progId, „texture2“),
               1);


Fragment Shader:
uniform sampler2D texture1;
uniform sampler2D texture2;
varying vec2 coord;
void main(void) {
  gl_FragColor = texture2D(texture1, coord) *
                  texture2D(texture2, coord);
}



                                                                  24

				
DOCUMENT INFO
Shared By:
Categories:
Stats:
views:218
posted:9/20/2010
language:German
pages:24