Steuerungstasten

nächste Folie (auch Enter oder Spacebar).
vorherige Folie
 d  schaltet das Zeichnen auf Folien ein/aus
 p  wechselt zwischen Druck- und Präsentationsansicht
CTRL  +  vergrößert die Folien
CTRL  -  verkleinert die Folien
CTRL  0  setzt die Größenänderung zurück

Das Weiterschalten der Folien kann ebenfalls durch das Klicken auf den rechten bzw. linken Folienrand erfolgen.

Notation

Typ Schriftart Beispiele
Variablen (Skalare) kursiv $a, b, x, y$
Funktionen aufrecht $\mathrm{f}, \mathrm{g}(x), \mathrm{max}(x)$
Vektoren fett, Elemente zeilenweise $\mathbf{a}, \mathbf{b}= \begin{pmatrix}x\\y\end{pmatrix} = (x, y)^\top,$ $\mathbf{B}=(x, y, z)^\top$
Matrizen Schreibmaschine $\mathtt{A}, \mathtt{B}= \begin{bmatrix}a & b\\c & d\end{bmatrix}$
Mengen kalligrafisch $\mathcal{A}, B=\{a, b\}, b \in \mathcal{B}$
Zahlenbereiche, Koordinatenräume doppelt gestrichen $\mathbb{N}, \mathbb{Z}, \mathbb{R}^2, \mathbb{R}^3$

2D-Texturen

2D-Texturen

  • 2D-Texturen sind 2D-Rastergrafiken, die als "Farbtapete" auf 3D-Geometrie aufgetragen werden können
  • Dadurch kann sehr effizient ein hoher Detailierungsgrad erreicht werden, ohne dass die Details als Geometrie repräsentiert werden müssen
face2_texture
3D-Geometrie
2D-Rastergrafik
Texturiertes 3D Modell
Quelle: 3D Model:Turbosquid (Standard Royalty Free License); Renderer: 3ds Max

2D-Texturen

  • Es ist möglich, verschiedene Texturen in einer Szene zu verwenden
  • Diese können z.B. für jedes Polygon oder pro Objekt umgeschaltet werden
house_texture
Quelle: 3D Model:Turbosquid (Standard Royalty Free License); Renderer: 3ds Max

Texture-Mapping

house_texture_uv
$x$
$y$
$z$
$s$
$t$
$1$
$1$
$0$
  • 2D-Texturen sind in der Regel quadratisch und mit den Parametern $s$ und $t$ im Bereich $[0;1]$ parametrisiert
  • Zum Anwenden einer Textur ist es notwendig, die Abbildungsfunktion ("Texture-Mapping") vom 2D-Raum auf eine 3D-Oberfläche zu definieren
  • Diese wird typischerweise durch Zuweisen von Texturkoordinaten $(s,t)^\top$ zu 3D-Stützpunkten $\mathbf{P}=(x,y,z)^\top$ des 3D-Modells erreicht

    $(s, t)^\top \quad \longmapsto \quad (x,y,z)^\top$

Quelle: 3D Model:Turbosquid (Standard Royalty Free License); Renderer: 3ds Max

Texturen in der OpenGL-Pipeline

openglpipeline
Quelle: basierend auf Mark Segal, Kurt Akeley, The OpenGL Graphics System: A Specification Version 2.0, 2004, Figure 2.1. Block diagram of the GL (modifiziert)

Texturen in der OpenGL-Pipeline

  • In OpenGL erfolgt das Setzen einer Texturkoordinate mit dem Befehl glTexCoord2f
  • Jeweils diejenige Texturkoordinate, die beim Zeichnen eines Stützpunkts gesetzt ist, wird dem Stützpunkt zugeordnet (OpenGL als Zustandsmachine):
    ...
    glBegin(GL_POLYGON);
    glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glEnd();
    ...
    

Texturen in der OpenGL-Pipeline

  • Die Texturkoordinaten sind Teil der Vertex-Daten in der OpenGL-Pipeline und durchlaufen den linken Pfad der Pipeline
  • Die Textur selbst besteht aus Pixel-Daten, die den rechten Pfad der Pipeline durchlaufen
  • Die Texturpixel werden auch häufig als "Texel" bezeichnet
  • Im Rasterisierer müssen für jedes ausgegebene Fragment (Pixel des Framebuffers) interpolierte Texturkoordinaten $(s,t)^\top$ berechnet werden
  • Im Idealfall wird bei der Interpolation die Verzerrung durch die perspektivische Abbildung berücksichtigt (Perspective Texture-Mapping)

Erstellen von 2D-Texturen in OpenGL

  • Das Erstellen einer Textur geschieht in der Regel vor dem eigentlichen Rendern, also z.B. in der Funktion init()
    • Eine 2D-Textur kann erzeugt werden mit
      glBindTexture(GL_TEXTURE_2D, texID);
      glTexImage2D(GL_TEXTURE_2D, texLevel, texFormat, inWidth, inHeight, 
                   inBorder, inFormat, inType, inData);
    • Dabei ist texID ein Integer-Wert, der als eindeutiger Kennzeichner ("ID") für die erzeugte Textur dient. Mit Hilfe der Funktion glGenTextures(1, &texID) kann ein solcher eindeutiger Kennzeichner generiert werden
    • Beim Erstellen werden häufig auch weitere Parameter gesetzt, die die Darstellung der Textur beeinflussen

Aktivieren von 2D-Texturen in OpenGL

  • Während des Render-Vorgangs muss zunächst die Textureinheit aktiviert werden:
    glEnable(GL_TEXTURE_2D);
  • Zum Rendern mit einer bestimmten Textur wird deren eindeutige ID verwendet:
    glBindTexture(GL_TEXTURE_2D, texID);
    Dieser Befehl "bindet" die Textur mit der angegebenen ID an das Ziel GL_TEXTURE_2D
  • D.h. glBindTexture hat mehrere Funktion: Beim erstmaligen Aufruf mit einer neuen ID wird eine neue Textur erzeugt und ansonsten eine existierende Textur aktiviert

Beispiel: Texturieren eines Würfels

  • In diesem Beispiel soll ein Würfel texturiert werden
  • Dazu müssen die entsprechenden Texturkoordinaten und Stützpunkte einander zugeordnet werden (in der Zeichnung sind diese mit den gleichen Farben kodiert)
texture_dice
$x$
$y$
$z$
$s$
$t$
$1$
$1$
$0$
Texturkoordinaten
Stützpunkte

Beispiel: Texturieren eines Würfels in OpenGL

texture

Beispiel: Texturieren eines Würfels in OpenGL

class Renderer {
public:
  float t;
private:
  GLuint texID;

public:
  // constructor
  Renderer() : t(0.0), texID(0) {}
  
  // destructor
  ~Renderer() {
    if(texID !=0) glDeleteTextures( 1, &texID);
  }

public:
  void init() {
    glEnable(GL_DEPTH_TEST);
    std::string fileName("dice_texture_flip.ppm");
    texID = loadTexture(fileName);
  }

  void resize(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0);
  }

  void display() {
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // set camera
    gluLookAt(8.0, -2.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);
    drawTexturedCube();
  }

private:
  // returns a valid textureID on success, otherwise 0
  GLuint loadTexture(std::string &filename) {

    unsigned width;
    unsigned height;
    int level = 0;
    int border = 0;
    std::vector<unsigned char> imgData;

    // load image data
    if(!loadPPMImageFlipped(filename, width, height, imgData)) return 0;

    // data is aligned in byte order
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    //request textureID
    GLuint textureID;
    glGenTextures( 1, &textureID);

    // bind texture
    glBindTexture( GL_TEXTURE_2D, textureID);

    //define how to filter the texture (important but ignore for now)
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    //texture colors should replace the original color values
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE

    // specify the 2D texture map
    glTexImage2D(GL_TEXTURE_2D, level, GL_RGB, width, height, border, GL_RGB, 
                 GL_UNSIGNED_BYTE, &imgData[0]);

    // return unique texture identifier
    return textureID;
  }

  void drawTexturedCube() {
    glEnable(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, texID);
    glColor3f(1.0f,0.0f,0.0f);
    glBegin(GL_POLYGON); // three
    glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glEnd();
    glColor3f(1.0f,1.0f,0.0f);
    glBegin(GL_POLYGON); // five
    glTexCoord2f(0.00f,0.50f); glVertex3f(-1.0f, 1.0f,-1.0f);
    glTexCoord2f(0.00f,0.25f); glVertex3f(-1.0f,-1.0f,-1.0f);
    glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glEnd();
    glColor3f(0.0f,0.0f,1.0f);
    glBegin(GL_POLYGON); // two
    glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.75f,0.25f); glVertex3f( 1.0f,-1.0f,-1.0f);
    glTexCoord2f(0.75f,0.50f); glVertex3f( 1.0f, 1.0f,-1.0f);
    glEnd();
    glColor3f(1.0f,1.0f,1.0f);
    glBegin(GL_POLYGON); // six
    glTexCoord2f(0.25f,0.75f); glVertex3f(-1.0f, 1.0f,-1.0f);
    glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.50f,0.75f); glVertex3f( 1.0f, 1.0f,-1.0f);
    glEnd();
    glColor3f(0.0f,1.0f,1.0f);
    glBegin(GL_POLYGON); // one
    glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f, -1.0f, 1.0f);
    glTexCoord2f(0.25f,0.00f); glVertex3f(-1.0f, -1.0f,-1.0f);
    glTexCoord2f(0.50f,0.00f); glVertex3f( 1.0f, -1.0f,-1.0f);
    glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f, -1.0f, 1.0f);
    glEnd();
    glColor3f(0.0f,1.0f,0.0f);
    glBegin(GL_POLYGON); //four
    glTexCoord2f(0.75f,0.50f); glVertex3f( 1.0f, 1.0f,-1.0f);
    glTexCoord2f(0.75f,0.25f); glVertex3f( 1.0f,-1.0f,-1.0f);
    glTexCoord2f(1.00f,0.25f); glVertex3f(-1.0f,-1.0f,-1.0f);
    glTexCoord2f(1.00f,0.50f); glVertex3f(-1.0f, 1.0f,-1.0f);
    glEnd();
    glDisable(GL_TEXTURE_2D);
  }
  ...
};

Wrap-Parameter

Der GL_TEXTURE_WRAP-Parameter bestimmt, wie sich das Texture-Mapping verhalten soll, wenn die Texturkoordinate $s$ oder $t$ den Zahlenbereich $[0;1]$ verlässt
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  • Mögliche Parameterwerte sind: GL_CLAMP, GL_REPEAT, GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE, oder GL_MIRRORED_REPEAT
texture_wrap

Filter-Parameter

  • Bei der Darstellung von Texturen müssen häufig Farbwerte für Texturkoordinaten berechnet werden, die nicht auf dem Pixelraster der zugrunde liegenden Rastergrafik liegen
  • Ein Filter-Parameter erlaubt die Filtermethode festzulegen, mit welcher Filtermethode die Farbwerte in diesem Fall bestimmt werden sollen
  • GL_TEXTURE_MIN_FILTER setzt den Filter bei Verkleinerungen und GL_TEXTURE_MAG_FILTER für Vergrößerungen:
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  • Mögliche Parameterwerte sind: GL_NEAREST oder GL_LINEAR

Filter-Parameter

  • Bei GL_NEAREST wird der Farbwert des Pixels verwendet, das den kleinsten Abstand zur Texturkoordinate hat
    (Nearest-Neighbor)
  • Diese Operation ist sehr schnell, da die Texturkoordinaten lediglich auf einen ganzzahligen Pixel-Wert gerundet werden müssen und der zugehörige Farbwert $f$ dann direkt abgelesen werden kann

    $\begin{align} x_{\small \mathrm{nearest}} &= \mathrm{int}(s * \mathrm{width} + 0.5)\\ y_{\small \mathrm{nearest}} &= \mathrm{int}(t * \mathrm{height} + 0.5)\\ f_{\small \mathrm{nearest}} &= f(x_{\small \mathrm{nearest}}, y_{\small \mathrm{nearest}} ) \end{align}$

  • Bei GL_LINEAR wird der Farbwert bilinear interpoliert

Bilineare Interpolation

bilinear_interpolation
$x$
$x_1$
$x_2$
$y$
$y_1$
$y_2$
Pixelraster
Datenpunkte
$\mathbf{p}$
$\mathbf{q}_1$
$\mathbf{q}_2$
  • Bei der bilinearen Interpolation an der Stelle $\mathbf{p}=(x,y)^\top$ wird der Farbwert aus den 4 benachbarten Pixelwerten ermittelt, die exakt auf dem Pixelraster liegen. Diese seien gegeben als: $(x_1, y_1)^\top$, $(x_1, y_2)^\top$, $(x_2, y_1)^\top$, und $(x_2, y_2)^\top$
  • Zunächst werden per linearer Interpolation in $x$-Richtung die Farbwerte an den Hilfspositionen $\mathbf{q}_1$ und $\mathbf{q}_2$ bestimmt:
    $\begin{align} f(\mathbf{q}_1) &= \frac{x_2-x}{x_2-x_1} f(x_1, y_1) + \frac{x - x_1}{x_2-x_1} f(x_2, y_1)\\ f(\mathbf{q}_2) &= \frac{x_2-x}{x_2-x_1} f(x_1, y_2) + \frac{x - x_1}{x_2-x_1} f(x_2, y_2) \end{align}$
  • Eine weitere lineare Interpolation, nun in $y$-Richtung, ergibt den Farbwerte bei $\mathbf{p}$:
    $\begin{align} f(\mathbf{p}) &= \frac{y_2-y}{y_2-y_1} f(\mathbf{q}_1) + \frac{y - y_1}{y_2-y_1} f(\mathbf{q}_2) \end{align}$

Bilineare Interpolation

  • Durch Einsetzen der Hilfsgrößen ergibt sich insgesamt:

    $\begin{align} f(\mathbf{p}) = \Big((x_2-x_1)(y_2-y_1)\Big)^{-1} \Big( &(x_2-x)(y_2-y)\, f(x_1,y_1) +\\\ & (x-x_1)(y_2-y) \,f(x_2,y_1) +\\ & (x_2-x)(y-y_1) \,f(x_1,y_2) +\\ & (x-x_1)(y-y_1) \,f(x_2,y_2) \Big) \end{align}$

  • Eine bilineare Interpolation ist somit im Vergleich zur Nearest-Neighbor-Interpolation relativ aufwendig
  • Der Vorteil ist, dass durch eine solche gewichtete Mittelwertbildung, wie sie bei der bilinearen Interpolation durchgeführt wird, der "Treppeneffekt" an Kanten (Aliasing) reduziert wird

Mip-Mapping

  • Bei weit entfernten Oberflächen können, trotz bilinearer Interpolation, Aliasing-Effekte auftreten
  • Das Problem bei entfernten Oberflächen ist, dass sehr viele Texturpixel auf die Fläche eines Pixels im Kamerabild projiziert werden
  • Um den richtigen Farbwert zu bestimmen, müssten nicht nur die 4 nächsten Nachbarn, sondern alle betroffenen Texturpixel bei der Interpolation berücksichtig werden
  • Um dies zu approximieren, werden Mipmaps verwendet
  • MIP = multum in parvo (Latein, "viel im Kleinen")

Mip-Mapping

texture_mipmap
resolution_pyramid
  • Eine Mipmap ist eine Auflösungspyramide, bei der jede nächst kleinere Auflösung genau halb so groß ist
  • Beim Texture-Mapping von weit entfernten Objekten kann eine Mipmap eingesetzt werden, um Aliasing-Effekte zu verringern
  • Die Extraktion der Farbwerte muss auf einer Auflösungstufe erfolgen, die grob genug ist, so dass Nearest-Neighbor oder bilineare Interpolation näherungsweise zum richtigen Ergebnis für den Farbwert kommen
  • D.h. die Auflösung muss so gewählt werden, dass das Texturraster nach der Projektion in die Bildebene ungefähr dem Raster des Framebuffers entspricht
  • Die passende Auflösung wird nicht pro Objekt oder Polygon festgelegt, sondern pro Framebuffer-Pixel individuell ermittelt

Mip-Mapping in OpenGL

  • Um eine Mipmap zu verwenden, müssen folgende Parameter gesetzt werden
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
    glTexImage2D (...);
  • Mögliche Parameterwerte für den GL_TEXTURE_MIN_FILTER sind: GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, und GL_LINEAR_MIPMAP_LINEAR
  • Beim GL_TEXTURE_MAG_FILTER ist die Verwendung von Mipmaps nicht sinnvoll und wird auch nicht unterstützt

Texturparameter und Mip-Mapping in OpenGL

opengl_textxureparameter

Texturparameter und Mip-Mapping in OpenGL

class Renderer {
public:
  float t;
  int wrapS;
  int wrapT;
  int magFilter;
  int minFilter;
  int selectedTexID;
private:
  GLuint texID0;
  GLuint texID1;

public:
  // constructor
  Renderer() : t(-90.0f),
               wrapS(GL_REPEAT), wrapT(GL_REPEAT),
               magFilter(GL_LINEAR), 
               minFilter(GL_LINEAR_MIPMAP_LINEAR),
               selectedTexID(0),
               texID0(0), texID1(0)
  {}
  // destructor
  ~Renderer() {
    if(texID0 !=0) glDeleteTextures( 1, &texID0);
    if(texID1 !=0) glDeleteTextures( 1, &texID1);
  }

public:
  void init() {
    glEnable(GL_DEPTH_TEST);
    std::string fileName0("checker_texture_512.ppm");
    texID0 = loadTexture(fileName0);
    std::string fileName1("checker_texture_32.ppm");
    texID1 = loadTexture(fileName1);
  }

  void resize(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0);
  }

  void display() {
    glClearColor(0.7f, 0.7f, 0.7f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // set camera
    gluLookAt(8.0, -2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);
    drawTexturedPlane();
  }

  // returns a valid textureID on success, otherwise 0
  GLuint loadTexture(std::string &filename) {

    unsigned width;
    unsigned height;
    int level = 0;
    int border = 0;
    std::vector <unsigned char> imgData;

    // load image data
    if(!loadPPMImageFlipped(filename, width, height, imgData)) return 0;

    // data is aligned in byte order
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    //request textureID
    GLuint textureID;
    glGenTextures(1, &textureID);

    // bind texture
    glBindTexture(GL_TEXTURE_2D, textureID);


    // parameters that define how to warp the texture
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    GLfloat borderColor[4] = {1.0f, 1.0f, 0.0f, 1.0f};
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

    // parameters that define how to filter the texture
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
                    GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);


    // texture colors should replace the original color values
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE

    // specify the 2D texture map
    glTexImage2D (GL_TEXTURE_2D, level, GL_RGB, width, height, border, 
                  GL_RGB, GL_UNSIGNED_BYTE, &imgData[0]);

    // return unique texture identifier
    return textureID;
  }

  void setTextureParameters() { 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MAG_FILTER, magFilter);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MIN_FILTER, minFilter);
  }

  void drawTexturedPlane() {
    glEnable(GL_TEXTURE_2D);
    if(selectedTexID == 0) {
      glBindTexture(GL_TEXTURE_2D, texID0);
    }else{
      glBindTexture(GL_TEXTURE_2D, texID1);
    }
    setTextureParameters();
    glColor3f(1.0,0.0,0.0);
    glBegin(GL_POLYGON);
    glTexCoord2f(5 .00f,  5.00f); 
    glVertex3f(-10.0f, 10.0f, 0.0f);
    glTexCoord2f( 5.00f, -4.00f); 
    glVertex3f(-10.0f,-10.0f, 0.0f);
    glTexCoord2f(-4.00f, -4.00f); 
    glVertex3f( 10.0f,-10.0f, 0.0f);
    glTexCoord2f(-4.00f,  5.00f); 
    glVertex3f( 10.0f, 10.0f, 0.0f);
    glEnd();
    glDisable(GL_TEXTURE_2D);
  }
  ...
};

Wirkung der Textur

  • Mit der Funktion
    glTexEnv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    kann beeinflusst werden, wie die Texturfarbe $C_s$ und der Texturalphakanal $A_s$ mit dem aktuellen Fragment (Farbwert $C_f$ und Alphakanal $A_f$) kombiniert wird
  • Mögliche Parameterwerte sind u.a.:
    GL_REPLACE, GL_MODULATE, GL_DECAL, GL_BLEND, oder GL_ADD

Wirkung der Textur

GL_RGB GL_RGBA
GL_REPLACE $C=C_s$
$A=A_f$
$C=C_s$
$A=A_s$
GL_MODULATE $C=C_fC_s$
$A=A_f$
$C=C_f C_s$
$A=A_f A_s$
GL_DECAL $C=C_s$
$A=A_f$
$C=C_f (1-A_s) + C_s A_s$
$A=A_f$
GL_BLEND $C=C_f(1-C_s)+C_c C_s$
$A=A_f$
$C=C_f(1-C_s)+C_c C_s$
$A=A_f A_s$
GL_ADD $C=C_f+ C_s$
$A=A_f$
$C=C_f+ C_s$
$A=A_f A_s$

Beispiel: Überblendung von zwei Texturen in OpenGL

textureblend

Automatische Erzeugung von Texturkoordinaten

  • Bisher wurde nur Texture-Mapping für eine geringe Anzahl an Polygonen betrachtet, wobei die Texturkoordinaten von Hand zugewiesen wurden
  • Für Texturierung komplexer Objektoberflächen ist dies jedoch sehr aufwendig, so dass die Anwendung eines automatischen Verfahren wünschenswert ist
  • Idee von Bier & Sloan (1986):
    • Schritt 1: Umhüllen des Objekts mit einer einfachen virtuellen Fläche (Zylinder, Kugel, Quader) mit kanonischen Texturkoordinaten
    • Schritt 2: Übertragen dieser Texturkoordinaten auf das umhüllte Objekt

Box Mapping

  • Schritt 1: Umhüllen des Objekts mit einer achsen-parallelen Bounding-Box des Objektes. Diese ist leicht zu berechnen, indem die maximalen und minimalen Koordinaten aller Stützpunkte ermittelt werden (getrennt für jede Raumrichtung).
  • Schritt 2: Die Raumrichtung mit der längsten Ausdehnung wird für die Texturkoordinate $s$ und die zweitlängste für $t$ verwendet.
    Gilt z.B. $(x_{\small \mathrm{max}} - x_{\small \mathrm{min}}) ≥ (y_{\small \mathrm{max}} - y_{\small \mathrm{min}}) ≥ (z_{\small \mathrm{max}} - z_{\small \mathrm{min}})$, ergeben sich die Texturkoordinaten für einen Stützpunkt $\mathbf{P}=(x,y,z)^\top$ zu:
    boxmapping
    $x$
    $y$
    $z$
    $x_{\small \mathrm{max}}$
    $x_{\small \mathrm{min}}$
    $y_{\small \mathrm{max}}$
    $y_{\small \mathrm{min}}$
    $z_{\small \mathrm{max}}$
    $z_{\small \mathrm{min}}$
    $\mathbf{P}$

    $\begin{align} s &= \frac{x-x_{\small \mathrm{min}} }{ x_{\small \mathrm{max}} - x_{\small \mathrm{min}} }\\ t &= \frac{y-y_{\small \mathrm{min}} }{ y_{\small \mathrm{max}} - y_{\small \mathrm{min}} } \end{align}$

Zylinder-Mapping

cylinder_mapping
$x$
$y$
$z$
$z_{\small \mathrm{max}}$
$z_{\small \mathrm{min}}$
$\mathbf{P}$
$\phi$
  • Schritt 1:Umhüllen des Objekts mit einem Zylinder. Im Folgenden wird angenommen, dass der Zylinder entlang der $z$-Achse ausgerichtet ist. Falls dies nicht der Fall ist, kann dies immer durch eine entsprechende Rotation erreicht werden.
  • Schritt 2: Umrechnen eines Stützpunkts $\mathbf{P}=(x,y,z)^\top$ in Zylinderkoordinaten mit Winkel $\phi$ und Höhe $h$. Für die Texturkoodinaten ergibt sich dann:

    $\begin{align} s &= h = \frac{z-z_{\small \mathrm{min}} }{ z_{\small \mathrm{max}} - z_{\small \mathrm{min}} }\\ t &= \frac{1}{2\pi} \phi = \frac{1}{2\pi} \arctan\left(\frac{y}{x}\right) \end{align}$

    Hinweise: Da in der Praxis $x=0$ sein kann, ist es sinnvoll, den $\arctan$ durch den $\mathrm{atan2}$ zu ersetzen

Kugel Mapping

spherical_coord
$y$
$x$
$z$
$\mathbf{P}$
$\phi$
$\theta$
  • Schritt 1: Umhüllen des Objekts mit einer Kugel
  • Schritt 2: Umrechnen eines Stützpunkts $\mathbf{P}=(x,y,z)^\top$ in Kugelkoordinaten mit den Winkeln $\phi$ und $\theta$. Für die Texturkoordinaten ergibt sich damit:

    $\begin{align} s &= \frac{1}{2\pi} \phi = \frac{1}{2\pi} \arctan\left(\frac{y}{x}\right)\\ t &= 1.0 - \frac{1}{\pi} \theta \\ &= 1.0 - \frac{1}{\pi} \arccos(z) \\ &= \frac{1}{\pi} \arccos(-z)\\ \end{align}$

3D-Texturen

3D-Texturen

  • 3D-Texturen sind Rastergrafiken mit drei Dimensionen
  • Solche volumetrischen Daten werden z.B. häufig in der medizinischen Bilderfassung erzeugt. Ein CT- oder MRT-Gerät nimmt dabei mehrere Schnitte durch das zu untersuchende Objekt auf. Die Schnitte sind jeweils eine 2D-Rastergrafik und können durch Aufeinanderstapeln zu einem 3D-Volumen zusammengesetzt werden
    texture_uvw.png
    s
    r
    t
    1
    1
    1
    0
  • Zu den bisherigen Texturkoordinaten $s$ und $t$, wie von den 2D-Texturen bekannt, kommt somit eine weitere Dimension $r$, die orthogonal auf den bisherigen steht
Quelle: Volume rendered CT scan Wikipedia, Public Domain

3D-Texturen in OpenGL

  • In OpenGL sind die Befehle zur Erstellung und Anwendung von 3D-Texturen konsistent zu den Befehlen für 2D-Texturen
  • Aus GL_TEXTURE_2D wird GL_TEXTURE_3D, aus glTexImage2D wird glTexImage3D, usw.
  • Die Speicheranordnung bei Übergabe von Textur-Daten an glTexImage3D kann als eine Aneinanderreihung von 2D-Texturen-Daten interpretiert werden
  • D.h. beim Füllen des Speichers müssen die Texel in $s$-Richtung in jedem Schritt erhöht, in $t$-Richtung erst nach width Texel-Werten, und in $r$-Richtung erst nach width*height Texel-Werten

Beispiel: Texturierung eines zufälligen Terrains

opengl_terrain

Beispiel: Texturierung eines zufälligen Terrains

class Renderer {
public:
  float t;
private:
  GLuint texID;
  std::vector <float> terrain;

public:
  // constructor
  Renderer() : t(0.0), texID(0) {}
  //destructor
  ~Renderer() {
    if(texID !=0) glDeleteTextures( 1, &texID);
  }

public:
  void init() {
    glEnable(GL_DEPTH_TEST);

    glTexImage3D = (PFNGLTEXIMAGE3DPROC) 
                   wglGetProcAddress("glTexImage3D");
    if(glTexImage3D == NULL) {
      QMessageBox msgBox;
      msgBox.setText("Can not load the glTexImage3D function");
      msgBox.exec();
    }

    std::vector< std::string > filenames;
    filenames.push_back("deep_water.ppm");
    filenames.push_back("shallow_water.ppm");
    filenames.push_back("shore.ppm");
    filenames.push_back("fields.ppm");
    filenames.push_back("rocks.ppm");
    filenames.push_back("snow.ppm");
    texID = loadTexture3D(filenames);

    rebuildTerrain();
  }

  void resize(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0);
  }
  
  void display() {
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // set camera
    gluLookAt(1.5, -1.0, 1.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);
    drawTerrain();
  }
  
  void rebuildTerrain() {

    //create random values
    int dim = 40;
    terrain.resize(dim*dim);

    for(int r=0; r < dim*dim; r++) {
      int rval = rand();
      terrain[r] = (fabs(float(rval))/float(RAND_MAX));
    }

    if(true) { // generate smooth terrain values
      std::vector<float≷ smoothTerrain(dim*dim);
      for(unsigned k=0; k < 5; k++){
        float maxVal = 0.0f;
        float minVal = 1.0f;
        for(int x = 0; x < dim; x++) {
          for(int y = 0; y < dim; y++) {
            if(x == 0 || x == dim-1) terrain[x*dim+y] = 0.0f;
            else if (y == 0 || y == dim-1) {
              terrain[x*dim+y] = 0.0f;
            }else {
              float a = 0.0f;
              int counter = 0;
              for(int s=-1; s <= 1; s++) {
                for(int r=-1; r <= 1; r++) {
                  a += terrain[(x+s)*dim+(y+r)];
                  counter++;
                }
              }
              float val = a / float(counter);
              smoothTerrain[x*dim+y] = val;
              if(val > maxVal) maxVal = val;
              if(val < minVal) minVal = val;
            }
          }
        }
        for(int r=0; r < dim*dim; r++) {
          terrain[r] = (smoothTerrain[r] - minVal) / 
                       (maxVal-minVal);
        }
      }
    }
  }

private:

  // returns a valid textureID on success, otherwise 0
  GLuint loadTexture3D(std::vector< std::string > &filenames) {

    unsigned width = 0;
    unsigned height = 0;
    unsigned depth = unsigned(filenames.size());
    int level = 0;
    int border = 0;
    std::vector<unsigned char> imgData;
    std::vector<unsigned char> data3d;
    unsigned prevWidth = 0;
    unsigned prevHeight = 0;

    for(unsigned i=0; i < depth; i++) {
      // load image data
      if(!loadPPMImageFlipped(filenames[i], width, height, imgData)) return 0;
      if(i != 0 && (prevWidth != width || prevHeight != height)) return 0;

      // pack 2D images subsequently into a large 3D buffer
      data3d.insert(data3d.end(), imgData.begin(), imgData.end());
      prevWidth = width;
      prevHeight = height;
    }

    // data is aligned in byte order
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    //request textureID
    GLuint textureID;
    glGenTextures( 1, &textureID);

    // bind texture
    glBindTexture(GL_TEXTURE_3D, textureID);

    //parameters the define how to warp the texture
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
    GLfloat borderColor[4] = {0.0f,0.0f,0.0f,1.0f};
    glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor);

    //define how to filter the texture
    glTexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    //texture colors should replace the original color values
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE

    // specify the 2D texture map
    glTexImage3D(GL_TEXTURE_3D, level, GL_RGB, width, height, depth, border, 
                 GL_RGB, GL_UNSIGNED_BYTE, &data3d[0]);

    // return unique texture identifier
    return textureID;
  }


  void drawTerrain() {
    glEnable(GL_TEXTURE_3D);

    glBindTexture(GL_TEXTURE_3D, texID);
    glColor3f(1.0f,0.0f,0.0f);
    unsigned dim = unsigned(sqrt(terrain.size()));
    float maxHeight = 0.2f;
    float texHeight = 0.9f;
    for(unsigned x = 1; x < dim; x++) {
      for(unsigned y = 1; y < dim; y++) {
        glBegin(GL_POLYGON);
        glTexCoord3f(float(x-1)/float(dim),
                     float(y-1)/float(dim),
                     terrain[(x-1)*dim+(y-1)]*texHeight);
        glVertex3f(float(x-1)/float(dim)-0.5f,
                   float(y-1)/float(dim)-0.5f,
                   terrain[(x-1)*dim+(y-1)]*maxHeight);
        glTexCoord3f(float(x)/float(dim),
                     float(y-1)/float(dim),
                     terrain[x*dim+(y-1)]*texHeight);
        glVertex3f(float(x)/float(dim)-0.5f,
                   float(y-1)/float(dim)-0.5f,
                    terrain[x*dim+(y-1)]*maxHeight);
        glTexCoord3f(float(x)/float(dim),
                   float(y)/float(dim),
                   terrain[x*dim+y]*texHeight);
        glVertex3f(float(x)/float(dim)-0.5f,
                   float(y)/float(dim)-0.5f,
                   terrain[x*dim+y]*maxHeight);
        glTexCoord3f(float(x-1)/float(dim),
                   float(y)/float(dim),
                   terrain[(x-1)*dim+y]*texHeight);
        glVertex3f(float(x-1)/float(dim)-0.5f,
                   float(y)/float(dim)-0.5f,
                   terrain[(x-1)*dim+y]*maxHeight);
        glEnd();
      }
    }
    glDisable(GL_TEXTURE_3D);
  }
  ...
};

Textureinheiten

  • Eine Grafikkarte hat mehrere Textureinheiten
  • Dies erlaubt den gleichzeitigen Zugriff auf mehrere Texturen, da jede Einheit eine andere Textur zur Verfügung stellen kann
  • Besonders interessant, wenn wir später eigene Shader schreiben
  • Um OpenGL 3.x zu unterstützen, muss eine Grafikkarte mindestens 16 Textureinheiten haben
  • Die maximale Anzahl der Textureinheiten liefert der Befehl:
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &noOfTextureUnits);
  • Der Befehl glActiveTexture schaltet die aktuell modifizierte Textureinheit um:
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texA);
    ...
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texB);
    ...

OpenGL Texture-Mapping aus objekt-orientierter Sicht

  • Textur-ID (Handle auf Texturobjekt)
  • Eigenschaften eines Texturobjekts
    • Dimension (1D, 2D, 3D) und Auflösung (128x128, 256x256, ...)
    • Komponententyp (ubyte, ushort, uint, float16, float32)
    • Anzahl & Bedeutung der Komponenten (RGB, RGBA, BGR, ... )
    • Datenblock für Rastergrafik und Mipmap
    • Parameter für Filterung und Randbehandlung (Wrap)
  • Eigenschaften der Textureinheit
    • aktuell gebundenes Texturobjekt
    • Texturumgebungsfarbe
    • Parameter für die Wirkung der Textur auf aktuelles Fragment
    • Texturzugriffsfunktion (deaktiviert, 1D, 2D, 3D)
    • Texturtransformationsstapel glMatrixMode(GL_TEXTURE)

Framebuffer Objects

Framebuffer Objects

  • OpenGL erlaubt, die Pixel-Daten des Framebuffers auszulesen, um diese z.B. als Eingabe für einen zweiten Rendering-Durchgang zu verwenden
  • Dazu muss, neben dem originalen Framebuffer, der typischerweise vom Fenster-Manager kontrolliert wird, ein benutzerdefiniertes Framebuffer Object (FBO) erzeugt und als neues Renderziel definiert werden (Off-Screen-Rendering)
  • Ein FBO kann dabei folgende 2D-Rastergrafiken umfassen:
    mehrere Color-Buffer, Depth-Buffer, Stencil-Buffer, Accumulation-Buffer

Erstellen von Framebuffer Objects

  • Ein FBO kann erzeugt werden mit
    glGenFramebuffers(1, &fboID);
    glBindFramebuffer(GL_FRAMEBUFFER, fboID);
  • Die Befehle erzeugen jedoch zunächst nur einen leeren Container, der keinen Speicher zum Schreiben der Pixel-Daten enthält
  • Um Pixel-Datenspeicher bereitzustellen, können entweder Texturen verwendet werden oder so genannte "Renderbuffer Objects"

2D-Texturen als Datenspeicher

  • Um eine Textur als Pixel-Datenspeicher zu verwenden, muss diese zunächst erzeugt und anschließend mit glFramebufferTexture2D(...) an das FBO übergeben werden
    glGenTextures (1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);
    glTexImage2D(GL_TEXTURE_2D, texLevel, texFormat, 
                 inWidth, inHeight, inBorder, inFormat, inType, NULL);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
                           GL_TEXTURE_2D, texID, texLevel);
    
  • Dabei gibt das zweite Argument an, für welchen Buffer die Textur verwendet werden soll. Mögliche Parameter sind: GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, ..., GL_DEPTH_ATTACHMENT oder GL_STENCIL_ATTACHMENT

Renderbuffer Objects als Datenspeicher

  • Um ein Renderbuffer-Object als Pixel-Datenspeicher zu verwenden, muss dies erzeugt und anschließend mit glFramebufferRenderbuffer(..) an das FBO übergeben werden
    glGenRenderbuffers(1, &depthID);
    glBindRenderbuffer(GL_RENDERBUFFER, depthID);
    glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
    
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
                              GL_RENDERBUFFER, depthID);
    
  • Dabei sollte das interne Speicherformat format so gewählt werden, dass es zum Verwendungszweck passt, also z.B. GL_RGBA für GL_COLOR_ATTACHMENT0 oder GL_DEPTH_COMPONENT für GL_DEPTH_ATTACHMENT, usw.

Beispiel: Rendern der Szene in eine Textur

dbo
  • Quelltext des Beispiels mit GLUT: Fbo.cpp
  • Quelltext des Beispiels mit Qt: Fbo.cpp
  • Quelltext des Beispiels mit Java: Fbo.java

Beispiel: Rendern der Szene in eine Textur

class Renderer {
public:
  float t;
  int mode;
private:
  GLuint fboID;
  GLuint texID;
  GLuint depthID;
  int fboTexSize;
  int width;
  int height;
public:
  // constructor
  Renderer() : t(0.0), mode(1),
               fboID(0), texID(0), depthID(0),
               fboTexSize(512), width(0), height(0) {}

  // destructor
  ~Renderer() {
    if(texID !=0) glDeleteTextures( 1, &texID);
    if(fboID !=0) glDeleteFramebuffers(1, &fboID);
    if(depthID != 0) glDeleteRenderbuffers(1, &depthID);
  }

public:
  void init() {
    glEnable(GL_DEPTH_TEST);

    // create a frame buffer object
    glGenFramebuffers(1, &fboID);

    // bind the frame buffer
    glBindFramebuffer(GL_FRAMEBUFFER, fboID);

    // Generate render texture
    glGenTextures (1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);

    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    int level = 0;
    glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, 
                 fboTexSize, fboTexSize, 
                 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    // Attach the texture to the fbo
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
                           GL_TEXTURE_2D, texID, level);
    if(!checkFramebufferStatus()) exit(1);

    // generate a renderbuffer for the depth buffer
    glGenRenderbuffers(1, &depthID);
    glBindRenderbuffer(GL_RENDERBUFFER, depthID);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 
                          fboTexSize, fboTexSize);

    // Attach the depth buffer to the fbo
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, 
                              GL_DEPTH_ATTACHMENT, 
                              GL_RENDERBUFFER, depthID);
    if(!checkFramebufferStatus()) exit(1);

    // unbind texture
    glBindTexture(GL_TEXTURE_2D, 0);
    //unbind fbo
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
  }

  void resize(int w, int h) {
    width = w;
    height = h;
    changeViewport(w, h);
  }

  void display() {

    if(mode == 1) {
      // bind the fbo
      glBindFramebuffer(GL_FRAMEBUFFER, fboID);
      changeViewport(fboTexSize,fboTexSize);
    }
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    // render to attached fbo
    drawScene();

    if(mode == 1) {
      // unbind frame buffer
      glBindFramebuffer(GL_FRAMEBUFFER, 0);

      changeViewport(width, height);
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

      // using first render pass result as texture
      glEnable(GL_TEXTURE_2D);
      glBindTexture(GL_TEXTURE_2D, texID);

      // second render pass
      drawScene();

      glDisable(GL_TEXTURE_2D);
    }
  }
  
private:
  void changeViewport(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0);
  }

  void drawScene() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // set camera
    gluLookAt(8.0, -2.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);
    drawTexturedCube();
  }

  bool checkFramebufferStatus() {
    GLenum status;
    status = (GLenum) glCheckFramebufferStatus(GL_FRAMEBUFFER);
    switch(status) {
    case GL_FRAMEBUFFER_COMPLETE:
      return true;
    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
      printf("Framebuffer incomplete, incomplete attachment\n");
      return false;
    case GL_FRAMEBUFFER_UNSUPPORTED:
      printf("Unsupported framebuffer format\n");
      return false;
    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
      printf("Framebuffer incomplete, missing attachment\n");
      return false;
    case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
      printf("Framebuffer incomplete, missing draw buffer\n");
      return false;
    case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
      printf("Framebuffer incomplete, missing read buffer\n");
      return false;
    }
    return false;
  }

  void drawTexturedCube() {

    glColor3f(1.0f,0.0f,0.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f,-1.0f, 1.0f);
    glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glEnd();
    glColor3f(1.0f,1.0f,0.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, 1.0f,-1.0f);
    glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f,-1.0f,-1.0f);
    glTexCoord2f(1.00f,1.00f); glVertex3f(-1.0f,-1.0f, 1.0f);
    glTexCoord2f(0.00f,1.00f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glEnd();
    glColor3f(0.0f,0.0f,1.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.00f,0.00f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.00f,0.00f); glVertex3f( 1.0f,-1.0f, 1.0f);
    glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f,-1.0f,-1.0f);
    glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, 1.0f,-1.0f);
    glEnd();
    glColor3f(1.0f,1.0f,1.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, 1.0f,-1.0f);
    glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, 1.0f,-1.0f);
    glEnd();
    glColor3f(0.0f,1.0f,1.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, -1.0f, 1.0f);
    glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f, -1.0f,-1.0f);
    glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f, -1.0f,-1.0f);
    glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, -1.0f, 1.0f);
    glEnd();
    glColor3f(0.0f,1.0f,0.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.00f,0.00f); glVertex3f( 1.0f, 1.0f,-1.0f);
    glTexCoord2f(1.00f,0.00f); glVertex3f( 1.0f,-1.0f,-1.0f);
    glTexCoord2f(1.00f,1.00f); glVertex3f(-1.0f,-1.0f,-1.0f);
    glTexCoord2f(0.00f,1.00f); glVertex3f(-1.0f, 1.0f,-1.0f);
    glEnd();
  }
};

Beispiel: Rendern des Depth-Buffers in eine Textur

dbo_depth

Beispiel: Rendern des Depth-Buffers in eine Textur

class Renderer {
public:
  float t;
  int mode;
private:
  GLuint fboID;
  GLuint depthID;
  int fboTexSize;
  int width;
  int height;
public:
  // constructor
  Renderer() : t(0.0), mode(1),
               fboID(0), depthID(0),
               fboTexSize(512), width(0), height(0) {}

  // destructor
  ~Renderer() {
    if(fboID !=0) glDeleteFramebuffers(1, &fboID);
    if(depthID != 0) glDeleteTextures(1, &depthID);
  }

public:
 void init() {
    glEnable(GL_DEPTH_TEST);

    // create a frame buffer object
    glGenFramebuffers(1, &fboID);

    // bind the frame buffer
    glBindFramebuffer(GL_FRAMEBUFFER, fboID);

     // Generate depth render texture
    glGenTextures (1, &depthID);
    glBindTexture(GL_TEXTURE_2D, depthID);

    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, 
                    GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    int level = 0;
    glTexImage2D(GL_TEXTURE_2D, level, GL_DEPTH_COMPONENT, 
                 fboTexSize, fboTexSize, 0, GL_DEPTH_COMPONENT, 
                 GL_FLOAT, NULL);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);


    // Attach the texture to the fbo
    glFramebufferTexture2D(GL_FRAMEBUFFER, 
                           GL_DEPTH_ATTACHMENT, 
                           GL_TEXTURE_2D, depthID, level);
    if(!checkFramebufferStatus()) exit(1);

    // unbind texture
    glBindTexture(GL_TEXTURE_2D, 0);
    //unbind fbo
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
  }

  void resize(int w, int h) {
    width = w;
    height = h;
    changeViewport(w, h);
  }

  void display() {

    if(mode > 0) {
      // bind the fbo
      glBindFramebuffer(GL_FRAMEBUFFER, fboID);
      changeViewport(fboTexSize,fboTexSize);
    }else{
      changeViewport(width, height);
    }

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    // render to attached fbo
    drawScene();

    if(mode == 1) {
      // unbind frame buffer
      glBindFramebuffer(GL_FRAMEBUFFER, 0);

      changeViewportOrtho(width, height);

      // using first render pass result as texture
      glEnable(GL_TEXTURE_2D);
      glBindTexture(GL_TEXTURE_2D, depthID);

      //draw quad with depth texture
      glColor3f(1.0f,0.0f,0.0f);
      glBegin(GL_POLYGON);
      glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f,-1.0f, 0.0f);
      glTexCoord2f(1.00f,0.00f); glVertex3f( 1.0f,-1.0f, 0.0f);
      glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f, 1.0f, 0.0f);
      glTexCoord2f(0.00f,1.00f); glVertex3f(-1.0f, 1.0f, 0.0f);
      glEnd();

      glDisable(GL_TEXTURE_2D);
    }  
  }

private:
  void changeViewport(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (30.0, (float)w/(float)h, 7.0, 12.0);
  }

  void changeViewportOrtho(int w, int h) {
    float aspect = (float)w/(float)h;
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.0*aspect, 1.0*aspect, -1.0, 1.0, -1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
  }


  void drawScene() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // set camera
    gluLookAt(8.0, -2.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);
    drawTexturedCube();
  }
...
};

Gibt es Fragen?

questions

Anregungen oder Verbesserungsvorschläge können auch gerne per E-mail an mich gesendet werden: Kontakt

Weitere Vorlesungsfolien

Folien auf Englisch (Slides in English)