Understanding OpenGL Objects

Introduction

OpenGL Objects are structures composed of states and data and are responsible for transmitting data to and from the GPU. OpenGL objects must first be created and bound before they can be used. There are several types of OpenGL Objects. For example, a Vertex Buffer Object can store vertices of a character. Whereas a Texture, another kind of OpenGL Object, can store image data.

OpenGL Buffer.jpeg

Creation and deletion of an OpenGL object

An OpenGL object can be created, deleted and bound to the OpenGL context. The functions responsible for these operations are of the form:

  • glGen
  • glDelete
  • glBind

Types of OpenGL Objects?

Objects in OpenGL can be separated into two different categories:

Regular Objects

These type of objects contain data. A list of regular objects are:

  • Buffer Objects
  • Renderbuffer Objects
  • Texture Objects
  • Query Objects
  • Sampler Objects

Container Objects

As the name implies, these type of objects do not contain any data. Instead they are containers for regular openGL objects. A list of container objects are:

  • Framebuffer Objects
  • Vertex Array Objects
  • Transform Feedback Objects
  • Program Pipeline Objects

Creating an OpenGL Object

Before an OpenGL object can be used, it must be created and bound to the OpenGL context. The general format syntax for creating an OpenGL object is:

void glGenObjectType(GLsizei n, GLuint *objects);

where:

ObjectType represents the OpenGL object type.

This function generates n objects of the given type, storing them in the array given by the objects parameter.

glGen() only creates the object's name, i.e., the reference to the object. It does not allocate storage.

Example:
//Name of buffer object
GLuint bufferName;
//Create an OpenGL object of BUFFER type
glGenBuffers(1, &bufferName);

An object name is always of GLuint type. These names are not pointers. They are references, numbers that identify an object.

Binding an OpenGL Object

To use or modify an OpenGL object, it must be bound to an OpenGL context. The general format syntax for binding an OpenGL object is:

void glBindObjectType(GLenum target, GLuint object);

where:

  • ObjectType represents the OpenGL object type.

  • target, also known as a binding point, could be GL_ARRAY_BUFFER, GL_TEXTURE_BUFFER, etc.

  • object is the OpenGL object you want to bind.

Unlinke glGen() which creates only a reference to the object, glBind() allocates storage for the object.

Example:
//Name of buffer object
GLuint bufferName;

//Create an OpenGL object of BUFFER type
glGenBuffers(1, &bufferName);

//Bind a OpenGL Buffer Object to the context
glBindBuffer(GL_ARRAY_BUFFER, bufferName);

Note: if an object is bound to a location where another object is already bound, the previously bound object will be unbound.

What is a Target in OpenGL?

Targets, also known as Binding Points, allows objects to be used for different purposes. A target specifies the behavior of the object.

The most common binding points are:

  • GL_ARRAY_BUFFER
  • GL_TEXTURE_BUFFER
  • GL_ELEMENT_ARRAY_BUFFER

Deleting an OpenGL Object

If an OpenGL object is no longer of use. It should be deleted. The general format syntax for deleting an OpenGL object is:

void glDeleteObjectType(GLsizei n, const GLuint *objects);

where:

ObjectType represents the OpenGL object type.

Example:
//Name of buffer object
GLuint bufferName;

//...Create the Buffer object...

//...Bind the Buffer object...

//...Once an object is of no use, delete it...

//Delete an OpenGL object of BUFFER type.
glDeleteBuffers(1, &bufferName);

Buffer Objects

A Buffer object inherits the states and functionalities from an OpenGL object. A buffer object is an OpenGL object that contains unformatted data. Buffer objects are stored in the GPU (Graphics Processing Unit), which provides fast and efficient access.

Behavior of OpenGL Buffer Objects

The behavior of a buffer object depends on its binding points. For example, when a buffer object's binding point is GL_ARRAY_BUFFER, it behaves as a Vertex Buffer Object. This type of objects are mainly used to store vertex data of characters. When a buffer object's binding point is set to GL_PIXEL_PACK_BUFFER, it behaves as a Pixel Buffer Object; mainly used to read data from framebuffers.

A binding point is also known as targets. As stated above, it allows buffers to be used for different purposes.

The different buffer binding points are:

  • GL_ARRAY_BUFFER
  • GL_COPY_READ_BUFFER
  • GL_COPY_WRITE_BUFFER
  • GL_ELEMENT_ARRAY_BUFFER
  • GL_PIXEL_PACK_BUFFER
  • GL_PIXEL_UNPACK_BUFFER
  • GL_TEXTURE_BUFFER
  • GL_TRANSFORM_FEEDBACK_BUFFER
  • GL_UNIFORM_BUFFER

Vertex Buffer Object

A Vertex Buffer Object (VBO) is a Buffer Object. As stated before, the behavior of a buffer object depends on its binding points. When an buffer object's binding point is set to GL_ARRAY_BUFFER, the buffer object behaves as a Vertex Buffer Object.

A VBO is a buffer object that represents storage for vertex data. These data can be a characters's vertex, normal, texture coordinate, etc.

Why is a VBO necessary?

OpenGL can't read vertex data in CPU memory. Vertex data must be sent to the GPU before it can be used in the rendering pipeline. A VBO is able to take the data stored in the CPU and transmit it to the GPU.

Example:

Create a VBO:

GLint myBuffer;
    //Create a buffer
    glGenBuffer(1, &myBuffer);

For a VBO to be able to store and retrieve vertex data, it must be bound to the GL_ARRAY_BUFFER binding point:

glBindBuffer(GL_ARRAY_BUFFER, myBuffer);

Once a VBO is bound, functions that retrieve and store data into the VBO can be used.

Example:

The function glBufferData(...) stores vertex data into a VBO.

glBufferData(GL_ARRAY_BUFFER,sizeof(data), data, GL_STATIC_DRAW);

The glBufferData requires a binding point. This is usually the same binding point when the object was bound, i.e. GL_ARRAY_BUFFER.

Since data in a VBO will be used to render a polygon on a screen. OpenGL wants to know how these data is going to change over time. Is it going to change constantly? rarely? These are hints that OpenGL use to make best use of the data. Some of these hints are:

  • GL_STATIC_DRAW: Data will rarely change.
  • GL_DYNAMIC_DRAW: Data will change constantly.

In the example above, we are telling OpenGL that the data in the VBO will rarely change.

OpenGL Shaders

In a nutshell, OpenGL Shaders is a pair of programs that lives in the GPU. They are in charge of rendering the polygon mesh onto the screen.

Even though a VBO lives in the GPU, there is no connection between a VBO and a Shader program. This connection is establish through the function glVertexAttribPointer(). The glVertexAttribPointer() informs the shaders where to get the data found in the VBO and how that data should be interpret.

Example:
//Data representing Position of a polygon    

glVertexAttribPointer(location_of_position_attribute,3,GL_FLOAT,GL_FALSE,0,0);

The first paremeter informs OpenGL which Attribute in the shader to assign this data to.

Normally, data stored in a VBO is in vector format. For example, a polygon contains these type of data:

  • Position: represented as (x, y, z)

  • Normal: represented as (n1, n2 ,n3)

  • Texture: represented as (u, v)

Since our example above requires positional data, the second parameter is set to 3. This represents the x, y and z coordinate.

The third parameter requires the vertex data type. In this case, the data type is GL_FLOAT. The fourth parameter states if the data is normalized or not. The fifth parameter specify any strides.

The sixth parameter is the offset to our data in the buffer.

Vertex Array Objects

A Vertex Array Object (VAO) is an OpenGL object. However, unlike a Buffer Object which contains storage, a VAO does not. Instead, a VAO is a container for Vertex Buffer objects (VBO).

Why use a VAO?

In the last section, the function glVertexAttribPointer() provided all the necessary information that a shader would need regarding a vertex-attribute. This information answered questions like:

  • What is the data type?
  • Is there any stride?
  • Is the data normalize?
  • What is the offset to the data?

Let's say that you need to render 12 different characters each with their own data type, offset, etc. All of this information must be prepared before it is rendered. This is a lot of states to set and a lot of error checking that the driver will have to do.

This is where a VAO can help organize all of this information. A VAO is a container that stores all the states needed for rendering. It stores the information of vertex-attribute as well as the buffer object.

Harold Serrano

Computer Graphics Enthusiast. Currently developing a 3D Game Engine.