Control Keys

move to next slide (also Enter or Spacebar).
move to previous slide.
 d  enable/disable drawing on slides
 p  toggles between print and presentation view
CTRL  +  zoom in
CTRL  -  zoom out
CTRL  0  reset zoom

Slides can also be advanced by clicking on the left or right border of the slide.

Notation

Type Font Examples
Variables (scalars) italics $a, b, x, y$
Functions upright $\mathrm{f}, \mathrm{g}(x), \mathrm{max}(x)$
Vectors bold, elements row-wise $\mathbf{a}, \mathbf{b}= \begin{pmatrix}x\\y\end{pmatrix} = (x, y)^\top,$ $\mathbf{B}=(x, y, z)^\top$
Matrices Typewriter $\mathtt{A}, \mathtt{B}= \begin{bmatrix}a & b\\c & d\end{bmatrix}$
Sets calligraphic $\mathcal{A}, B=\{a, b\}, b \in \mathcal{B}$
Number systems, Coordinate spaces double-struck $\mathbb{N}, \mathbb{Z}, \mathbb{R}^2, \mathbb{R}^3$

2D Textures

2D Textures

  • 2D textures are 2D raster graphics, which can be applied as a "color wallpaper" to 3D geometry
  • This an efficient way to achieve a high level of detail without representing the details as geometry
face2_texture
3D geometry
2D raster graphics
Textured 3D model
Source: 3D Model:Turbosquid (Standard Royalty Free License); Renderer: 3ds Max

2D Textures

  • It is possible to use multiple textures in a scene
  • For example, the textures can be switched for each polygon or each object
house_texture
Source: 3D Model:Turbosquid (Standard Royalty Free License); Renderer: 3ds Max

Texture Mapping

house_texture_uv
$x$
$y$
$z$
$s$
$t$
$1$
$1$
$0$
  • 2D textures are usually squared and are parameterized with the parameters $s$ and $t$ in the range $[0;1]$
  • In order to apply a texture, it is necessary to define the mapping function ("texture mapping") from the 2D texture space to a 3D surface
  • This is typically accomplished by assigning
    texture coordinates $(s,t)^\top$ to 3D vertices  $\mathbf{P}=(x,y,z)^\top$ of the 3D model

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

Source: 3D model:Turbosquid (Standard Royalty Free License); Renderer: 3ds Max

Textures in the OpenGL-Pipeline

openglpipeline
Source: based on Mark Segal, Kurt Akeley, The OpenGL Graphics System: A Specification Version 2.0, 2004, Figure 2.1. Block diagram of the GL (modified)

Textures in the OpenGL-Pipeline

  • In OpenGL, setting a texture coordinate is performed with the command glTexCoord2f
  • The particular texture coordinate that is set while drawing a vertex is assigned to the vertex
    (OpenGL as a state machine):
    ...
    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();
    ...
    

Textures in the OpenGL Pipeline

  • The texture coordinates are part of the vertex data in the OpenGL pipeline and pass through the left path of the pipeline
  • The texture itself consists of pixel data that pass through the right path of the pipeline
  • The texture pixels are also often referred to as "texels"
  • In the rasterizer, for each output fragment (pixel of the frame buffer) interpolated texture coordinates $(s,t)^\top$ are computed
  • Ideally, for the interpolation the distortion caused by perspective projection is taken into account (perspective texture-mapping)

Creating 2D Textures in OpenGL

  • Creating a texture is usually done before the actual rendering, e.g., in the function init()
    • A 2D texture can be generated with
      glBindTexture(GL_TEXTURE_2D, texID);
      glTexImage2D(GL_TEXTURE_2D, texLevel, texFormat, inWidth, inHeight, 
                   inBorder, inFormat, inType, inData);
    • where texID is an integer value that serves as an unique identifier ("ID") for the generated texture. With the function glGenTextures(1, &texID) such an unique identifier can be generated
    • During the generation, additional parameters are often set that influence the appearance of the texture

Activating 2D Textures in OpenGL

  • During rendering, first the texture unit must be activated
    glEnable(GL_TEXTURE_2D);
  • To render with a particular texture, its unique ID is used:
    glBindTexture(GL_TEXTURE_2D, texID);
    This command "binds" the texture with the given ID to the target GL_TEXTURE_2D
  • That is, glBindTexture has several functions: when first called with a new ID, a new texture is generated, otherwise an existing texture is activated

Example: Texturing a Cube

  • In this example, a cube is textured
  • To this end, the texture coordinates and vertices must be associated with each other (in the figure below, correspondences are coded with the same colors)
texture_dice
$x$
$y$
$z$
$s$
$t$
$1$
$1$
$0$
texture coordinates
vertices

Example: Texturing a Cube in OpenGL

texture

Example: Texturing a Cube 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

The GL_TEXTURE_WRAP parameter defines how the texture mapping behaves if the texture coordinate $s$ or $t$ leaves the range $[0;1]$
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  • Possible parameter values are: GL_CLAMP, GL_REPEAT, GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE, oder GL_MIRRORED_REPEAT
texture_wrap

Filter Parameters

  • When displaying textures it often occurs that color values for texture coordinates must be calculated that are not on the pixel grid of the underlying raster graphics
  • A filter parameter allows to specify with which filtering method the color values are to be determined
  • GL_TEXTURE_MIN_FILTER sets the filter for reduction in size and GL_TEXTURE_MAG_FILTER for enlargements:
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  • Possible parameter values are: GL_NEAREST or GL_LINEAR

Filter Parameter

  • With GL_NEAREST the color value of the pixel is used that has the shortest distance to the texture coordinate (nearest-neighbor)
  • This operation is very fast, because the texture coordinate is simply rounded to an integer pixel value and the corresponding color value  $f$ can then be read directly

    $\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}$

  • With GL_LINEAR the color value is determined by bilinear interpolation

Bilinear Interpolation

bilinear_interpolation
$x$
$x_1$
$x_2$
$y$
$y_1$
$y_2$
pixel grid
data points
$\mathbf{p}$
$\mathbf{q}_1$
$\mathbf{q}_2$
  • For bilinear interpolation at the point $\mathbf{p}=(x,y)^\top$ the color value is computed from the 4 neighboring pixel values that lie exactly on the pixel grid. Let's assume they are given as: $(x_1, y_1)^\top$, $(x_1, y_2)^\top$, $(x_2, y_1)^\top$, and $(x_2, y_2)^\top$
  • First, color values at auxiliary positions $\mathbf{q}_1$ und $\mathbf{q}_2$ are determined by linear interpolation in $x$-direction:

    $\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}$

  • Another linear interpolation, now in $y$-direction, gives the color value at location $\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}$

Bilinear Interpolation

  • By inserting the auxiliary variables the overall interpolation formula is given by:

    $\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}$

  • Thus, a bilinear interpolation has a relatively high computational effort compared to nearest neighbor interpolation
  • The advantage is that by such a weighted averaging (as it is performed by bilinear interpolation) the "staircasing effect" at the edges (aliasing) is reduced

Mipmaps

  • For distant surfaces, despite bilinear interpolation, aliasing effects occur
  • The problem with distant surfaces is that a lot of texture pixels are projected into the area of one pixel in the camera image
  • To determine the correct color value, not only the 4 nearest neighbors, but all affected texture pixels must be taken into account for the interpolation
  • To approximate this, mipmaps are used
  • MIP = multum in parvo (Latin, "much in little")

Mipmaps

texture_mipmap_256
resolution_pyramid
  • A mipmap is a resolution pyramid, where for each level the resolution is exactly halved
  • Texture mapping for distant objects can use mipmaps to reduce aliasing effects
  • The extraction of the color values should occur at a pyramid level that is coarse enough so that nearest neighbor or bilinear interpolation gets approximately the correct result for the color value
  • To this end, the resolution must be chosen such that the texture grid after projection into the image plane matches approximately the size of the framebuffer
  • The appropriate resolution is not set per object or polygon, but is determined individually for each pixel of the framebuffer

Mipmaps in OpenGL

  • To use a mipmap the following parameters must be set
    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 (...);
  • Possible parameter values for the GL_TEXTURE_MIN_FILTER are: GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, und GL_LINEAR_MIPMAP_LINEAR
  • For the GL_TEXTURE_MAG_FILTER the use of mipmaps does not make sense and is not supported

Texture Parameters and Mip-Mapping in OpenGL

opengl_textxureparameter

Texture Parameters and 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);
  }
  ...
};

Effect of the Texture

  • The function
    glTexEnv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    controls how the texture color  $C_s$ and its alpha value $A_s$ is combined with the current fragment
    (color  $C_f$ and alpha $A_f$)
  • Possible parameter values are, e.g.:
    GL_REPLACE, GL_MODULATE, GL_DECAL, GL_BLEND, oder GL_ADD

Effect of the Texture

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$

Example: Blending of Two Textures in OpenGL

textureblend

Automatic Generation of Texture Coordinates

  • So far, texture mapping for a small number of polygons was presented, where the texture coordinates were assigned by hand
  • For texturing of object with complex surface topology this is very complicated, so that an automatic procedure is desirable
  • Idea of Bier & Sloan (1986):
    • Step 1: Wrap the object with a simple virtual surface (cylinder, sphere, or cube) with canonical texture coordinates
    • Step 2: Transfer the texture coordinates form the virtual to the wrapped object

Box Mapping

  • Step 1: Wrap the object with an axis-parallel bounding box. This is easily calculated by the maximum and minimum coordinates of all the vertices (separately computed for each spatial direction)
  • Step 2: The spatial direction with the longest expansion is used for $s$, the second longest for $t$.
    If we have $(x_{\small \mathrm{max}} - x_{\small \mathrm{min}}) ≥ (y_{\small \mathrm{max}} - y_{\small \mathrm{min}}) ≥ (z_{\small \mathrm{max}} - z_{\small \mathrm{min}})$, the texture coordinates $\mathbf{P}=(x,y,z)^\top$ for a vertex are computed by:
    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}$

Cylinder Mapping

cylinder_mapping
$x$
$y$
$z$
$z_{\small \mathrm{max}}$
$z_{\small \mathrm{min}}$
$\mathbf{P}$
$\phi$
  • Step 1: Wrap the object with a cylinder. In the following it is assumed that the cylinder is aligned with the $z$-axis. If this is not the case, this can always be achieved by a corresponding rotation.
  • Step 2: Convert a vertex $\mathbf{P}=(x,y,z)^\top$ to cylindrical coordinates with angle $\phi$ and height $h$. For the texture coordinates we have:

    $\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}$

    Notes: Since in practice $x=0$ can occur, it makes sense to replace the $\arctan$ with the $\mathrm{atan2}$

Sphere Mapping

spherical_coord
$x$
$y$
$z$
$\mathbf{P}$
$\phi$
$\theta$
  • Step 1: Wrap the object with a sphere
  • Step 2: Convert a vertex $\mathbf{P}=(x,y,z)^\top$ to spherical coordinates with the angles $\phi$ and $\theta$. For the texture coordinates we have:

    $\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 Textures

3D Textures

  • 3D textures are raster graphics with three dimensions
  • Such volumetric data is often generated in medical imaging. A CT or MRI machine takes several cuts through the investigated object. Each slice represents a 2D raster graphics and can be assembled to a 3D volume by stacking the slices.
    texture_uvw.png
    s
    r
    t
    1
    1
    1
    0
  • The texture coordinates  $s$ and $t$, as known from 2D textures, are extended by a new dimension $r$, which is orthogonal to the two existing ones
Source: Volume rendered CT scan Wikipedia, Public Domain

3D Textures in OpenGL

  • In OpenGL the commands for creation and application of 3D textures are consistent with the commands for 2D textures
  • The function glTexImage2D becomes glTexImage3D and GL_TEXTURE_2D becomes GL_TEXTURE_3D, etc.
  • When passing texture data to glTexImage3D the memory layout can be interpreted as a series of 2D textures data
  • That is, when filling the memory, the texel in $s$-direction is increased in each step, in $t$-direction after width texel values, and in $r$-direction only after width*height texel values

Example: Texturing a Random Terrain

opengl_terrain

Example: Texturing a Random Terrain

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);
  }
  ...
};

Texture Units

  • A graphics card has multiple texture units
  • This allows simultaneous access to multiple textures since each unit can provide a different texture
  • This becomes especially interesting when we start writing our own shaders
  • To support OpenGL 3.x, a graphics card must have at least 16 texture units
  • The maximum number of texture units can be queried by the command:
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &noOfTextureUnits);
  • The command glActiveTexture sets the currently modified texture unit:
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texA);
    ...
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texB);
    ...

OpenGL Texture Mapping from an Object-Oriented View

  • Texture ID (handle on texture object)
  • Properties of a texture object
    • Dimension (1D, 2D, 3D) and resolution (128x128, 256x256, ...)
    • Component Type (ubyte, ushort, uint, float16, float32)
    • Number and semantic of the components (RGB, RGBA, BGR, ... )
    • Data block for raster graphics and mipmap
    • Parameters for filtering and wrapping
  • Properties of the texture unit
    • Currently bounded texture object
    • Texture environment color
    • Parameters for the effect of the texture on the current fragment
    • Texture access function (disabled, 1D, 2D, 3D)
    • Texture transformation stack glMatrixMode(GL_TEXTURE)

Framebuffer Objects

Framebuffer Objects

  • OpenGL allows to read off the pixel data from the framebuffer, for example, to use them as an input for a second rendering pass
  • To this end, in addition to the original frame buffer (which is typically controlled by the window manager) a custom framebuffer object (FBO) is created and defined as a new render target (off-screen rendering)
  • An FBO may include the following 2D raster graphics:
    several color buffer, depth buffer, stencil buffer, and accumulation buffer

Creating Framebuffer Objects

  • An FBO can be generated with
    glGenFramebuffers(1, &fboID);
    glBindFramebuffer(GL_FRAMEBUFFER, fboID);
  • The commands only produce an empty container that has no memory assigned for storing the pixel data
  • Pixel data storage can be provided by either textures or so-called "renderbuffer objects"

2D Textures as a Data Storage

  • To use a texture as a pixel data memory, it must be generated and then passed to the FBO with glFramebufferTexture2D(...)
    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);
    
  • The second argument indicates for which buffer the texture should be used. Possible parameters are: GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, ..., GL_DEPTH_ATTACHMENT or GL_STENCIL_ATTACHMENT

Renderbuffer Objects as a Data Storage

  • To use a rendernbuffer object as a pixel data memory, it must be generated with glFramebufferRenderbuffer(..) and then passed with glFramebufferRenderbuffer(..) to the FBO
    glGenRenderbuffers(1, &depthID);
    glBindRenderbuffer(GL_RENDERBUFFER, depthID);
    glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
    
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
                              GL_RENDERBUFFER, depthID);
    
  • The internal storage format format should be chosen such that it fits the purpose, that is: GL_RGBA for GL_COLOR_ATTACHMENT0 or GL_DEPTH_COMPONENT for GL_DEPTH_ATTACHMENT, etc.

Example: Rendering the Scene to a Texture

dbo
  • Source code of the example with GLUT: Fbo.cpp
  • Source code of the example with Qt: Fbo.cpp
  • Source code of the example with Java: Fbo.java

Example: Rendering the Scene to a Texture

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();
  }
};

Example: Rendering the Depth Buffer into a Texture

dbo_depth

Example: Rendering the Depth Buffer into a Texture

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();
  }
...
};

Are there any questions?

questions

Please notify me by e-mail if you have questions, suggestions for improvement, or found typos: Contact

More lecture slides

Slides in German (Folien auf Deutsch)