Why is glUseProgram called every frame with glUniform?
Asked Answered
B

3

5

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.

Broadnax answered 27/9, 2020 at 14:34 Comment(4)
"Why do we need to call glUseProgram on every frame?" - You don't have to do that. Who says that? " As far as I understand, OpenGL is a state machine, ..." - You are right. It is sufficient to install the program once before the application loop (when everything is rendered with the same program).Searcy
I edited the my post to elaborate the question further.Broadnax
You cannot remove it you have to put it before the loop.Searcy
Understood, the program works correctly now after writing glUseProgram outside the loop, which makes sense.Broadnax
A
7

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.

Ambient answered 27/9, 2020 at 14:40 Comment(0)
B
3

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.

Blocking answered 27/9, 2020 at 15:31 Comment(0)
B
2

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.

Broadnax answered 27/9, 2020 at 16:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.