A. Khaled
A. Khaled

Reputation: 175

Why is glUseProgram called every frame with glUniform?

I am following an OpenGL v3.3 tutorial that instructs me to modify a uniform attribute in a fragment shader using glUniform4f (refer to the code below). As far as I understand, OpenGL is a state machine, we don't unbind the current shaderProgram being used, we rather modify an attribute in one of the shaders attached to the program, so why do we need to call glUseProgram on every frame?

I understand that this is not the case for later versions of OpenGL, but I'd still like to understand why it's the case for v3.3

OpenGL Program:

while (!glfwWindowShouldClose(window))
{
    processInput(window);

    glClearColor(0.2f, 1.0f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);


    glUseProgram(shaderProgram); // the function in question

    float redValue = (sin(glfwGetTime()) / 2.0f) + 0.5f;
    int colorUniformLocation = glGetUniformLocation(shaderProgram, "ourColor");
    glUniform4f(colorUniformLocation, redValue, 0.0f, 0.0f, 1.0f);

    std::cout << colorUniformLocation << std::endl;

    glBindVertexArray(VAO[0]);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glBindVertexArray(VAO[1]);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glfwSwapBuffers(window);
    glfwPollEvents();
}

Fragment Shader

#version 330 core
out vec4 FragColor;
uniform vec4 ourColor;
void main() 
{
 FragColor = ourColor;
}

Edit: I forgot to point out that glUniform4f sets a new color (in a periodic fashion) each frame, the final output of the code are 2 triangles with animating color, removing glUseProgram from the while loop while result in a static image, which isn't the intended goal of the code.

Upvotes: 5

Views: 1905

Answers (3)

A. Khaled
A. Khaled

Reputation: 175

As the answers and comments demonstrated, in the example stated in my question, glUseProgram can only be written once outside the while loop to produce the intended output, which is 2 triangles with colors animating periodically. The misunderstanding I had is a result of the following chapter in learnopengl.com e-book https://learnopengl.com/Getting-started/Shaders where it states:

"updating a uniform does require you to first use the program (by calling glUseProgram), because it sets the uniform on the currently active shader program."

I thought that every time I wanted to update the uniform via glUniform* I had to also issue a call to glUseProgram which is an incorrect understanding.

Upvotes: 2

Nicol Bolas
Nicol Bolas

Reputation: 473447

Mutable global variables (which is effectively what state is with OpenGL) are inherently dangerous. One of the most important dangers of mutable globals is making assumptions about their current state which turn out to be wrong. These kinds of failures make it incredibly difficult to understand whether or not a piece of code will work correctly, since its behavior is dependent on something external. Something that is assumed about the nature of the world rather than defined by the function that expects it.

Your code wants to issue two drawing commands that use a particular shader. By binding that shader at the point of use, this code us not bound to any assumptions as to the current shader. It doesn't matter what the previous shader was when you start the loop; you're setting it to what it needs to be.

This makes this code insulated to any later changes you might make. If you want to render a third thing that uses a different shader, your code continues to work: you reset the shader at the start of each loop. If you had only set the shader outside of the loop, and didn't reset it each time, then your code would be broken by any subsequent shader changes.

Yes, in a tiny toy program like this, that's probably an easy problem to track down and fix. But when you're dealing with code that spans hundreds of files with tens of thousands of lines of code, with dependency relationship scattered across 3rd party libraries all that might modify any particular OpenGL state? Yeah, it's probably best not to assume too much about the nature of the world.

Learning good habits early on is a good thing.

Now to be fair, re-specifying a bunch of OpenGL state at every point in the program is also a bad idea. Making assumptions/expectations about the nature of the OpenGL context as part of a function is not a-priori bad. If you have some rendering function for a mesh, it's OK for that function to assume that the user has bound the shader it intends to use. It's not the job of this function to specify all of the other state that needs to be specified for rendering. And indeed, it would be a bad mesh class/function if it did that, since you would be unable to render the same mesh with different state.

But at the beginning of each frame, or the start of each major part of your rendering process, specifying a baseline of OpenGL state is perfectly valid. When you loop back to the beginning of a new frame, you should basically assume nothing about OpenGL's state. Not because OpenGL won't remember, but because you might be wrong.

Upvotes: 4

jcoder
jcoder

Reputation: 30035

In your case you probably don't have to set it every frame. However in bigger program you'll use multiple shaders so will need to set the one you want before you use it each time, and likely the samples are just written to do that.

Upvotes: 7

Related Questions