io7m | single-page | multi-page | archive (zip, signature)
5. Vertex Buffer ObjectsA Brief History Of Vertex Specification In OpenGL 7. Examples
PreviousUpNext

Modern Vertex Buffer Objects
At the time of writing, OpenGL 4.2 is the most recent revision of the specification. Prior to 3.0, the specifications were backwards compatible with each version having strictly more features than the last. As stated previously, the programming models supported by the early revisions of OpenGL do not reflect how modern graphics hardware works at all. Starting with 3.0, all methods of vertex specification other than vertex buffer objects have been deprecated/removed.
With all modern graphics hardware now supporting a fully programmable pipeline, the concept of a "vertex buffer" has essentially been generalized to a "vertex attribute buffer". In other words, programs no longer use functions such as glVertexPointer() to mark sections of an array as containing "vertex position data", or glColorPointer() to mark sections of an array as "colour data". Indeed, these functions are also deprecated and do not exist in versions of OpenGL beyond 3.0. OpenGL essentially no longer has any concept of "the colour of a vertex" or "the position of the vertex"!
Now, of course, the previous statement seems nonsensical. How would it be possible to produce 3D graphics in a system that could not understand "the position of a vertex"? Essentially, OpenGL now leaves the interpretation of buffered data up to programs written by the programmer that execute directly on the GPU. The programs are written in GLSL ("GL Shading Language") and a full tutorial is out of the scope of this document. In other words, where "vertex" and "colour" data (etc.) served as input for a fixed-function rendering pipeline, data is now treated generically and serves as input to a programmable rendering pipeline.
With "modern" OpenGL, a simple example is rather difficult to write without supporting code, as most of the functionality has been delegated to external libraries. The matrix stack, for example, no longer exists and must be provided by third party matrix libraries. In the following example, a few deprecated features are used for the sake of simplicity (such as the immediate mode glTranslate() function) (source):
#define GL_GLEXT_PROTOTYPES 1

#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <GL/glut.h>
#include <GL/glext.h>

typedef float vector4[4];

typedef struct
{
  vector4 position;
  vector4 colour;
} vertex;

static GLuint vertices_name;
static GLuint triangles[2];

static void
init_data()
{
  vertex *data = malloc(4 * sizeof(vertex));
  if (data == NULL) abort();
  
  data[0].position[0] = 0.0;
  data[0].position[1] = 0.0;
  data[0].position[2] = 0.0;
  data[0].position[3] = 1.0;
  data[0].colour[0] = 1.0;
  data[0].colour[1] = 0.0;
  data[0].colour[2] = 0.0;
  data[0].colour[3] = 1.0;
  
  data[1].position[0] = 100.0;
  data[1].position[1] = 0.0;
  data[1].position[2] = 0.0;
  data[1].position[3] = 1.0;
  data[1].colour[0] = 0.0;
  data[1].colour[1] = 1.0;
  data[1].colour[2] = 0.0;
  data[1].colour[3] = 1.0;
 
  data[2].position[0] = 100.0;
  data[2].position[1] = 100.0;
  data[2].position[2] = 0.0;
  data[2].position[3] = 1.0;
  data[2].colour[0] = 0.0;
  data[2].colour[1] = 0.0;
  data[2].colour[2] = 1.0;
  data[2].colour[3] = 1.0;
 
  data[3].position[0] = 0.0;
  data[3].position[1] = 100.0;
  data[3].position[2] = 0.0;
  data[3].position[3] = 1.0;
  data[3].colour[0] = 1.0;
  data[3].colour[1] = 1.0;
  data[3].colour[2] = 0.0;
  data[3].colour[3] = 1.0;
 
  glGenBuffers(1, &vertices_name);
  glBindBuffer(GL_ARRAY_BUFFER, vertices_name);
  glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(vertex), data, GL_STATIC_DRAW);

  free(data);

  glGenBuffers(2, triangles);

  {
    unsigned short indices[] = { 0, 1, 2 };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triangles[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 
  }

  {
    unsigned short indices[] = { 1, 2, 3 };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triangles[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 
  }

  assert(glGetError() == GL_NO_ERROR);
}

static GLuint shader_vertex;
static GLuint shader_fragment;
static GLuint shader_program;

static const char *shader_vertex_source[] = {
  "#version 110\n",
  "\n",
  "attribute vec4 position_data;\n",
  "attribute vec4 colour_data;\n",
  "\n",
  "void main()\n",
  "{\n",
  "  gl_Position   = gl_ProjectionMatrix * gl_ModelViewMatrix * position_data;\n",
  "  gl_FrontColor = colour_data;\n",
  "}\n"
};

static const char *shader_fragment_source[] = {
  "#version 110\n",
  "\n",
  "void main()\n",
  "{\n",
  "  gl_FragColor = gl_Color;\n",
  "}\n"
};

static void
init_shaders(void)
{
  GLint ok;
  char shader_log[8192];

  /*
   * Compile vertex shader.
   */

  shader_vertex = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(
    shader_vertex,
    sizeof(shader_vertex_source) / sizeof(const char *),
    shader_vertex_source,
    NULL);
  glCompileShader(shader_vertex);
  glGetShaderiv(shader_vertex, GL_COMPILE_STATUS, &ok);
  if (ok == GL_FALSE) {
    GLsizei size;
    glGetShaderInfoLog(shader_vertex, sizeof(shader_log), &size, shader_log);
    fprintf(stderr, "fatal: vertex shader:\n%s\n", shader_log);
    exit(1);
  }

  /*
   * Compile fragment shader.
   */

  shader_fragment = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(
    shader_fragment,
    sizeof(shader_fragment_source) / sizeof(const char *),
    shader_fragment_source,
    NULL);
  glCompileShader(shader_fragment);
  glGetShaderiv(shader_fragment, GL_COMPILE_STATUS, &ok);
  if (ok == GL_FALSE) {
    GLsizei size;
    glGetShaderInfoLog(shader_fragment, sizeof(shader_log), &size, shader_log);
    fprintf(stderr, "fatal: fragment shader:\n%s\n", shader_log);
    exit(1);
  }

  /*
   * Link shading program.
   */

  shader_program = glCreateProgram();
  glAttachShader(shader_program, shader_vertex);
  glAttachShader(shader_program, shader_fragment);
  glLinkProgram(shader_program);

  glGetProgramiv(shader_program, GL_LINK_STATUS, &ok);
  if (ok == GL_FALSE) {
    GLsizei size;
    glGetShaderInfoLog(shader_program, sizeof(shader_log), &size, shader_log);
    fprintf(stderr, "fatal: %s\n", shader_log);
    exit(1);
  }

  assert(glGetError() == GL_NO_ERROR);
}

static void
init(void)
{
  init_data();
  init_shaders();
}

static void
reshape(int width, int height)
{
  glViewport(0, 0, width, height);
 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, width, 0, height, -1.0, 100.0);

  assert(glGetError() == GL_NO_ERROR);
}

static void
display(void)
{
  glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  {
    const int position_attrib = glGetAttribLocation(shader_program, "position_data");
    const int colour_attrib = glGetAttribLocation(shader_program, "colour_data");
    assert (position_attrib != -1);
    assert (colour_attrib != -1);

    glBindBuffer(GL_ARRAY_BUFFER, vertices_name);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triangles[0]);

    glEnableVertexAttribArray(position_attrib);
    glVertexAttribPointer(position_attrib, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), 0);
    glEnableVertexAttribArray(colour_attrib);
    glVertexAttribPointer(colour_attrib, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (void *) offsetof(vertex, colour));

    glUseProgram(shader_program);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *) 0);

    glTranslated(120.0, 120.0, 0.0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triangles[1]);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *) 0);

    glUseProgram(0);
    glDisableVertexAttribArray(position_attrib);
    glDisableVertexAttribArray(colour_attrib);
  }

  assert(glGetError() == GL_NO_ERROR);

  glutSwapBuffers();
}

int
main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutCreateWindow("Modern GL triangles");
  init();
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutIdleFunc(glutPostRedisplay);

  glutMainLoop();
  return 0;
}
      
The preceding program uses custom vertex and fragment shaders. The vertex shader defines two attributes, named position_data and colour_data, respectively. The program uses glVertexAttribPointer to specify that position_data takes input from the buffered vertex data in the same manner that glVertexPointer did in the previous programs. It also uses glVertexAttribPointer to specify that colour_data takes input from the buffered vertex data in the same manner that glColorPointer did in the previous programs. As stated before, the interpretation of the data is left up to the writer of the shading program that runs on the GPU. In the vertex and fragment shaders above, the position data is multiplied by the modelview and projection matrices and the colour data is interpolated and used in the fragment shader. The precise details of these programs are better described by any of the GLSL tutorials (or the official GLSL book).

PreviousUpNext
5. Vertex Buffer ObjectsA Brief History Of Vertex Specification In OpenGL 7. Examples