Go to OpenGL Home

Last time we talked about VBO and VAO which are required to set our vertices in the GPU memory. Finally it’s time to see how to connect these vertex attributes (positions, normals, colors) with vertex shader inputs.

In this tutorial we will cover the following topics:

  1. Connect vertex attributes (position) with attributes from vertex shader
  2. Modify our vertex shader
  3.  GameModels.cpp source code & Draw triangle

1. Tying attributes

We have to tie together our vertex attributes (in our case position from VertexFormat) from OpenGL with attributes from vertex shader. There are a few ways to do this. In modern OpenGL is recommended to use Layout Qualifiers. This method uses qualifiers to tie our attributes together. You can also see them as pipes where data flows from buffers to vertex shaders or just as simple indices. As we discussed earlier, to set this in OpenGL we will have to use glVertexAttribPointer(), glEnableVertexAttribArray() methods.

 unsigned int vao;           //our VAO handler
 glGenVertexArrays(1, &vao); //create VAO container and get ID for it
 glBindVertexArray(vao);     //bind to OpenGL context

 unsigned int vbo;                   //our vbo handler
 glGenBuffers(1, &vbo);
 glBindBuffer(GL_ARRAY_BUFFER, vbo);//bind to context
 glBufferData(GL_ARRAY_BUFFER, sizeof(VertexFormat) * vertices.size(), &vertices[0], GL_STATIC_DRAW);

 //buffer is binded to context,set pipes:)
 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0); //Tell OpenGL about our data format


In the example above:

  •  glEnableVertexAttribArray sets qualifier(pipe) 0  as available and ready to be filled with data.
  • glVertexAttribPointer : on qualifier 0 (first param), send 3 (second param), floats (third param) non normalized (forth param), knowing that the next vertex appears every sizeof(VertexFormat) (fifth param, called stride) and the offset of the first element from VertexFormat (x from position) is 0.

Qualifier is an unsigned int, activated with first method and used next by the second method.

A few considerations that are good to know before we move on:

Notice that we use interleaved vertex attributes. Beside position, our VertexFormat structure could have more attributes like color, texture coordinates, normals etc. In a interleaved mode, all we have to do is to set the offsets right. In our case with the triangle, we only have the position attribute, so we set the offset to 0. Do not get scared with conversion to (void*). This is required by the glVertexAttribPointer method which remained unchanged in time from pervious versions of OpenGL. All you have to do is pass the offset number.

Beside Interleaved attributes, you can use separate buffers, where for example you have a buffer for colors and a buffer for positions. There are huge debates from a performance point of view on which technique is better. The recommendation is to use interleaved attributes because vertices located one after the other and can be feteched more efficiently.

However there are times when some attributes will change often and some attributes will rarely change. In this case is better to use separate buffers. For example, if you have to draw an ocean, the color remains the same throughout the simulation but the positions of vertices in buffer need to change to simulate wind effect based on math formulas like Gerstner wave functions.

From my experience I can tell you that these optimizations will not gain huge performance on modern hardware and I’m pretty sure that the bottleneck of your program is somewhere else. So don’t waste your time here.

2. Modify our vertex shader

Now it’s time to talk about vertex processing which is done in the first stage of the pipeline by vertex shader. We already talked about shaders in a past tutorial and you should have a minimal idea on how they work.

It’s important to know that one call of the vertex shader will process just one vertex at the time. And at drawing time, the number of invocation to the vertex shader is done by the GPU and not by you. Also you don’t have just one vertex shader on your GPU. Now you can see why we done all those vertex data preparations above.

Shaders can be seen as black boxes. We give them a set of inputs and we get a sets of outputs. Vertex shader is no exception. We set our vertex attributes (position, colors, normals etc) as inputs, vertex shader process them and we get an output position in clip space, which is divided by w to get the final normalized device coordinates(NDC).

#version 450 core                           //lower your version if GLSL 4.5 is not supported by your GPU
layout(location = 0) in vec3 in_position;  //set the frist input on location (index) 0 ; in_position is our attribute 

void main()
  gl_Position = vec4(in_position, 1.0);//w is 1.0, also notice cast to a vec4

Now we can see that how vertex attributes from  OpenGL are tying together with input of vertex shader. Note that location is set to 0, the same parameter that we set  for glVertexAttribPointer(), glEnableVertexAttribArray() methods. And the type of input is a vec3. Just like our position attribute from VertexFormat structure. vec3 are just 3 float numbers.

In the diagram below I tried to show how OpenGL – Vertex tying attributes flow. I drew a  super simplified GPU structure with just a controller, a memory and a vertex shader.

Simplified GPU

Simplified GPU. Notice that this schema is abstract and is meant  to show just a flow. A real GPU is far more complicated and has  more components than this. I didn’t put any cache memory, cores,  thread scheduler, peripherials etc. If you are interested in a GPU architecture you can begin reading this :


 3. GameModels.cpp source code & Draw triangle 

Now let’s finish our GameModels.cpp implementation.

#include "GameModels.h"
using namespace Models;




  std::map<std::string, Model>::iterator it;
  for (it = GameModelList.begin(); it != GameModelList.end(); ++it)
    //delete VAO and VBOs (if many)
    unsigned int* p = &it->second.vao;
    glDeleteVertexArrays(1, p);
    glDeleteBuffers(it->second.vbos.size(), &it->second.vbos[0]);

void GameModels::CreateTriangleModel(const std::string& gameModelName)
   unsigned int vao;
   unsigned int vbo;

   glGenVertexArrays(1, &vao);

   std::vector<VertexFormat> vertices;//our vertex positions
   vertices.push_back(VertexFormat(glm::vec3( 0.25, -0.25, 0.0)));
   vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, 0.0)));
   vertices.push_back(VertexFormat(glm::vec3( 0.25, 0.25, 0.0)));

   glGenBuffers(1, &vbo);
   glBindBuffer(GL_ARRAY_BUFFER, vbo);
   glBufferData(GL_ARRAY_BUFFER, sizeof(VertexFormat) * 3, &vertices[0], GL_STATIC_DRAW);
   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0);

   Model myModel;                            //is allocated on Stack
   myModel.vao = vao;                        //add vao
   myModel.vbos.push_back(vbo);              //add vbo
   GameModelList[gameModelName] = myModel;  //add to std::map


void GameModels::DeleteModel(const std::string& gameModelName)
   Model model = GameModelList[gameModelName];
   unsigned int p = model.vao;
   glDeleteVertexArrays(1, &p);
   glDeleteBuffers(model.vbos.size(), &model.vbos[0]);


unsigned int GameModels::GetModel(const std::string& gameModelName)
   return GameModelList[gameModelName].vao;

As you can see GetModel method will return the VAO to bind it in the rendering loop. Also notice how I delete the VAO and the VBO in the class destructor. It’s always good to know what VBOs are under a VAO. If you delete just the VAO, VBOs will continue to exist in memory and vice versa. This is how I designed my program to deal with VAO and VBOs. This is not the only way you can keep your data. Feel free to add anything you want to the code. It is up to you how you use the GPU memory.

Finally main.cpp will look like this:

#pragma once
#include "Core\Shader_Loader.h"
#include "Core\GameModels.h"

#include <iostream>

Models::GameModels* gameModels;
GLuint program;

void renderScene(void)

  glClearColor(0.0, 0.3, 0.3, 1.0);

  glDrawArrays(GL_TRIANGLES, 0, 3);

void closeCallback()

  std::cout << "GLUT:\t Finished" << std::endl;

void Init()


  gameModels = new Models::GameModels();

  //load and compile shaders
  Core::Shader_Loader shaderLoader;
  program = shaderLoader.CreateProgram("Shaders\\Vertex_Shader.glsl",
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

int main(int argc, char **argv)

 glutInit(&argc, argv);
 glutInitWindowPosition(500, 500);
 glutInitWindowSize(800, 600);
 glutCreateWindow("OpenGL First Window");

 if (glewIsSupported("GL_VERSION_4_5")) //lower your version if 4.5 is not supported by your video card
   std::cout << " OpenGL Version is 4.5\n ";
   std::cout << "OpenGL 4.5 not supported\n ";

 // register callbacks


 delete gameModels;
 return 0;

You can see that a few thing were added :

  • Include GameModels.h
  • Have a pointer to class Models::GameModels and create an instance for it and of course delete it at  the end.
  • Call gameModels->CreateTriangleModel(“triangle1”); to create the triangle with key “triangle1”
  • In the rendering loop call glBindVertexArray(gameModels->GetModel(“triangle1”)); to bind the VAO and draw the triangle.
  • glutCloseFunc(closeCallback); called when the program is closed to exit the rendering loop and delete final pointers.

Until now we have our triangle set, but the color is still hardcoded in our pixel phader. In the next tutorial we will see how to set up the color attribute in our VertexFormat.

Triangle OpenGL

Triangle OpenGL

blog comments powered by Disqus