More Power to the Shaders
Prev | Chapter 3. OpenGL's Moving Triangle | Next

## More Power to the Shaders

It's all well and good that we are no longer having to transform vertices manually. But perhaps we can move more things to the vertex shader. Could it be possible to move all of `ComputePositionOffsets` to the vertex shader?

Well, no. The call to `glutGet(GL_ELAPSED_TIME)` cannot be moved there, since GLSL code cannot directly call C/C++ functions. But everything else can be moved. This is what `vertCalcOffset.cpp` does.

The vertex program is found in `data\calcOffset.vert`.

Example 3.6. Offset Computing Vertex Shader

    #version 330

    layout(location = 0) in vec4 position;
    uniform float loopDuration;
    uniform float time;

    void main()
    {
        float timeScale = 3.14159f * 2.0f / loopDuration;

        float currTime = mod(time, loopDuration);
        vec4 totalOffset = vec4(
            cos(currTime * timeScale) * 0.5f,
            sin(currTime * timeScale) * 0.5f,
            0.0f,
            0.0f);

        gl_Position = position + totalOffset;
    }

This shader takes two uniforms: the duration of the loop and the elapsed time.

In this shader, we use a number of standard GLSL functions, like `mod`, `cos`, and `sin`. We saw `mix` in the last tutorial. And these are just the tip of the iceberg; there are a lot of standard GLSL functions available.

The rendering code looks quite similar to the previous rendering code:

Example 3.7. Rendering with Time

    void display()
    {
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(theProgram);

        glUniform1f(elapsedTimeUniform, glutGet(GLUT_ELAPSED_TIME) / 1000.0f);

        glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);

        glDrawArrays(GL_TRIANGLES, 0, 3);

        glDisableVertexAttribArray(0);
        glUseProgram(0);

        glutSwapBuffers();
        glutPostRedisplay();
    }

This time, we do not need any code to use the elapsed time; we simply pass it unmodified to the shader.

You may be wondering exactly how it is that the `loopDuration` uniform gets set. This is done in our shader initialization routine, and it is done only once:

Example 3.8. Loop Duration Setting

    void InitializeProgram()
    {
        std::vector<GLuint> shaderList;

        shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, "calcOffset.vert"));
        shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, "standard.frag"));

        theProgram = Framework::CreateProgram(shaderList);

        elapsedTimeUniform = glGetUniformLocation(theProgram, "time");

        GLuint loopDurationUnf = glGetUniformLocation(theProgram, "loopDuration");
        glUseProgram(theProgram);
        glUniform1f(loopDurationUnf, 5.0f);
        glUseProgram(0);
    }

We get the time uniform as normal with `glGetUniformLocation`. For the loop duration, we get that in a local variable. Then we immediately set the current program object, set the uniform to a value, and then unset the current program object.

Program objects, like all objects that contain internal state, will retain their state unless you explicitly change it. So the value of `loopDuration` will be 5.0f in perpetuity; we do not need to set it every frame.

Prev | Up | Next
A Better Way | Home | Multiple Shaders