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$

Buffer Objects

  • Bisher wurden zum Zeichnen von Grafikprimitiven (wie Polygone, Linien oder Punkte) sehr viele Funktionsaufrufe benötigt
    ...
    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();
    ...
    
  • Mit jedem Funktionsaufruf werden nur sehr wenige Daten an die OpenGL-Pipeline übergeben

Buffer Objects

  • Diese Art der Grafikprogrammierung führt zu langsamen Verarbeitungsgeschwindigkeiten, da die Grafikhardware die Operationen nicht parallel auf einer großen Anzahl von Daten auszuführen kann, sondern ständig auf die Daten warten muss
  • Deshalb werden die entsprechenden Funktionen (glBegin, glVertex, etc.) seit OpenGL >= 3.1 nur noch im "Compatibility Profile" unterstützt
  • Stattdessen werden Vertex-Daten nun blockweise übergeben und auf der Grafikkarte als Buffer Objects gespeichert und verwaltet

Vertex Buffer Objects (VBO)

Erstellen von VBOs in OpenGL

  • Das Erstellen eines VBOs geschieht in der Regel vor dem eigentlichen Rendern, also z.B. in der Funktion init()
  • Das Vorgehen ist analog zur Erzeugung einer Textur und umfasst drei Schritte:
    • Schritt 1: Mit Hilfe der Funktion glGenBuffers(1, &bufID) kann ein eindeutiger Kennzeichner generiert werden
    • Schritt 2: Mit glBindBuffer(GL_ARRAY_BUFFER, bufID) wird ein neues VBO erzeugt. Mögliche Ziele sind GL_ARRAY_BUFFER oder GL_ELEMENT_ARRAY_BUFFER
    • Schritt 3: Mit glBufferData(GL_ARRAY_BUFFER, size, inData, GL_STATIC_DRAW) werden die Vertex-Daten übergeben

Aktivierten von VBOs in OpenGL

  • Soll der VBO zum Rendern verwendet werden, muss dieser aktiviert werden. Dies umfasst ebenfalls drei Schritte:
    • Schritt 1: Binden des Buffers mit glBindBuffer(GL_ARRAY_BUFFER, bufID)
    • Schritt 2: Definieren einer Schrittweite und einer Startadresse für den Speicherzugriff
    • Schritt 3: Aktivieren des Buffers für eine bestimmte Verwendung mit glEnableClientState(GL_VERTEX_ARRAY). Mögliche Parameter sind: GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_SECONDARY_COLOR_ARRAY, GL_INDEX_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY usw.

Rendern mit VBOs in OpenGL

  • Nach Aktivierung des VBOs wird das Rendern aller enthaltenen Daten mit einem einzigen Funktionsaufruf angestoßen:
    glDrawArrays(GL_POLYGON, first, count);
    Mögliche Argumente sind die gleichen wie bei glBegin(...), wie z.B. GL_POLYGON, GL_LINE_LOOP, GL_LINES, GL_POINTS, usw.
  • Wenn der VBO nicht mehr verwendet wird, kann der Speicher mit glDeleteBuffers(1, &bufID) wieder freigegeben werden

Beispiel: Rendern einer Kugel mit sehr vielen Punkten

opengl_VBO_drawarrays

Beispiel: Rendern einer Kugel mit sehr vielen Punkten

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

Verwendung von Vertex-Indizes

  • Beim Zeichnen von polygonalen Oberflächen kommt es häufig vor, dass Stützpunkte mehrfach für benachbarte Polygone verwendet werden
  • Sollen die Stützpunktkoordinaten nur einmal abgelegt werden, können Polygone durch die Indizes der Stützpunkte definiert werden

Verwendung von Vertex-Indizes

dice
  • Beispiel für einen Würfel:
    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
    };
    

Verwendung von Vertex-Indizes mit VBOs

  • Zum Verwenden von Vertex-Indizes mit VBOs werden in der Regel zwei VBOs definiert, eins mit Vertex-Daten und ein anderes mit Index-Daten
  • Ein VBO mit Index-Daten wird mit glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufID) erzeugt und aktiviert
  • Das Rendern erfolgt dann mittels der Funktion
    glDrawElements(GL_POLYGON, count, GL_UNSIGNED_INT);

Beispiel: Rendern einer stacheligen Kugel mit Vertex-Indizes

opengl_VBO_drawelements

Beispiel: Rendern einer stacheligen Kugel mit Vertex-Indizes

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

  }
};

Dynamische Aktualisierung von VBOs

  • Sollen VBOs in einer dynamischen Szene verwendet werden, ist es für eine schnelle Darstellung am besten, wenn die Vertex-Daten in den VBOs statisch sind und eine Dynamik lediglich durch die Veränderung der Modelview-Matrix realisiert wird
    • In diesem Fall müssen keine neuen Vertex-Daten auf die GPU übertragen werden
    • Möglich ist dies z.B. bei der Animation eines Autos
  • Häufig kommt es jedoch vor, dass die Vertex-Daten verändert werden müssen
    • Z.B. um eine Muskelverformung bei einer Laufanimation zu realisieren oder ähnliches
    • In diesem Fall müssen neue Daten auf die GPU übertragen werden

Dynamische Aktualisierung von VBOs

  • Komplett:
    • Eine Option für eine dynamische Aktualisierung ist, mittels
      glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)
      die kompletten Daten neu zu übergeben
    • Geschieht dies häufig, sollte anstatt GL_STATIC_DRAW der Parameter GL_DYNAMIC_DRAW angegeben werden
    • Ändert sich der Inhalt des VBOs bei jedem neuen Bild, bietet sich der Parameter GL_STREAM_DRAW an
  • Teilweise:
    • Sind nur wenige Daten innerhalb des VBO von der Änderung betroffen, lohnt es sich wahrscheinlich den VBO nur teilweise neu zu übergeben
    • Dies geschieht mittels
      glBufferSubData(GL_ARRAY_BUFFER, offset, size, data)

Beispiel: Dynamische Veränderung der stacheligen Kugel

opengl_VBO_drawelements

Schrittweite und Startadresse

  • Häufig werden verschiedene Vertex-Attribute (wie Position, Farbe, usw.) nicht separat gespeichert, sondern in einer gemeinsamen Datenstruktur, die pro Vertex angelegt wird, beispielsweise:
    struct Vertex
    {
        float position[3];
        float color[4];
        float texCoord[2];
        float normal[3];
    };
        
    std::vector<Vertex> vertexData;
    
  • Wird vertexData als ein großes VBO angelegt, so muss der Speicherzugriff auf die einzelnen Komponenten durch Angabe der Schrittweite stride und der Startadresse offset definiert werden, z.B.:
    int stride = sizeof(Vertex);
    char* offset = (char*) NULL + (3+4+2)*sizeof(float);
    glNormalPointer(GL_FLOAT, stride, offset);

Beispiel: Schrittweite und Startadresse

opengl_VBO_stride

Beispiel: Schrittweite und Startadresse

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

  • Wenn die display() Funktion des letzten Beispiels betrachtet wird, fällt auf, dass neben des eigentlichen Renderaufrufs mittels glDrawArrays(...) zusätzlich noch mehrere Funktionsaufrufe von glBindBuffer(...), glXXXPointer(...) und glEnableClientState(...) benötigt werden, um ein Objekt darzustellen
  • Ein Vertex Array Object (VAO) kapselt die Aufrufe indem die Zustände dieser zusätzlichen Funktionen gespeichert werden und später durch das Binden des VAOs automatisch wieder hergestellt werden
  • Ein VAO einhält nur die Zustände, aber keine Vertex-Daten. Die Vertex-Daten sind weiterhin in den VBOs abgelegt

Erstellen von Vertex Array Objects

  • Das Erstellen von VAOs folgt dem üblichen Schema:
    • Erzeugen einer eindeutigen ID mit
       glGenVertexArrays(1, &vaoID);
    • Erzeugen des VAO mit glBindVertexArray(...) und Setzen der VBO Zustände, diese werden vom aktuell gebundenen VAO gespeichert
      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);
      ...

Verwenden von Vertex Array Objects

  • Das eigentliche Rendern erfolgt dann mit nur zwei Funktionsaufrufen:
    glBindVertexArray(vaoID);
    glDrawArrays(GL_TRIANGLES, 0, numOfTris);
    
  • Löschen eines VAOs mit:
    glDeleteVertexArrays(1, &vaoID);
    Durch Löschen des VAOs werden die zugehörigen VBOs nicht gelöscht. Falls gewünscht, müssen diese separat freigegeben werden.

Beispiel: Vertex Array Objects

opengl_VAO

Beispiel: 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);
  }
};

Importieren von 3D-Geometrie

Importieren von 3D-Geometrie

  • Bisher wurden in den Bespielen die VBO Daten entweder per Zufall erzeugt oder per Hand angegeben
  • In der Praxis werden 3D-Modelle jedoch typischerweise in einer 3D-Modellier-Software erstellt, wie z.B. Blender,
    3ds Max, Maya, Cinema 4D, etc.
  • Ein relativ einfaches und beliebtes Dateiformat zum Austausch von 3D-Modellen ist das Wavefront OBJ Format
  • Das OBJ-Dateiformat wird von fast allen 3D-Grafikprogrammen unterstützt

Wavefront OBJ Format

  • Das OBJ Format besteht aus einer Textdatei in der Listen von 3D-Positionen, Texturkoordinaten, Normalen und Vertex-Indizes stehen
  • Angabe einer Vertex-Position durch "v" gefolgt von 3 Float-Werten:
    v 0.6 -2.5 0.1
  • Angabe einer Vertex-Texturkoordinate durch "vt" gefolgt von 2 Float-Werten:
    vt 0.5 0.75
  • Angabe einer Vertex-Normalen durch "vn" gefolgt von 3 Float-Werten:
    vn 0.0 -1.0 0.0
  • Angabe eines Viereck (engl. "quadrilateral" oder kurz "quad") durch "f" gefolgt von 4 Vertex-Beschreibungen, die jeweils aus 3 Indizes bestehen:
    f v/vt/vn v/vt/vn v/vt/vn v/vt/vn
    Dabei beziehen sich der jeweils erste Index auf die Liste der Positionen, der zweite auf die Liste der Texturkoordinaten und der dritte auf die Liste der Normalen

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

  • Ein Dreieck kann durch 3 Vertex-Beschreibungen definiert werden
    f v/vt/vn v/vt/vn v/vt/vn
  • Bei einer Vertex-Beschreibung müssen nicht alle Indizes angegeben werden, möglich ist auch
    • nur Positionen:
      f v v v
    • nur Positionen und Texturkoordinaten:
      f v/vt v/vt v/vt
    • nur Positionen und Normalen:
      f v//vn v//vn v//vn
  • Eine vollständige Beschreibung des Formats:
    Wavefront’s Advanced Visualizer Object Files

ObjToVbo

  • Für die Beispiele aus der Vorlesung ist es nicht notwendig, dass diese einen eigenen Parser für das OBJ-Format enthalten
  • Stattdessen wird im Folgenden das Werkzeug ObjToVbo verwendet, das eine OBJ-Datei in ein Array von Floats umwandelt. Das Array kann anschließend in eine Text- oder Binärdatei geschrieben werden.
  • Ein solches Float-Array kann schnell und mit wenig Programmieraufwand gelesen und direkt an ein VBO in OpenGL übergeben werden.
  • Dabei erlaubt ObjToVbo durch ändern des Parameters includedPerVertexData[] das erzeugte Float-Array so anzupassen, dass es dem jeweils benötigten Format des VBOs entspricht (Details im Quelltext)
  • Quelltext von ObjToVbo: ObjToVbo.cpp

Beispiel für die Verwendung von ObjToVbo

opengl_objtovbo
  • In diesem Beispiel wird eine OBJ-Datei in ein Float-Array umgewandelt, das pro Vertex folgende Daten enthält:
    struct Vertex
    {
        float position[3];
        float color[4];
        float texCoord[2];
        float normal[3];
    };

Beispiel: OpenGL 3 Core konformes Programm

opengl_firstforward
  • Mit VAOs oder Buffer Objekten kann nun ein OpenGL Programm erstellt werden, dass dem "Core Profile" entspricht und damit keine Funktionen des "Compatibility Profile" mehr enthält
  • Zwar kann ein Dreieck gerendert werden, jedoch bereits für einfache Transformationen wäre es notwendig, Shader-Code zu erzeugen (siehe dazu Kapitel GLSL)
  • Quelltext mit GLUT: FirstForwardCompatible.cpp
  • Quelltext mit Qt: FirstForwardCompatibler.cpp
  • Quelltext mit Java: FirstForwardCompatible.java

Beispiel: OpenGL 3 Core konformes Programm

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

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)