Kapitel 9b: Texturierung
Texturen (textures) trennen das Erscheinungsbild einer Oberflaeche (Material, Farbe, Muster, vorberechnetes Licht) von der Geometrie: ein 2D-Bild wird ueber Texturkoordinaten auf das Polygon abgebildet. Behandelt werden UV-Koordinaten, Wrapping, Filterung, Mipmaps und der OpenGL-Textur-Workflow.
Ueberblick
Statt jedes Detail eines Objekts durch aufwendige Geometrie zu modellieren, speichert man das Erscheinungsbild als Textur (texture), also ein meist 2D-Bild, getrennt von der Geometrie. Beim Rendern projiziert der Rasterizer / Fragment Shader die Textur auf die Oberflaeche des Polygons.
Motivation (Why textures?):
- Rendering detaillierter Geometrie wird teuer.
- Modellieren und Animieren detaillierter Geometrie ist aufwendig.
- Besserer Workflow: Material von Geometrie trennen.
- Erlaubt das Vorberechnen / Simulieren von Beleuchtung (precompute / simulate lighting).
Roter Faden (OpenGL-Workflow):
- Wie werden Koordinaten zugeordnet? (Texturkoordinaten)
- Wie wird eine Textur in OpenGL definiert? (
glGenTextures...glTexImage2D) - Wie wird ueber die [0,1]-Grenze hinaus abgetastet? (Wrapping)
- Wie wird beim Vergroessern/Verkleinern gefiltert? (Filtering, Mipmaps)
- Wie werden mehrere Texturen kombiniert? (Multitexturing)
- Wie greift der Shader darauf zu? (Sampler)
Texturkoordinaten (texture coordinates)
Ein Texel (texel, texture element) ist das einzelne Bildelement einer Textur, analog zum Pixel im Ausgabebild. Ueber die Texturkoordinate wird der passende Texel gefunden.
Texturkoordinaten liegen im normierten Bereich
Wichtig (typische Falle): Bei Texturen verwendet man nicht , sondern (oder ). Der Wertebereich ist normiert auf , unabhaengig von der tatsaechlichen Pixelaufloesung der Textur. Texturkoordinaten sind "just another vertex attribute", also nur ein weiteres Vertex-Attribut. Waehrend der Rasterisierung wird die Koordinate pro Fragment interpoliert.
Die Ablaufkette von der Koordinate zur Farbe hat drei Stufen:
Beispiel (Koordinatenumrechnung): Parameterraum wird auf den Texel-Index abgebildet, dort liegt die Texel-Farbe . (Die zugehoerige Objektraum-Position ist .)
Beispiel (8x8-Raster): In einem 8x8-Gitter markiert die normierte Koordinate die untere linke Ecke der Zelle mit Index . Ecke unten links , Ecke oben rechts .
Texture Atlas und Wahl der Koordinaten
Ein Texture Atlas (Texturatlas) fasst mehrere Teiltexturen bzw. das gesamte UV-Layout eines Objekts in einem Texturbild zusammen. Aus Effizienzgruenden verwendet man oft ein Mapping pro Objekt (weniger Bindungswechsel).
Texturkoordinaten werden zugewiesen (1) von Hand oder ueber automatische Projektionen oder (2) mit Modellierungswerkzeugen und interaktiver Platzierung.
Wichtig (typische Falle): OpenGL trianguliert Polygone (ein Viereck wird in zwei Dreiecke zerlegt). Dadurch kann die Textur an der Diagonale sichtbar verzerrt (deformiert) erscheinen. Bei der Wahl der Texturkoordinaten beachten.
Texturdefinition in OpenGL
Der Ablauf ist analog zu VBOs: ID generieren, binden, Daten hochladen, spaeter loeschen.
GLuint texID;
glGenTextures(1, &texID); // ID erzeugen
glBindTexture(GL_TEXTURE_2D, texID); // Textur aktivieren / binden
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_FLOAT, mydata);
// ... spaeter:
glDeleteTextures(1, &texID); // Textur loeschen
Die Argumente von glTexImage2D (aus dem Beispiel S. 16):
| Argument | Wert | Bedeutung |
|---|---|---|
| target | GL_TEXTURE_2D | Texturziel (auch 1D/3D) |
| level | 0 | Mipmap-Stufe (0 = volle Aufloesung) |
| internalFormat | GL_RGBA | wie OpenGL die Textur intern speichert |
| width, height | 512, 512 | Groesse (moeglichst ) |
| border | 0 | deprecated, seit OpenGL 3.1 immer 0 |
| format | GL_RGBA | Format des uebergebenen Daten-Arrays |
| type | GL_FLOAT | Datentyp im Array (z.B. GL_UNSIGNED_BYTE) |
| data | mydata | rohe Texturdaten im Hauptspeicher |
Wichtig (typische Falle):
internalFormat(interne Speicherung durch OpenGL, z.B.GL_ALPHA,GL_RGB,GL_RGBA,GL_RG16) undformat/type(Beschreibung des uebergebenen Client-Arrays) sind verschiedene Dinge und werden gern verwechselt.
Wichtig:
glTexImage2Dlaedt keine Bilddateien ("no image loader").datasind bereits dekodierte Rohdaten. Das Laden/Dekodieren muss vorher geschehen.
Zweierpotenzen (power-of-two)
Fuer width/height/depth sollte man Werte der Form verwenden. Mipmaps werden immer aus -Texturen erzeugt; bei anderen Groessen skaliert OpenGL intern.
Texturkoordinaten als VBO
Texturkoordinaten sind nur ein weiteres VBO, definiert mit glVertexAttribPointer, aktiviert mit glEnableVertexAttribArray. Der index wird im Shader wieder aufgegriffen: layout(location = index) in vec2 texCoords;.
Texture Wrapping (Wiederholung / Umlauf)
Wrapping regelt, was passiert, wenn Texturkoordinaten ausserhalb liegen. Gesetzt per glTexParameteri mit GL_TEXTURE_WRAP_{S,T,R} (pro Koordinate s, t, r).
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
| Modus | Verhalten ausserhalb [0,1] |
|---|---|
GL_CLAMP_TO_EDGE | Werte oder werden auf den Rand geklemmt; der Randtexel wird wiederholt / gestreckt |
GL_REPEAT | nur der Nachkommaanteil wird verwendet, die Textur wird gekachelt / wiederholt |
Wrapping kann pro Richtung unterschiedlich sein (z.B. REPEAT fuer s, CLAMP_TO_EDGE fuer t): dann wird horizontal gekachelt, vertikal geklemmt.
Texture Filtering (Filterung)
Filterung behandelt das Missverhaeltnis zwischen Texel- und Pixelaufloesung. Es gibt zwei Faelle:
- Magnification (Vergroesserung): ein Texel wird auf mehr als ein Pixel abgebildet (Textur zu klein / Kamera zu nah).
- Minification (Verkleinerung): viele Texel werden auf dasselbe Pixel abgebildet (Textur zu gross / Flaeche weit entfernt).
Gesetzt per glTexParameteri mit dem Filtertyp GL_TEXTURE_MIN_FILTER oder GL_TEXTURE_MAG_FILTER.
Filtermodi
GL_NEAREST(Point Filter): der naechstgelegene Texel wird verwendet.GL_LINEAR: linearer Durchschnitt der vier naechstgelegenen Texel = bilineare Interpolation.
| Fall | GL_NEAREST | GL_LINEAR |
|---|---|---|
| Magnification | Texel repliziert, blockig/pixelig | weiche Uebergaenge (bilinear) |
| Minification | exakte Koordinate, Aliasing/Flimmern | bilinear, etwas glatter |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
Wichtig (typische Falle): Nur der MIN_FILTER (Minification) kennt Mipmap-Filtermodi. Der MAG_FILTER (Magnification) akzeptiert ausschliesslich
GL_NEARESToderGL_LINEAR, keine Mipmap-Modi. Einen Mipmap-Modus fuerGL_TEXTURE_MAG_FILTERanzugeben ist ein haeufiger Fehler.
Mipmaps und trilineare Interpolation
Eine Textur ist eine abgetastete Funktion. Korrekt waere, die Farbe eines Ausgabefragments als normalisiertes Integral ueber alle vom Fragment abgedeckten Texel zu berechnen. In der Ferne (Minification) deckt ein Pixel viele Texel ab, und das Nachschlagen aller ist zu rechenintensiv.
Mipmaps approximieren dieses Integral durch vorgemittelte Texel in verschiedenen Aufloesungsstufen (Levels):
- Level 0 = volle Aufloesung.
- Jede weitere Stufe hat halbe Kantenlaenge (Breite/Hoehe geteilt durch 2).
Der zusaetzliche Speicher der vollen Mipmap-Pyramide ist gering:
also ca. +1/3 (33%) gegenueber dem Original. Ein -Bild wird intern als -Bild dargestellt.
Erzeugung fuer die aktuell gebundene Textur:
glGenerateMipmap(GL_TEXTURE_2D);
(alternativ ueber den level-Parameter von glTexImage2D).
Trilineare Interpolation
Die Minification-Filtermodi haben die Form
- 1. Teil (vor
MIPMAP): Interpolation innerhalb einer Mipmap-Stufe. - 2. Teil (nach
MIPMAP): Interpolation zwischen zwei Stufen.
Wichtig (typische Falle): Trilinear = bilinear innerhalb einer Stufe plus linear zwischen zwei Stufen =
GL_LINEAR_MIPMAP_LINEAR. Der Vorteil von Mipmaps (Anti-Aliasing) ist am deutlichsten bei Bewegung sichtbar (weniger Flimmern).
Multitexturing
Texturen muessen an Texture (Mapping) Units (TMU) auf der GPU gebunden werden. Eine TMU hat eine eigene Texture Engine, um Texturen auf 3D-Geometrie abzubilden. Im Allgemeinen gilt: Anzahl der Texturen Anzahl verfuegbarer Texture Units (). Zur Einordnung: eine nvidia RTX 5080 hat 336 TMUs und verarbeitet ca. 879 Gigatexel/s.
Aktivierung: erst die Unit waehlen (glActiveTexture), dann die Textur binden (glBindTexture):
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, myFirstTexture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mySecondTexture);
Die GL_TEXTUREi sind fortlaufend, daher ist glActiveTexture(GL_TEXTURE0 + 1) aequivalent zu glActiveTexture(GL_TEXTURE1).
Texturen in den Shadern
Texturierung betrifft beide Shader-Stufen:
- Vertex Shader: berechnet/reicht Texturkoordinaten durch (an eine
out-Variable). - Fragment Shader: fragt die interpolierten Koordinaten ab, liest den Farbwert der Textur und verwendet ihn (z.B. fuers Shading).
Im Fragment Shader heisst eine Textur Sampler (sampler1D, sampler2D, sampler3D). Zugriff ueber texture(texID, texCoord).
Vertex Shader:
layout(location = 2) in vec2 texcoords;
out vec2 tc;
// do something with texcoords //
tc = texcoords;
Fragment Shader (additive Kombination zweier Texturen = Multitexturing im Shader):
out vec4 color;
in vec2 tc0;
in vec2 tc1;
uniform sampler2D tex0;
uniform sampler2D tex1;
void main(){
color = texture(tex0, tc0) + texture(tex1, tc1);
}
Sampler-Uniforms setzen: Texture Unit, nicht ID!
glUniform1i(glGetUniformLocation(programID, "tex0"), 0);
glUniform1i(glGetUniformLocation(programID, "tex1"), 1);
Wichtig (zentrale, explizit hervorgehobene Prüfungsfalle): Das zweite Argument von
glUniform1iist die Texture Unit (hier 0 bzw. 1), NICHT die Textur-ID (der vonglGenTextureszurueckgegebene Name). Der Sampler zeigt auf die Unit, an die die Textur zuvor mitglActiveTexture+glBindTexturegebunden wurde.
Worked Example: vollstaendiger Ablauf
Create (Textur erstellen):
GLuint texID;
glGenTextures(1, &texID); // 1. ID erzeugen
glBindTexture(GL_TEXTURE_2D, texID); // 2. binden
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0,
GL_RGBA, GL_UNSIGNED_BYTE, mydata); // 3. Daten senden
glGenerateMipmap(GL_TEXTURE_2D); // (optional) Mipmaps
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // 4. Parameter
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Usage (Textur verwenden, pro Frame/Draw):
glActiveTexture(GL_TEXTURE0); // 1. Unit aktivieren
glBindTexture(GL_TEXTURE_2D, texID); // 2. Textur binden
glUniform1i(glGetUniformLocation(programID, "tex0"), 0); // Sampler -> Unit 0
// 3. Texturkoordinaten im Vertex Shader (in -> out)
// 4. texture(tex0, tc) im Fragment Shader
Prüfungsrelevanz
- / statt , Wertebereich normiert .
- Ablaufkette parameter space image space (Texel-Index) texel color nachvollziehen.
- CLAMP_TO_EDGE vs REPEAT, und dass Wrapping pro Richtung (s, t, r) getrennt gesetzt wird.
- Magnification vs Minification ueber das Verhaeltnis Texel:Pixel definieren.
GL_NEARESTvsGL_LINEAR(bilinear = Mittel der 4 naechsten Texel).- Mipmaps: warum (Integral-Approximation), Level 0 = voll, halbe Kantenlaenge pro Stufe, ca. +1/3 Speicher, .
- Trilinear =
GL_LINEAR_MIPMAP_LINEAR; NamensschemaGL_{...}_MIPMAP_{...}deuten koennen. - MAG_FILTER hat keine Mipmap-Modi.
glUniform1ierwartet die Texture Unit, nicht die Textur-ID.- Reihenfolge der Create-/Usage-Checklisten (ideale Prüfungsfrage).
Typische Fehler
- -Notation oder falscher Wertebereich statt .
- Texel (Textur-Element) mit Pixel (Ausgabebild) verwechseln.
- Mipmap-Filtermodus faelschlich fuer
GL_TEXTURE_MAG_FILTERangeben (nurNEAREST/LINEARerlaubt). - Texture Unit vs Textur-ID bei
glUniform1iverwechseln (haeufigste Fehlerquelle). internalFormatundformat/typegleichsetzen.- Vergessen, dass
glTexImage2Dkeinen Bildlader hat (Rohdaten noetig). - Nicht-Zweierpotenz-Groessen bei Mipmaps (interne Skalierung).
- Deformation durch Triangulierung an der Diagonale uebersehen.
borderverwenden (seit OpenGL 3.1 muss er 0 sein).
Glossar
| Deutsch | English |
|---|---|
| Textur | texture |
| Texel / Texturelement | texel (texture element) |
| Texturkoordinaten (s,t / u,v) | texture coordinates |
| UV-Mapping | UV mapping |
| Texturatlas | texture atlas |
| Textur-Umlauf / Wiederholung | texture wrapping |
| auf Rand klemmen | clamp to edge (GL_CLAMP_TO_EDGE) |
| kacheln / wiederholen | repeat (GL_REPEAT) |
| Texturfilterung | texture filtering |
| Vergroesserung | magnification |
| Verkleinerung | minification |
| Naechster-Texel-Filter (Point Filter) | GL_NEAREST |
| bilineare Interpolation | GL_LINEAR |
| Mipmap / Aufloesungspyramide | mipmap |
| Mipmap-Stufe | mipmap level |
| trilineare Interpolation | trilinear interpolation (GL_LINEAR_MIPMAP_LINEAR) |
| Zweierpotenz | power-of-two |
| Multitexturing | multitexturing |
| Texture Mapping Unit | texture (mapping) unit (TMU) |
| Sampler | sampler (sampler2D) |
| Sampling-Funktion | texture(texID, texCoord) |
| interne Speicherform | internalFormat |
| Format / Datentyp des Arrays | format / type |
| Textur-ID vs Texture Unit | texture ID vs texture unit |