play.TechGeneral : Transforms and uniforms

tl;dr

A constant triangle is pretty boring. To "transform", or to make the object rotate and move, matrix operations are needed. To introduce values that change depending on the frame (say, the rotation/movement of an object), you can use a uniform value.

Transforms

In my previous post, I mentioned that we want to use vertex buffers to store the vertex information of objects on the graphics card. That way we don't have to send it on every frame we want to render. However, we often want that object to move around in space - maybe it represents a planet orbiting a sun, or a bullet heading towards a space ship - how do we avoid updating that data every frame now?

Instead of sending the new vertex locations indicating the rotation and movement, we can send a matrix that represents the transformation we want to apply to the object. We defined our position as a vector of 4 floats, so if we multiply it by a matrix of 4x4 floats, we'll have a new vector of 4 floats. The linear algebra involved in determining the matrix to send over is fairly involved, and so I happily used gl3n, an OpenGL maths library for D, to generate it instead.

Uniforms

Now we know what matrix to send through - how do we get it to our shader? Our vertex shader looked like this:

#version 330 core
layout(location = 0) in vec4 pos;

void main() {
    gl_Position = pos;
}

We now need to make it pos * transform, and somehow provide transform. A uniform value is how to do this - you set it in your code, and it is passed through to the shader whenever an object is to be drawn.

#version 330 core
layout(location = 0) in vec4 pos;

uniform mat4 u_transform;

void main() {
    gl_Position = pos * u_transform;
}

Through the magic of linear algebra, our object will now be rotated and relocated.

Much like the vec4 type is a vector of 4 floats, the mat4 type is a matrix of 4x4 floats.

Uniforms - the D side

Once you've created your shaders and linked them into a program, you can extract the index of the uniforms referenced in them using glGetUniformLocation.

string transformMatrixName = "u_transform";
transformMatrix = glGetUniformLocation(program, transformMatrixName.ptr);
if (transformMatrix == -1) {
    writefln("Could not bind uniform %s", transformMatrixName);
    throw new Error("Uniform bind failure");
}

Whenever we want to populate that mat4 value, say every frame, we need to call glUniformMatrix4fv and pass that index and the location of the data to it. Each uniform type has a separate function - so if you have a single int value, you need glUniform1i.

glUniformMatrix4fv(transformMatrix, 1, GL_FALSE, matrix.value_ptr);

Transforms - the D side

How do we build that matrix that we call value_ptr on. gl3n makes it easy:

auto matrix = mat4.identity
    .rotatey(timeDiff)
    .rotatex(timeDiff / PI)
    .scale(0.2, 0.2, 0.2);

In this example timeDiff is simply the seconds (a double) since the program started running. This rotates the object along two dimensions, one pi times slower than the other.

Putting it together

Here is the state of of my code repo at this point.

Here is a short demo of what it looks like at this point: