OpenGL Instanced rendering example

You can read about it at many places. Probably the red book and http://ogldev.atspace.co.uk/www/tutorial33/tutorial33.html are one of the best sources. However, there is a lot of information in the red book and the example which is given is not too clear. You just see something rendered and you may have not cover transformations yet or you do not know how to load some model from some file.
In Ogldev you have to read a lot of other tutorials to get aquainted what happens and the tutorial mostly modifies previously written code (the one about loading models with Assimp).
Also, it may be a bit hard to follow since a lot of other stuff is happening as well like texture mapping, etc.

#define GLM_FORCE_RADIANS
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include "../common/Shader.hpp"
#include "../common/ShaderProgram.hpp"
using namespace glm;
using namespace std;

/*
    checks whether there are errors and prints them
*/
void checkForOpenglErrors() {
    GLenum glErr;
    while ((glErr = glGetError()) != GL_NO_ERROR) {
        std::cerr << "OpenGL error: " << glErr << std::endl;
    }
}

/*
    vertices of a pyramid
*/
GLfloat vertices[] = {
    -1,0,2,
    1,0,2,
    1,0,0,
    -1,0,0,
    0,1,1
};

/*
    indices to represent all of the faces (triangles) of the pyramid
*/
GLushort indices[] = {
    0,1,4,
    1,2,4,
    2,3,4,
    3,0,4
};

/*
    colors for each pyramid instance rendered
*/
GLfloat colors[] = {
    1,0,0,
    0,1,0,
    0,0,1
};

/* Vertex array */
GLuint VAO;

/* Buffers bind to vertex array */
#define NUM_BUFFERS 4
GLuint buffers[NUM_BUFFERS];


/* Locations of buffers in array */
#define POSITION_BUFFER 0
#define INDEX_BUFFER 1
#define COLOR_BUFFER 2
#define TRANS_BUFFER 3

/* Locations of attributes in vertex shader */
#define POSITION_ATTR_LOC 0
#define COLOR_ATTR_LOC 1
#define TRANS_ATTR_LOC 2

void init() {
    /* create aand use shader program */
    ShaderProgram program = ShaderProgram(Shader("shader.vert", GL_VERTEX_SHADER),
     Shader("shader.frag", GL_FRAGMENT_SHADER));
    glUseProgram(program.getProgram());

    /* generate the needed vertex array so that we can use later when rendering */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    /* generate buffers needed for the pyramid */
    glGenBuffers(NUM_BUFFERS, buffers);

    /* buffer to store vertices */
    glBindBuffer(GL_ARRAY_BUFFER, buffers[POSITION_BUFFER]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(POSITION_ATTR_LOC);
    glVertexAttribPointer(POSITION_ATTR_LOC, 3, GL_FLOAT, GL_FALSE, 0, 0);

    /* index buffer */
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[INDEX_BUFFER]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    /* buffer for the colors of each pyramid */
    glBindBuffer(GL_ARRAY_BUFFER, buffers[COLOR_BUFFER]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
    glVertexAttribPointer(COLOR_ATTR_LOC, 3, GL_FLOAT, GL_FALSE, 0, 0);
    // set change per instance -> every instance will have a new entry from the array buffer
    glVertexAttribDivisor(COLOR_ATTR_LOC, 1);
    glEnableVertexAttribArray(COLOR_ATTR_LOC);

    /*
        create just translate transformations for each pyramid -> 3 pyramids
    */
    mat4 transf[] = {
        translate(mat4(1.0f), vec3(-4.0f, -3.0f, -14.0f)),
        translate(mat4(1.0f), vec3(3.0f, -1.0f, -17.0f)),
        translate(mat4(1.0f), vec3(2.0f, -2.0f, -9.0f))
    };
    glBindBuffer(GL_ARRAY_BUFFER, buffers[TRANS_BUFFER]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(transf), transf, GL_STATIC_DRAW);

    /*
        set the attrib location and enable it. since we have a 4x4 matrix we have
        4 columns of vectors of length 4.
    */
    for (int i = 0; i < 4; ++i) {
        glVertexAttribPointer(TRANS_ATTR_LOC+i, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (GLvoid *)(i*sizeof(vec4)));
        glVertexAttribDivisor(TRANS_ATTR_LOC+i, 1);
        glEnableVertexAttribArray(TRANS_ATTR_LOC+i);
    }

    // done with VAO
    // unbind
    glBindVertexArray(0);

    /*
        create projection transformation and update the uniform
    */
    GLint projectionLoc = glGetUniformLocation(program.getProgram(), "projection");
    mat4 projection = perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
    glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, &projection[0][0]);
}

void display() {
    checkForOpenglErrors();

    glClear(GL_COLOR_BUFFER_BIT);

    /* bind and draw */
    glBindVertexArray(VAO);

    /* sizeof(indices) / sizeof(GLushort) will return number of elements in the array */
    glDrawElementsInstanced(GL_TRIANGLES, sizeof(indices) / sizeof(GLushort), GL_UNSIGNED_SHORT, NULL, 3);

    glFlush();

}

int main(int argc, char ** argv)
{

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);
    glutInitWindowSize(512, 512);
    glutInitContextVersion(4,3); // freeglut required
    glutInitContextProfile(GLUT_CORE_PROFILE);
    glutCreateWindow("");
    glewExperimental=GL_TRUE;

    if (glewInit()) {
        cerr << "Problem initializing glew" << endl;
        exit(EXIT_FAILURE);
    }

    init();

    glutDisplayFunc(display);

    glutMainLoop();

    return 0;
}

Vertex shader:

#version 430 core

layout (location = 0) in vec3 position;

// per-instance attribute
layout (location = 1) in vec3 color;

// per-instance transformation
layout (location = 2) in mat4 trans;

uniform mat4 projection;


out vec4 col;

void main() {
    gl_Position = projection * trans * vec4(position, 1.0f);
    col = vec4(color, 1.0f);
}

Fragment shader

#version 430 core


in vec4 col;
out vec4 color;

void main() {
    color = col;
}

Leave a Reply

Your email address will not be published. Required fields are marked *