Vertex buffer object (VBO)

A Vertex Buffer Object (VBO) is an OpenGL buffer object for buffering vertex data to VRAM so as to make rendering faster.

There are different types of VBO, here we will consider GL_ARRAY_BUFFER type.

create VBO

If you manage native memory yourself, you can pre-allocate a huge block and create several buffers from it. In LWJGL MemoryUtil and MemoryStack classes do it.

Create VBO example
fun createVBO(data: FloatArray): Int {
    MemoryStack.stackPush().use { stack ->
        val idVbo = glGenBuffers()
        glBindBuffer(GL_ARRAY_BUFFER, idVbo)
        
        val fb = stack.mallocFloat(data.size)
        fb.put(data).flip()
        glBufferData(GL_ARRAY_BUFFER, fb, GL_STATIC_DRAW)
        return idVbo
   }
}
public static int createVBO(float[] data) {

    try (MemoryStack stack = MemoryStack.stackPush()) {
        int idVbo = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, idVbo);

        FloatBuffer fb = stack.mallocFloat(data.length);
        fb.put(data).flip();
        glBufferData(GL_ARRAY_BUFFER, fb, GL_STATIC_DRAW);

        return idVbo;
        }
    }
GlUtils.createVBO = function(gl, array){
    const idVbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, idVbo);
    gl.bufferData(gl.ARRAY_BUFFER,
            new Float32Array(array),
            gl.STATIC_DRAW);
    return idVbo;    
}
private var FLOAT_BUFFER: FloatBuffer = ByteBuffer.allocateDirect(100 * 4)
        .order(ByteOrder.nativeOrder()).asFloatBuffer()
        
private val ID_ARRAY = intArrayOf(0)
    
fun createVBO(data: FloatArray): Int {
    glGenBuffers(1, ID_ARRAY, 0)
    val idVbo = ID_ARRAY[0]
    glBindBuffer(GL_ARRAY_BUFFER, idVbo)
    FLOAT_BUFFER.put(data).position(0)
    glBufferData(GL_ARRAY_BUFFER, data.size * 4, FLOAT_BUFFER, GL_STATIC_DRAW)
    return idVbo
}

vertex attributes

After creating VBO you need specify what type of data it contains. It can be any vertex attributes like coordinates, color, etc.

One VBO may contains several attributes. For example, the following function allows you to specify coordinates, color, and texture coordinates, which are packed into sequential order.

Specify attributes in VBO example
fun bindAttributes(idVBO: Int, numCoords: Int, numColorComponents: Int, texCoord: Boolean) {
    glBindBuffer(GL_ARRAY_BUFFER, idVBO)

    // size of float is 4
    val stride = 4 * (numCoords + numColorComponents + if (texCoord) 2 else 0)
    
    if (numCoords > 0) {
        glEnableVertexAttribArray(A_LOCATION_COORDS)
        glVertexAttribPointer(
            A_LOCATION_COORDS,
            numCoords, GL_FLOAT, false,
            stride, 0)
    }
    
    if (numColorComponents > 0) {
        glEnableVertexAttribArray(A_LOCATION_COLORS)
        glVertexAttribPointer(
            A_LOCATION_COLORS,
            numColorComponents, GL_FLOAT, true,
                stride, 4 * numCoords)
    }
        
    if (texCoord) {
        glEnableVertexAttribArray(A_LOCATION_TEXCOORDS)
        glVertexAttribPointer(
            A_LOCATION_TEXCOORDS,
            2, GL_FLOAT, false,
            stride, 4 * (numCoords + numColorComponents))
     }
     
     glBindBuffer(GL_ARRAY_BUFFER, 0)
}
public static void bindAttributes(int idVBO, int numCoords, int numColorComponents, boolean texCoord) {

    glBindBuffer(GL_ARRAY_BUFFER, idVBO);

    // size of float is 4
    int stride = 4 * (numCoords + numColorComponents + ((texCoord) ? 2 : 0));

    if (numCoords > 0) {
        glEnableVertexAttribArray(A_LOCATION_COORDS);
        glVertexAttribPointer(A_LOCATION_COORDS,
                    numCoords, GL_FLOAT, false,
                    stride, 0);
        }

     if (numColorComponents > 0) {
        glEnableVertexAttribArray(A_LOCATION_COLORS);
        glVertexAttribPointer(A_LOCATION_COLORS,
                    numColorComponents, GL_FLOAT, true,
                    stride, 4L * numCoords);
        }

     if (texCoord) {
        glEnableVertexAttribArray(A_LOCATION_TEXCOORDS);
        glVertexAttribPointer(A_LOCATION_TEXCOORDS,
                    2, GL_FLOAT, false,
                    stride, 4L * (numCoords + numColorComponents));
     }

     glBindBuffer(GL_ARRAY_BUFFER, 0);
}
GlUtils.bindAttributes = function(gl, idVbo,
                  numCoords, 
                  numColorComponents, 
                  texCoord) {

    gl.bindBuffer(gl.ARRAY_BUFFER, idVbo);

        // size of float is 4
    const stride = 4 * (numCoords + numColorComponents + ((texCoord) ? 2 : 0));
    const A_LOCATION_COORDS = 0;
    const A_LOCATION_COLORS = 1;
    const A_LOCATION_TEXCOORDS = 3;
    
    if (numCoords > 0) {
       gl.enableVertexAttribArray(A_LOCATION_COORDS);
       gl.vertexAttribPointer(
            A_LOCATION_COORDS, numCoords, 
            gl.FLOAT, false, stride, 0);     
       }

   if (numColorComponents > 0) {
       gl.enableVertexAttribArray(A_LOCATION_COLORS);
       gl.vertexAttribPointer(
            A_LOCATION_COLORS, numCoords, 
            gl.FLOAT, false, stride, 4 * numCoords);        
    }

    if (texCoord) {
       gl.enableVertexAttribArray(A_LOCATION_TEXCOORDS);
       gl.vertexAttribPointer(
            A_LOCATION_TEXCOORDS, numCoords, 
            gl.FLOAT, false, stride, 4 * (numCoords + numColorComponents));   
    }    
}
same as kotlin for lwjgl

drawing VBO

To draw VBO you must just activate it, then invoke glDrawArrays() function.

// draw white quad (4 vertices)
glBindBuffer(GL_ARRAY_BUFFER, idVbo)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) 

OpenGL 3.0 may not to draw VBO without VAO. So, in the LWJGL example I used OpenGL 2.0 and old shaders.

#version 120 
attribute vec4 vCoord;
varying vec4 exColor;
uniform mat4 matrix;
void main() {
    gl_Position = matrix  * vCoord;
    exColor = vec4(1.0,1.0,1.0,1.0);
}
#version 120 varying vec4 exColor; void main() { gl_FragColor = exColor; }

You can download full sources of quad with solid color on GitHub.