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$

Buffer Objects

  • Until now, a large number of function calls were needed for drawing graphic primitives (such as polygons, lines, or points)
    ...
    glColor3f(1.0f, 1.0f, 0.0f);
    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();
    ...
    
  • Each function call is transferring only as small amount of data to the OpenGL pipeline

Buffer Objects

  • This way of graphics programming leads to slow processing, because the graphics hardware operations can not be executed in parallel on a large number of data, instead the hardware is constantly waiting for data
  • Therefore, since OpenGL >= 3.1 the corresponding functions (glBegin, glVertex, etc.) are only supported in the "Compatibility Profile"
  • Instead, vertex data is now transferred in larger blocks, which are stored and managed on the graphics card using Buffer Objects

Vertex Buffer Objects (VBO)

Creating VBOs in OpenGL

  • Creating VBOs is usually done before the actual rendering, e.g., in the function init()
  • The procedure is analogous to creating textures and involves three steps:
    • Step 1: The function glGenBuffers(1, &bufID) is generating an unique identifier
    • Step 2: With glBindBuffer(GL_ARRAY_BUFFER, bufID) a new VBO is created. Possible targets are GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
    • Step 3: The function glBufferData(GL_ARRAY_BUFFER, size, inData, GL_STATIC_DRAW) transfers the vertex data

Activating VBOs in OpenGL

  • In order to use a VBO for rendering, it must be activated. This also involves three steps:
    • Step 1: Binding the buffer with glBindBuffer(GL_ARRAY_BUFFER, bufID)
    • Step 2: Defining a stride and a start address for the memory access
    • Step 3: Activating the buffer for a certain purpose glEnableClientState(GL_VERTEX_ARRAY).
      Possible parameters are: GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_SECONDARY_COLOR_ARRAY, GL_INDEX_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY usw.

Rendering using VBOs in OpenGL

  • After activating a VBO the rendering of all containing data is initiated with a single function call:
    glDrawArrays(GL_POLYGON, first, count);
    Possible arguments are the same as those of glBegin(...), such as GL_POLYGON, GL_LINE_LOOP, GL_LINES, GL_POINTS, etc.
  • If the VBO is no longer used, the memory can be released by glDeleteBuffers(1, &bufID)

Example: Rendering a Sphere with a Large Number of Points

opengl_VBO_drawarrays

Example: Rendering a Sphere with a Large Number of Points

class Renderer {
public:
  float t;
  int mode;
private:
  GLuint bufID;
  int bufSize;

public:
  // constructor
  Renderer() : t(0.0), mode(0), bufID(0), bufSize(0) {}
  //destructor
  ~Renderer() {
    if(bufID !=0) glDeleteBuffers( 1, &bufID);
  }

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

    // generating VBO input data
    std::vector<float> dataIn;
    unsigned ayimutSegs = 1000;
    unsigned polarSegs = 1000;
    float ayimutStep = 2.0f * M_PI / float(ayimutSegs);
    float polarStep = M_PI / float(polarSegs);
    float r = 1.0;
    bufSize = 0;
    for(unsigned m=0; m < ayimutSegs; m++) {
      for(unsigned n=0; n < polarSegs; n++) {
        float phi = ayimutStep*m;
        float theta = polarStep * n;
        
        // compute xyz from spherical coordinates
        float x = r * sin(theta) * cos(phi);
        float y = r * sin(theta) * sin(phi);
        float z = r * cos(theta);

        dataIn.push_back(x);
        dataIn.push_back(y);
        dataIn.push_back(z);
        bufSize++;
      }
    }
    // generating VBO
    glGenBuffers(1, &bufID);
    glBindBuffer(GL_ARRAY_BUFFER, bufID);
    glBufferData(GL_ARRAY_BUFFER, dataIn.size()*sizeof(float), 
                 &dataIn[0], GL_STATIC_DRAW);
  }

  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(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);

    // activating VBO
    glBindBuffer(GL_ARRAY_BUFFER, bufID);
    int stride = 0;
    glVertexPointer(3, GL_FLOAT, stride, NULL);
    glEnableClientState(GL_VERTEX_ARRAY);
    if(mode == 0) {
      glColor3f(1.0f,1.0f,1.0f);
    }else{
      glColorPointer(3, GL_FLOAT, stride, NULL);
      glEnableClientState(GL_COLOR_ARRAY);
    }

    // render VBO
    glDrawArrays(GL_POINTS, 0, bufSize);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
  }
};

Using Vertex Indices

  • When drawing polygonal surfaces, it often happens that vertices are used several times for adjacent polygons
  • If the vertices shall be stored only once, polygons can be defined using the indices of the vertices

Using Vertex Indices

dice
  • Example of a cube:
    float vertexData[] = 
    {-1.0f,  1.0f,  1.0f,
     -1.0f, -1.0f,  1.0f,
      1.0f, -1.0f,  1.0f,
      1.0f,  1.0f,  1.0f,
     -1.0f,  1.0f, -1.0f,
     -1.0f, -1.0f, -1.0f,
      1.0f, -1.0f, -1.0f,
      1.0f,  1.0f, -1.0f
    };
    
    int indicesData[] = 
    { 0, 1, 2, 3,
      0, 4, 5, 1,
      4, 7, 6, 5,
      2, 6, 7, 3,
      1, 5, 6, 2,
      3, 7, 4, 0
    };
    

Using Vertex Indices with VBOs

  • To use vertex indices with VBOs, usually two VBOs are defined: one for vertex data and another for index data
  • A VBO with index data is generated and activated with glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufID)
  • The rendering is then carried out with the function
    glDrawElements(GL_POLYGON, count, GL_UNSIGNED_INT);

Example: Rendering a Spiky Sphere with Vertex Indices

opengl_VBO_drawelements

Example: Rendering a Spiky Sphere with Vertex Indices

class Renderer {
public:
  float t;
  int mode;
  unsigned ayimutSegs;
  unsigned polarSegs;
private:
  GLuint vertBufID, indicesBufID;
  int vertSize;

public:
  // constructor
  Renderer() : t(0.0), mode(1), ayimutSegs(10), polarSegs(10),
               vertBufID(0), indicesBufID(0), vertSize(0)  {}
  //destructor
  ~Renderer() {
    if(vertBufID !=0) glDeleteBuffers( 1, &vertBufID);
    if(indicesBufID !=0) glDeleteBuffers( 1, &indicesBufID);
  }

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

    recreateVBOs();
  }

 void recreateVBOs() {

    if(vertBufID !=0) glDeleteBuffers( 1, &vertBufID);
    if(indicesBufID !=0) glDeleteBuffers( 1, &indicesBufID);

     // generating VBO input data
    std::vector<float> vertexData;
    std::vector<int> indicesData;
    int totalSegs = ayimutSegs * polarSegs;
    float ayimutStep = 2.0f * M_PI / float(ayimutSegs);
    float polarStep = M_PI / float(polarSegs);
    float r = 1.0;
    vertSize = 0;
    for(unsigned m=0; m < ayimutSegs; m++) {
      for(unsigned n=0; n < polarSegs; n++) {
        float phi = ayimutStep*m;
        float theta = polarStep * n;

        // random radius
        int rval = rand();
        r = 1.0 - 0.3 * (fabs(float(rval))/float(RAND_MAX));

        // create xyz from spherical coordinates
        float x = r * sin(theta) * cos(phi);
        float y = r * sin(theta) * sin(phi);
        float z = r * cos(theta);

        vertexData.push_back(x);
        vertexData.push_back(y);
        vertexData.push_back(z);

        // create vertex indices
        indicesData.push_back(vertSize % totalSegs);
        indicesData.push_back((vertSize+ayimutSegs)%totalSegs);
        indicesData.push_back((vertSize+ayimutSegs+1)%totalSegs);
        indicesData.push_back((vertSize+1)%totalSegs);

        vertSize++;
      }
    }

    // generating vertex VBO
    glGenBuffers(1, &vertBufID);
    glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
    glBufferData(GL_ARRAY_BUFFER, 
                 vertexData.size()*sizeof(float), 
                 &vertexData[0], GL_STATIC_DRAW);

    // generating index VBO
    // generating index VBO
    glGenBuffers(1, &indicesBufID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
                 indicesData.size()*sizeof(int), 
                 &indicesData[0], GL_STATIC_DRAW);

  }


  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(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);

    // activating VBO
    glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
    int stride = 0;
    glVertexPointer(3, GL_FLOAT, stride, NULL);
    glEnableClientState(GL_VERTEX_ARRAY);

    if(mode == 0) {
      glColor3f(1.0f,1.0f,1.0f);
    }else{
      glColorPointer(3, GL_FLOAT, stride, NULL);
      glEnableClientState(GL_COLOR_ARRAY);
    }

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
    glDrawElements(GL_QUADS, vertSize*4, GL_UNSIGNED_INT, NULL);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);

  }
};

Dynamic Update of VBOs

  • If VBOs should be used in a dynamic scene, it is best for fast display when the vertices are static in the VBOs and the motion is realized only by changing the modelview matrix
    • In this case, no new vertex data must be transferred to the GPU
    • This is possible, for example, for the animation of a car
  • However, it often happens that the vertex data must be changed
    • For example, to realize a muscle deformation during the animation of a runner
    • In this case, new data must be transferred to the GPU

Dynamic Update of VBOs

  • Complete:
    • An option for a dynamic update is to transfer the complete data again with
      glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)
    • If this happens often, instead of GL_STATIC_DRAW the parameters GL_DYNAMIC_DRAW should be used
    • When the content of the VBOs changes with each new frame, the parameter GL_STREAM_DRAW can be employed
  • Partial:
    • If only few data within the VBO is affected, it is probably worth to update the new VBO only partially
    • This is done by:
      glBufferSubData(GL_ARRAY_BUFFER, offset, size, data)

Example: Dynamic Update of the Spiky Sphere

opengl_VBO_drawelements

Stride and Start Address

  • Often different vertex attributes (such as position, color, etc.) are not stored separately, but in a common data structure that is created for each vertex, for example:
    struct Vertex
    {
        float position[3];
        float color[4];
        float texCoord[2];
        float normal[3];
    };
        
    std::vector<Vertex> vertexData;
    
  • If vertexData is created as one large VBO, memory access to the individual components is defined by specifying the stride, which is the number of bytes between consecutive elements of the same type, and the start address offset:
    int stride = sizeof(Vertex);
    char* offset = (char*) NULL + (3+4+2)*sizeof(float);
    glNormalPointer(GL_FLOAT, stride, offset);

Stride and Start Address

opengl_VBO_stride

Stride and Start Address

class Renderer {
private:
  struct Vertex
  {
    float position[3];
    float color[4];
    float texCoord[2];
    float normal[3];
  };

public:
  float t;
  int mode;
private:
  GLuint vertBufID;
  GLuint texID;
  int vertNo;

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

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

    // generating VBO input data
    float vertexData[] = {
       0.0f, 0.0f, 2.0f, 1.0f, 0.0f, 0.0f, 1.0f, 
             0.5f, 1.0f, 0.0000f,-0.9701f, 0.2425f,
      -0.5f,-0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 
             0.0f, 0.0f, 0.0000f,-0.9701f, 0.2425f,
       0.5f,-0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 
             1.0f, 0.0f, 0.0000f,-0.9701f, 0.2425f,
       0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 1.0f, 
             0.5f, 1.0f, 0.9701f, 0.0000f, 0.2425f,
       0.5f,-0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 
             0.0f, 0.0f, 0.9701f, 0.0000f, 0.2425f,
       0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 
             1.0f, 0.0f, 0.9701f, 0.0000f, 0.2425f,
       0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 1.0f, 1.0f, 
             0.5f, 1.0f, 0.0000f, 0.9701f, 0.2425f,
       0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 
             0.0f, 0.0f, 0.0000f, 0.9701f, 0.2425f,
      -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 
             1.0f, 0.0f, 0.0000f, 0.9701f, 0.2425f,
       0.0f, 0.0f, 2.0f, 1.0f, 1.0f, 0.0f, 1.0f, 
             0.5f, 1.0f,-0.9701f, 0.0000f, 0.2425f,
      -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 
             0.0f, 0.0f,-0.9701f, 0.0000f, 0.2425f,
      -0.5f,-0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 
             1.0f, 0.0f,-0.9701f, 0.0000f, 0.2425f
    };

    vertNo = 12;

    // generating vertex VBO
    glGenBuffers(1, &vertBufID);
    glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
    glBufferData(GL_ARRAY_BUFFER, vertNo*sizeof(Vertex),
                 vertexData, GL_STATIC_DRAW);

    std::string fileName("checkerboard.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(3.0, -1.0, 4.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);

    // activating VBO
    glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
    int stride = sizeof(Vertex);
    char *offset = (char*)NULL;

    // position
    glVertexPointer(3, GL_FLOAT, stride, offset);
    glEnableClientState(GL_VERTEX_ARRAY);

    // color
    offset = (char*)NULL + 3*sizeof(float);
    glColorPointer(4, GL_FLOAT, stride, offset);
    glEnableClientState(GL_COLOR_ARRAY);

    // texture
    offset = (char*)NULL + (3+4)*sizeof(float);
    glTexCoordPointer(2, GL_FLOAT, stride, offset);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    // normals
    offset = (char*)NULL + (3+4+2)*sizeof(float);
    glNormalPointer(GL_FLOAT, stride, offset);
    glEnableClientState(GL_NORMAL_ARRAY);

    // bind texture
    if(mode == 0) {
      glEnable(GL_TEXTURE_2D);
      glBindTexture(GL_TEXTURE_2D, texID);
    }else{
      glDisable(GL_TEXTURE_2D);
      glBindTexture(GL_TEXTURE_2D, 0);
    }

    // render data
    glDrawArrays(GL_TRIANGLES, 0, vertNo);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisable(GL_TEXTURE_2D);
  }
...
};

Vertex Array Objects

Vertex Array Objects

  • When examining the display() function in the previous example, it is noticeable that in addition to the actual render call with glDrawArrays(...) several additional function calls to glBindBuffer(...), glXXXPointer(...) and glEnableClientState(...) are needed
  • A Vertex Array Object (VAO) encapsulates these calls by storing the states of these additional functions and later restoring them automatically when binding the VAO again
  • A VAO contains only the states, but no vertex data. The vertex data is still stored in the VBOs.

Creating Vertex Array Objects

  • The creation of VAOs follows the usual pattern:
    • Creating of a unique ID with
       glGenVertexArrays(1, &vaoID);
    • Creating a VAO with glBindVertexArray(...) and setting of the VBO states, they are stored to the currently bound VAO
      glBindVertexArray(vaoID);
      glBindBuffer(GL_ARRAY_BUFFER, bufID);
      glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
      glVertexPointer(3, GL_FLOAT, stride, offset);
      glEnableClientState(GL_VERTEX_ARRAY);
      ...

Using Vertex Array Objects

  • The actual rendering is then carried out with only two function calls:
    glBindVertexArray(vaoID);
    glDrawArrays(GL_TRIANGLES, 0, numOfTris);
    
  • Deleting a VAO with:
    glDeleteVertexArrays(1, &vaoID);
    When deleting the VAO its associated VBOs are not deleted. If desired, they must be released separately

Example: Vertex Array Objects

opengl_VAO

Example: Vertex Array Objects

class Renderer {
private:
  struct Vertex
  {
    float position[3];
    float color[4];
  };

public:
  float t;
  int mode;
private:
  enum {Pyramid, Cube, numVAOs};
  enum {PyramidAll, CubePos, CubeCol, CubeIndices, numVBOs};
  GLuint vaoID[numVAOs];
  GLuint bufID[numVBOs];
  int pyramidVertNo;
  int cubeIndicesNo;

public:
  // constructor
  Renderer() : t(0.0), pyramidVertNo(0), cubeIndicesNo(0) {}
  //destructor
  ~Renderer() {
    glDeleteVertexArrays(numVAOs, vaoID);
    glDeleteBuffers(numVBOs, bufID);
  }

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

    // now we create 2 Vertex Array Objects (VAO):
    // one for a pyramid and one for a cube.
    // The pyramid uses one interleaved VBO
    // which is later drawn with "glDrawArrays",
    // while the cube uses 3 separate VBOs
    // which are later drawn with "glDrawElements"

    // create the Vertex Array Objects
    glGenVertexArrays(numVAOs, vaoID);

    // generating Vertex Buffer Objects (VBO)
    glGenBuffers(numVBOs, bufID);

    // specifying the pyramid VAO
    glBindVertexArray(vaoID[Pyramid]);

    float pyramidVertexData[] = {
       0.0f, 0.0f, 2.0f, 1.0f, 0.0f, 0.0f, 0.0f,
      -0.5f,-0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
       0.5f,-0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
       0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 0.0f,
       0.5f,-0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
       0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
       0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 1.0f, 0.0f,
       0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
      -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
       0.0f, 0.0f, 2.0f, 1.0f, 1.0f, 0.0f, 0.0f,
      -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
      -0.5f,-0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
    };
    pyramidVertNo = 12;

    glBindBuffer(GL_ARRAY_BUFFER, bufID[PyramidAll]);
    glBufferData(GL_ARRAY_BUFFER, pyramidVertNo*sizeof(Vertex),
                 pyramidVertexData, GL_STATIC_DRAW);

    int stride = sizeof(Vertex);
    char *offset = (char*)NULL;

    // position
    glVertexPointer(3, GL_FLOAT, stride, offset);
    glEnableClientState(GL_VERTEX_ARRAY);

    // color
    offset = (char*)NULL + 3*sizeof(float);
    glColorPointer(4, GL_FLOAT, stride, offset);
    glEnableClientState(GL_COLOR_ARRAY);

    // specifying the cube VAO
    glBindVertexArray(vaoID[Cube]);

    float cubePosData[] = {
      -1.0f,  1.0f,  1.0f,
      -1.0f, -1.0f,  1.0f,
       1.0f, -1.0f,  1.0f,
       1.0f,  1.0f,  1.0f,
      -1.0f,  1.0f, -1.0f,
      -1.0f, -1.0f, -1.0f,
       1.0f, -1.0f, -1.0f,
       1.0f,  1.0f, -1.0f
    };

    float cubeColorData[] = {
      1.0f, 1.0f, 0.0f,
      1.0f, 0.0f, 0.0f,
      0.0f, 1.0f, 0.0f,
      0.0f, 0.0f, 1.0f,
      1.0f, 1.0f, 0.0f,
      1.0f, 0.0f, 0.0f,
      0.0f, 1.0f, 0.0f,
      0.0f, 0.0f, 1.0f,
    };

    int cubeIndicesData[] =
    { 0, 1, 2, 3,
      0, 4, 5, 1,
      4, 7, 6, 5,
      2, 6, 7, 3,
      1, 5, 6, 2,
      3, 7, 4, 0
    };

    cubeIndicesNo = 24;

    // position
    glBindBuffer(GL_ARRAY_BUFFER, bufID[CubePos]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(cubePosData), 
                 cubePosData, GL_STATIC_DRAW);
    glVertexPointer(3, GL_FLOAT, 0, NULL);
    glEnableClientState(GL_VERTEX_ARRAY);

    // color
    glBindBuffer(GL_ARRAY_BUFFER, bufID[CubeCol]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(cubeColorData), 
                 cubeColorData, GL_STATIC_DRAW);
    glColorPointer(3, GL_FLOAT, 0, NULL);
    glEnableClientState(GL_COLOR_ARRAY);

    // indices
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufID[CubeIndices]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndicesData), 
                 cubeIndicesData, GL_STATIC_DRAW);

  }


  void resize(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (60.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(3.0, -1.0, 4.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0);

    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);
    // bind cube VAO
    glBindVertexArray(vaoID[Cube]);
    // render data
    glDrawElements(GL_QUADS, cubeIndicesNo, 
                   GL_UNSIGNED_INT, NULL);

    glTranslatef(0.0f, 0.0f, 1.0f);
    // bind pyramid VAO
    glBindVertexArray(vaoID[Pyramid]);
    // render data
    glDrawArrays(GL_TRIANGLES, 0, pyramidVertNo);
  }
};

Importing 3D Geometry

Importing 3D Geometry

  • In the VBO examples presented so far, vertex data was either generated randomly or was specified by hand
  • In practice, however, 3D models are typically created in a 3D modeling software, such as Blender, 3ds Max,
    Maya, Cinema 4D, etc.
  • A relatively simple and popular file format for the exchange of 3D models is the Wavefront OBJ format
  • The OBJ file format is supported by almost all 3D graphics programs

Wavefront OBJ Format

  • The OBJ format consists of a text file with lists of 3D positions, texture coordinates, normals and vertex indices
  • A vertex position is specified by "v" followed by 3 float values:
    v 0.6 -2.5 0.1
  • A vertex texture coordinate is specified by "vt" followed by 2 float values:
    vt 0.5 0.75
  • A vertex normal is specified by "vn" followed by 3 float values:
    vn 0.0 -1.0 0.0
  • A quadrilateral (or short "quad") is specified by a "f" followed by 4 vertex descriptions, each consisting of 3 indices:
    f v/vt/vn v/vt/vn v/vt/vn v/vt/vn
    where the respective first index refers to the list of vertices, the second one to the list of texture coordinates, and the third one to the list of normals

Beispiel OBJ-Datei

Example OBJ File

dice
# Wavefront OBJ file example
#
o Cube
v -1.0 1.0 1.0
v -1.0 -1.0 1.0
v 1.0 -1.0 1.0
v 1.0 1.0 1.0
v -1.0 1.0 -1.0
v -1.0 -1.0 -1.0
v 1.0 -1.0 -1.0
v 1.0 1.0 -1.0
# 8 vertices

vt 0.0 0.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 1.0
# 4 texture coordinates

vn 0.0 0.0 1.0
vn -0.0 -0.0 -1.0
vn -1.0 -0.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 -0.0
vn -0.0 1.0 -0.0
# 6 normals

f 1/1/1 2/2/1 3/3/1 4/4/1
f 5/1/2 8/2/2 7/3/2 6/4/2
f 1/1/3 5/2/3 6/3/3 2/4/3
f 2/1/4 6/2/4 7/3/4 3/4/4
f 3/1/5 7/2/5 8/3/5 4/4/5
f 5/1/6 1/2/6 4/3/6 8/4/6
# 6 quads
# indices start with 1 not 0

Wavefront OBJ Format

  • A triangle can be defined by 3 vertex descriptions
    f v/vt/vn v/vt/vn v/vt/vn
  • For a vertex description not all indices need to be specified, it is also possible to have
    • only positions:
      f v v v
    • only positions and texture coordinates:
      f v/vt v/vt v/vt
    • only positions and normals:
      f v//vn v//vn v//vn
  • A complete description of the format:
    Wavefront’s Advanced Visualizer Object Files

ObjToVbo

  • For the examples shown in this lecture, it is not necessary that they contain their own parser for the OBJ format
  • Instead, in the following, we use the tool ObjToVbo, which converts an OBJ file into an array of floats. The array can then be written to a text or binary file.
  • Such a float array can be read with high speed and with little programming effort and can be passed directly to a VBO in OpenGL.
  • Changing the parameter includedPerVertexData[] allows to adjust the composition of the generated float array so that it fits to the required format of the VBOs (details in the source code)
  • Source code of ObjToVbo: ObjToVbo.cpp

Example for the Usage of ObjToVbo

opengl_objtovbo
  • In this example, an OBJ file is converted to a float array that contains the following data structure per vertex:
    struct Vertex
    {
        float position[3];
        float color[4];
        float texCoord[2];
        float normal[3];
    };

Example: OpenGL 3 Core Compatible Program

opengl_firstforward
  • Now, with VAOs and buffer objects an OpenGL program can be created that is compatible with the "Core Profile" and contains no more functions of the "Compatibility Profile"
  • It is possible to render a white triangle. However, even for simple transformations, it would be necessary to generate shader code (see Chapter GLSL)
  • Source code with GLUT: FirstForwardCompatible.cpp
  • Source code with Qt: FirstForwardCompatibler.cpp
  • Source code with Java: FirstForwardCompatible.java

Example: OpenGL 3 Core Compatible Program

class Renderer {
private:
  struct Vertex {
    float position[3];
    float color[4];
  };

public:
  float t;
private:
  enum {Triangle, numVAOs};
  enum {TriangleAll, numVBOs};
  GLuint vaoID[numVAOs];
  GLuint bufID[numVBOs];
  int triangleVertNo;
  int cubeIndicesNo;

public:
  // constructor
  Renderer() : t(0.0), triangleVertNo(0), cubeIndicesNo(0) {}
  //destructor
  ~Renderer() {
    glDeleteVertexArrays(numVAOs, vaoID);
    glDeleteBuffers(numVBOs, bufID);
  }

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

    // create a Vertex Array Objects (VAO)
    glGenVertexArrays(numVAOs, vaoID);

    // generate a Vertex Buffer Object (VBO)
    glGenBuffers(numVBOs, bufID);

    // binding the Triangle VAO
    glBindVertexArray(vaoID[Triangle]);

    float triangleVertexData[] = {
       0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
      -0.5f,-0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
       0.5f,-0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
    };
    triangleVertNo = 3;

    glBindBuffer(GL_ARRAY_BUFFER, bufID[TriangleAll]);
    glBufferData(GL_ARRAY_BUFFER, triangleVertNo*sizeof(Vertex),
                 triangleVertexData, GL_STATIC_DRAW);

    int stride = sizeof(Vertex);
    char *offset = (char*)NULL;

    // position
    int vertexLoc = 0;
    glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, GL_FALSE, 
                          stride, offset);
    glEnableVertexAttribArray(vertexLoc);
  }


  void resize(int w, int h) {
    glViewport(0, 0, w, h);
  }

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

    // bind Triangle VAO
    glBindVertexArray(vaoID[Triangle]);
    // render data
    glDrawArrays(GL_TRIANGLES, 0, triangleVertNo);
  }
};

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)