Kapitel 4: OpenGL
OpenGL als API und State-Machine: der moderne Datenpfad von der Datenerstellung (interleaved Array) ueber VBO und VAO (Attributzeiger, stride/offset in Bytes) und die Shader-Pipeline bis zur Bilderzeugung mit glDrawArrays bzw. indiziert per IBO und glDrawElements.
Ueberblick
Kapitel 4 zeigt, wie man die Daten aus Kapitel 3 (Farben + Primitive) mit OpenGL auf die GPU bringt und rendert. Der rote Faden ist der Datenfluss von der CPU zur GPU:
Datenerstellung (interleaved Array) -> VBO -> VAO (Attributzeiger) -> Shader-Pipeline -> Bilderzeugung (glDrawArrays) und, als Erweiterung, die Indizierung ueber ein IBO mit glDrawElements.
- OpenGL ist eine Spezifikation einer 3D-Grafik-API; die Implementierung steckt im Grafikkartentreiber.
- OpenGL arbeitet als State-Machine (Zustandsautomat) und ist explizit: was nicht aktiviert wurde, bleibt aus.
- Die CPU erstellt/manipuliert Daten, die GPU verarbeitet und rendert; dazwischen liegt der Upload.
- Ein VBO haelt die rohen Geometriedaten auf der GPU; ein VAO beschreibt deren Interpretation.
- Ein IBO speichert Indizes und ermoeglicht die Wiederverwendung von Vertices.
Quelle laut Extraktion: VC_Grafik_Kapitel_3_und_4_ss2026.pdf, Hochschule Darmstadt, Visual Computing SS2026 (Hergenroether / Froemmer / Meyer).
Was ist OpenGL?
OpenGL (Open Graphics Library) ist die Spezifikation einer API fuer 3D-Grafik, die rund 250 Befehle standardisiert.
- Die Implementierung steckt im Grafikkartentreiber (driver); Befehle laufen auf der GPU oder als Fallback auf der CPU.
- OpenGL ist ein Renderingsystem, keine Modellierungssoftware - komplexe Modelle muessen aus einfachen Primitiven aufgebaut werden.
State-Machine und Explizitheit
- State-Machine (Zustandsautomat): OpenGL merkt sich einen internen Zustand. Funktionen aendern diesen Zustand oder nutzen ihn zur Darstellung. Einmal gesetzt, bleibt ein Zustand aktiv, bis er umgeschaltet wird (z.B. das aktuell gebundene VBO/VAO oder die aktuelle Farbe).
- Explizitheit (explicit): Was nicht explizit aktiviert wurde, bleibt aus. Beispiel: Eine Transparenz zu setzen bringt nichts, solange die Transparenzberechnung nicht explizit eingeschaltet ist.
Hilfsbibliotheken
- GLFW (Graphics Library Framework): verwendete plattformuebergreifende Open-Source-Bibliothek fuer Fenster, Kontext und Input.
- GLEW und GLM: Erweiterungen fuer Zusatzfunktionen.
- GLUT (OpenGL Utility Toolkit) bzw. FreeGLUT: die genannte Alternative.
Legacy-Code (OpenGL 1.x)
Der alte immediate mode verwendet glBegin(...) ... glEnd() mit glColor4f und glVertex3f. Einfach, aber ineffizient, da jeder Vertex einzeln pro Frame zur GPU geschickt wird. Heute ersetzt durch VBO/VAO.
glBegin( GL_POLYGON );
// State-Machine: wird nur der erste Farbwert angegeben,
// haben alle folgenden Eckpunkte denselben Farbwert
glColor4f( 1., 0., 0., 1.); // Rot
glVertex3f( -0.5, -0.5, 0);
glColor4f( 1., 1., 0., 1.); // Gelb
glVertex3f( 0.5, -0.5, 0);
glColor4f( 0., 1., 0., 1.); // Gruen
glVertex3f( 0.5, 0.5, 0);
glColor4f( 0., 0., 1., 1.); // Blau
glVertex3f( -0.5, 0.5, 0);
glEnd();
Ergebnis: ein Quadrat mit an den Ecken definierten Farben, die ueber die Flaeche interpoliert werden. glColor4f nimmt ; die aktuelle Farbe ist State und gilt fuer alle folgenden Vertices.
CPU und GPU
- CPU: Datenerstellung/-manipulation und Programmlogik.
- GPU: Datenverarbeitung und Bilderzeugung.
- Dazwischen: der Upload der Daten (CPU -> GPU).
Datenerstellung: interleaved Array
Beim interleaved (verschachtelten) Speicherlayout liegen alle Daten eines Vertex hintereinander (z.B. x,y,z,r,g,b | x,y,z,r,g,b | ...). Das Gegenteil waeren getrennte Arrays pro Attribut.
float vertices[] = {-0.5, -0.5, 0.0, 0.0, 1.0, // 2D + Farbe (x y r g b)
0.5, -0.5, 0.0, 0.0, 1.0,
0.5, 0.5, 0.0, 1.0, 0.0,
0.0, 1.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.0, 1.0, 0.0};
(Hier 5 Werte/Vertex: + .)
Das Vertex Buffer Object (VBO)
Ein VBO (Vertex Buffer Object) ist ein spezieller Memory-Buffer im Speicher der GPU fuer Geometriedaten. Es wird ueber eine eindeutige ID referenziert. Der Datentransfer erfolgt in drei Schritten:
- ID erzeugen -
glGenBuffers - Buffer aktivsetzen (binden) -
glBindBuffer - Daten hochladen -
glBufferData
Wichtige Befehle: glGenBuffers, glBindBuffer, glBufferData, glDeleteBuffers.
Der usage hint in glBufferData gibt an, wie der Buffer genutzt wird:
GL_STATIC_DRAW- einmal initialisiert, oft gerendert.GL_DYNAMIC_DRAW- oft geaendert.GL_STREAM_DRAW- selten genutzt.
Das Vertex Array Object (VAO)
"OpenGL ist dumm": Es sieht im VBO nur ein Buendel rohen Speicher. Ein VAO (Vertex Array Object) beschreibt, wie die rohen VBO-Daten zu interpretieren sind.
- Das VAO speichert Attributzeiger (vertex attribute pointers), je einer pro Vertex-Attribut, mit einem Attribut-Index.
- Beispiel: Attribut 0 = Position, Attribut 1 = Farbe.
- Nur ein VAO kann gleichzeitig aktiv sein.
Wichtige Befehle: glGenVertexArrays, glBindVertexArray, glDeleteVertexArrays.
glVertexAttribPointer, stride und offset
Ein Vertex-Attribut (vertex attribute) ist eine benannte Dateneigenschaft pro Vertex. Definiert wird es mit glVertexAttribPointer(index, size, type, normalized, stride, offset):
- stride (Schrittweite): Groesse eines kompletten Vertex in Bytes (Abstand zum naechsten gleichartigen Attribut).
- offset (Versatz): Speicher-Offset in Bytes, an dem ein Attribut innerhalb des Vertex beginnt.
- normalized: Flag, ob Werte auf bzw. abgebildet werden.
Merkregel: ein float = 4 Byte. Bei interleaved Layout aus Position (3 floats) + Farbe (3 floats):
Attribute muessen explizit aktiviert werden: glEnableVertexAttribArray(index) (per Default deaktiviert). Deaktivieren mit glDisableVertexAttribArray.
Codebeispiel: VBO + VAO fuer Position und Farbe
float[] vertices = ...; // 3 float Position, danach 3 float Farbe pro Vertex
GLuint vaoID, vboID;
// VBO erzeugen, binden, Daten hochladen (3-Schritte-Upload)
glGenBuffers(1, &vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
// VAO erzeugen und binden
glGenVertexArrays(1, &vaoID);
glBindVertexArray(vaoID);
// VBO im VAO beschreiben (2 Attribute)
glVertexAttribPointer(0, 3, GL_FLOAT, false, 24, 0); // Position, ID 0
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 24, (void*)12); // Farbe, ID 1
glEnableVertexAttribArray(1);
Rechnung: 6 floats/Vertex 4 Byte = stride 24. Position beginnt bei offset 0, Farbe nach 3 floats bei offset 12 Byte (als (void*)12 gecastet - der "seltsame Datentyp"). glVertexAttribPointer bezieht sich immer auf das aktuell gebundene VBO/VAO.
Datenverarbeitung: Shader-Pipeline
Shader sind kleine Programmfragmente, die direkt auf der GPU laufen und Daten vor dem Rendern manipulieren. Vorteil: parallele Ausfuehrung pro Vertex bzw. pro Pixel.
Pipeline-Reihenfolge:
Vertex Shader -> Tesselation Shader (optional) -> Geometry Shader (optional) -> Fragment Shader
- Vertex Shader (Pflicht): berechnet die finale Position jedes Vertex im Ausgabebild.
- Tesselation Shader und Geometry Shader: optionale Stufen.
- Fragment Shader (Pflicht): berechnet die Farbe jedes Pixels im Ausgabebild.
Bilderzeugung: glDrawArrays
glDrawArrays zeichnet ein VAO (und alle verbundenen VBOs) sequenziell durch. "Brute-force": jeder Vertex wird der Reihe nach abgearbeitet. Unflexibel, aber schnell.
- = Startindex, = Anzahl.
Index Buffer Objects (IBO)
Ein IBO (Index Buffer Object, auch Element Array Buffer) ist ein zusaetzlicher Buffer (GL_ELEMENT_ARRAY_BUFFER) mit Indizes auf Vertices im VBO.
- Ermoeglicht die Wiederverwendung von Vertices: von mehreren Dreiecken gemeinsam genutzte Vertices werden nur einmal gespeichert (weniger Datentransfer).
- Gerendert wird mit
glDrawElements. - Flexibler und manchmal schneller; durch Anpassung des IBO lassen sich auch Teile eines Objekts rendern.
Das IBO wird an das aktive VAO gebunden. Deshalb erfolgt glGenBuffers fuer das IBO erst nach glGenVertexArrays / glBindVertexArray.
glDrawElements und count
Beim Aufruf muessen der Primitivtyp (z.B. GL_TRIANGLES) und der IBO-Datentyp (z.B. GL_UNSIGNED_INT) angegeben werden. Weitere Indexdatentypen: GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT.
Codebeispiel: VBO + VAO + IBO Setup
float[] vertices = ...;
int[] indices = ...;
GLuint vaoID, vboID;
// VBO
glGenBuffers(1, &vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
// VAO (nur Position, stride 12)
glGenVertexArrays(1, &vaoID);
glBindVertexArray(vaoID);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0);
glEnableVertexAttribArray(0);
// IBO
GLuint iboID;
glGenBuffers(1, &iboID); // only works after glGenVertexArrays();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
Hier nur Position (3 floats): .
Codebeispiel: Rendering mit IBO
void setup(){
// given: array of vertices and index array
// setup VAO //
// setup VBO //
// setup IBO // // VBOs sind implizit aktiviert
}
void render(){
glBindVertexArray(vaoID); // VAO aktivieren
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0); // count = Anzahl Indizes!
glBindVertexArray(0); // good programmers should reset
}
Merke: count = Anzahl der Indizes, nicht Primitive. Ein Index-Array besteht aus Dreier-Gruppen (3 Indizes pro Dreieck bei GL_TRIANGLES), z.B. V0 V1 V2 | V2 V1 V3 | V2 V3 V4 | ....
glDrawArrays vs. glDrawElements
| Merkmal | glDrawArrays | glDrawElements |
|---|---|---|
| Verfahren | sequenziell (brute-force) | indiziert |
| Vertex-Wiederverwendung | nein | ja (via IBO) |
| Datentransfer | mehr (Vertices dupliziert) | weniger |
| Flexibilitaet | gering | hoeher (Teilrendering moeglich) |
| noetige Buffer | VBO + VAO | VBO + VAO + IBO |
Worked Examples
Beispiel 1 - stride/offset berechnen (Position 3f + Farbe 3f)
- Ein
float= 4 Byte; 6 floats pro Vertex. - stride Byte.
- offset Position ; offset Farbe Byte (als
(void*)12).
Beispiel 2 - count bei glDrawElements
- Ein Dreieck hat bei
GL_TRIANGLES3 Indizes. - Bei Dreiecken: .
- Beispiel 4 Dreiecke -> (nicht 4!).
Beispiel 3 - 3-Schritte-VBO-Upload
glGenBuffers- ID erzeugen.glBindBuffer(GL_ARRAY_BUFFER, vboID)- aktivsetzen.glBufferData(..., GL_STATIC_DRAW)- Daten hochladen.
Pruefungsrelevanz
- OpenGL als State-Machine und "explizit" erklaeren.
- Der 3-Schritte-VBO-Upload (Gen -> Bind -> BufferData) und die Rolle des VAO (Interpretation der Daten).
- stride/offset ausrechnen -
float= 4 Byte, beide in Bytes. Klassische Aufgabe: gegebenes interleaved Layout -> stride und offset je Attribut. - Unterschied glDrawArrays (brute-force, sequenziell) vs. glDrawElements (indiziert, Vertex-Wiederverwendung).
- Shader-Pipeline-Reihenfolge und Aufgaben von Vertex-Shader (finale Position) und Fragment-Shader (Pixelfarbe).
countbei glDrawElements = Anzahl Indizes ( Dreiecke).- Zweck des IBO (Vertex-Wiederverwendung, weniger Datentransfer).
Typische Fehler
- stride/offset in Elementen statt in Bytes angeben. Beide sind in Bytes (3 Positions-floats -> offset 12, nicht 3).
- count bei glDrawElements als Anzahl Dreiecke annehmen. Falsch: es ist die Anzahl der Indizes ( Dreiecke bei GL_TRIANGLES).
- Vergessen, Attribute mit
glEnableVertexAttribArrayzu aktivieren (per Default deaktiviert -> nichts sichtbar). - Verwechslung
GL_ARRAY_BUFFER(Geometriedaten) vs.GL_ELEMENT_ARRAY_BUFFER(Indizes). - Glauben, OpenGL "versteht" die Datenstruktur - nein, "OpenGL ist dumm", das VAO liefert die Interpretation.
- Nur ein VAO gleichzeitig aktiv - vergessen zu binden/zuruecksetzen.
glVertexAttribPointerbezieht sich immer auf das aktuell gebundene VBO/VAO - falsche Bindungsreihenfolge = falsche Zuordnung.- usage-Hints verwechseln:
GL_STATIC_DRAWvs.GL_DYNAMIC_DRAWvs.GL_STREAM_DRAW.
Glossar
| Deutsch | English |
|---|---|
| OpenGL (Open Graphics Library) | OpenGL |
| Spezifikation / API | specification / API |
| Grafikkartentreiber | graphics driver |
| State-Machine / Zustandsautomat | state machine |
| Explizitheit | explicitness |
| GLFW / GLEW / GLM / GLUT / FreeGLUT | helper libraries |
| immediate mode / Legacy | immediate mode / legacy |
| CPU / GPU | CPU / GPU |
| Upload | upload |
| interleaved / verschachtelt | interleaved |
| Vertex Buffer Object (VBO) | vertex buffer object |
| Vertex Array Object (VAO) | vertex array object |
| Vertex-Attribut | vertex attribute |
| Attributzeiger | vertex attribute pointer |
| stride / Schrittweite | stride |
| offset / Versatz | offset |
| normalized | normalized |
| usage hint | usage hint |
| Shader | shader |
| Vertex Shader | vertex shader |
| Fragment Shader | fragment shader |
| Tesselation / Geometry Shader | tessellation / geometry shader |
| Bilderzeugung / Rendering | rendering |
| Index Buffer Object (IBO) / Element Array Buffer | index buffer object |
| Index | index |