为什么 glUseProgram 在每一帧都使用 glUniform 调用?
Why is glUseProgram called every frame with glUniform?
我正在学习 OpenGL v3.3 教程,该教程指导我使用 glUniform4f 修改片段着色器中的统一属性(请参阅下面的代码)。据我了解,OpenGL 是一个状态机,我们不会取消绑定当前正在使用的 shaderProgram,我们宁愿修改附加到程序的着色器之一的属性,所以为什么我们需要在每一帧调用 glUseProgram 吗?
我知道对于更高版本的 OpenGL 不是这种情况,但我仍然想了解为什么 v3.3 是这种情况
OpenGL 程序:
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();
}
片段着色器
#version 330 core
out vec4 FragColor;
uniform vec4 ourColor;
void main()
{
FragColor = ourColor;
}
编辑:我忘了指出 glUniform4f 设置新颜色(以周期性方式)每帧,代码的最终输出是 2 个带有动画颜色的三角形,从 while 循环中删除 glUseProgram 而导致静态图像,这不是代码的预期目标。
在您的情况下,您可能不必在每一帧都进行设置。
然而,在更大的程序中,您将使用多个着色器,因此在每次使用它之前都需要设置您想要的着色器,而且示例可能就是为此而编写的。
可变全局变量(这实际上是 OpenGL 的状态)本质上是危险的。可变全局变量最重要的危险之一是对其当前状态做出的假设最终证明是错误的。这些类型的故障使得理解一段代码是否能正常工作变得异常困难,因为它的行为依赖于外部的东西。关于世界本质的假设,而不是由期望它的函数定义的东西。
您的代码想要发出两个使用特定着色器的绘图命令。通过在使用点绑定该着色器,此代码我们不受任何关于当前着色器的假设的约束。当您开始循环时,先前的着色器是什么并不重要;您正在将其设置为需要的。
这使得此代码不受您以后可能进行的任何更改的影响。如果你想渲染第三个使用不同着色器的东西,你的代码会继续工作:你在每个循环开始时重置着色器。如果您只在循环外设置着色器,而不是每次都重置它,那么您的代码将被任何后续着色器更改破坏。
是的,在像这样的小玩具程序中,这可能是一个容易追踪和修复的问题。但是,当您处理的代码跨越数百个文件和数万行代码时,依赖关系分散在第 3 方库中,所有这些都可能修改任何特定的 OpenGL 状态?是的,最好不要对世界的本质假设太多。
早点养成好习惯是好事。
现在说句公道话,re-specifying 在程序中的每个点都有一堆 OpenGL 状态 也是 一个坏主意。将 OpenGL 上下文的性质作为函数的一部分 assumptions/expectations 并不是 a-priori 坏事。如果您有一些网格渲染函数,该函数可以假设用户已经绑定了它打算使用的着色器。指定需要指定用于渲染的所有其他状态不是此函数的工作。事实上,如果这样做,那将是一个糟糕的网格 class/function,因为您将无法渲染具有不同状态的相同网格。
但是在每一帧的开始,或者渲染过程的每个主要部分的开始,指定 OpenGL 状态的基线是完全有效的。当您循环回到新帧的开头时,您基本上应该假设 nothing 关于 OpenGL 的状态。不是因为 OpenGL 不记得了,而是因为你可能是错的。
如答案和评论所示,在我的问题中所述的示例中,glUseProgram 只能在 while 循环之外编写一次以产生预期的输出,即 2 个三角形,颜色定期动画。我的误解是 learnopengl.com e-book https://learnopengl.com/Getting-started/Shaders 中下一章的结果:
“更新制服确实需要您首先使用该程序(通过调用 glUseProgram),因为它会在当前活动的着色器程序上设置制服。”
我认为每次我想通过 glUniform* 更新制服时我还必须发出对 glUseProgram 的调用,这是一个错误的理解。
我正在学习 OpenGL v3.3 教程,该教程指导我使用 glUniform4f 修改片段着色器中的统一属性(请参阅下面的代码)。据我了解,OpenGL 是一个状态机,我们不会取消绑定当前正在使用的 shaderProgram,我们宁愿修改附加到程序的着色器之一的属性,所以为什么我们需要在每一帧调用 glUseProgram 吗?
我知道对于更高版本的 OpenGL 不是这种情况,但我仍然想了解为什么 v3.3 是这种情况
OpenGL 程序:
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();
}
片段着色器
#version 330 core
out vec4 FragColor;
uniform vec4 ourColor;
void main()
{
FragColor = ourColor;
}
编辑:我忘了指出 glUniform4f 设置新颜色(以周期性方式)每帧,代码的最终输出是 2 个带有动画颜色的三角形,从 while 循环中删除 glUseProgram 而导致静态图像,这不是代码的预期目标。
在您的情况下,您可能不必在每一帧都进行设置。 然而,在更大的程序中,您将使用多个着色器,因此在每次使用它之前都需要设置您想要的着色器,而且示例可能就是为此而编写的。
可变全局变量(这实际上是 OpenGL 的状态)本质上是危险的。可变全局变量最重要的危险之一是对其当前状态做出的假设最终证明是错误的。这些类型的故障使得理解一段代码是否能正常工作变得异常困难,因为它的行为依赖于外部的东西。关于世界本质的假设,而不是由期望它的函数定义的东西。
您的代码想要发出两个使用特定着色器的绘图命令。通过在使用点绑定该着色器,此代码我们不受任何关于当前着色器的假设的约束。当您开始循环时,先前的着色器是什么并不重要;您正在将其设置为需要的。
这使得此代码不受您以后可能进行的任何更改的影响。如果你想渲染第三个使用不同着色器的东西,你的代码会继续工作:你在每个循环开始时重置着色器。如果您只在循环外设置着色器,而不是每次都重置它,那么您的代码将被任何后续着色器更改破坏。
是的,在像这样的小玩具程序中,这可能是一个容易追踪和修复的问题。但是,当您处理的代码跨越数百个文件和数万行代码时,依赖关系分散在第 3 方库中,所有这些都可能修改任何特定的 OpenGL 状态?是的,最好不要对世界的本质假设太多。
早点养成好习惯是好事。
现在说句公道话,re-specifying 在程序中的每个点都有一堆 OpenGL 状态 也是 一个坏主意。将 OpenGL 上下文的性质作为函数的一部分 assumptions/expectations 并不是 a-priori 坏事。如果您有一些网格渲染函数,该函数可以假设用户已经绑定了它打算使用的着色器。指定需要指定用于渲染的所有其他状态不是此函数的工作。事实上,如果这样做,那将是一个糟糕的网格 class/function,因为您将无法渲染具有不同状态的相同网格。
但是在每一帧的开始,或者渲染过程的每个主要部分的开始,指定 OpenGL 状态的基线是完全有效的。当您循环回到新帧的开头时,您基本上应该假设 nothing 关于 OpenGL 的状态。不是因为 OpenGL 不记得了,而是因为你可能是错的。
如答案和评论所示,在我的问题中所述的示例中,glUseProgram 只能在 while 循环之外编写一次以产生预期的输出,即 2 个三角形,颜色定期动画。我的误解是 learnopengl.com e-book https://learnopengl.com/Getting-started/Shaders 中下一章的结果:
“更新制服确实需要您首先使用该程序(通过调用 glUseProgram),因为它会在当前活动的着色器程序上设置制服。”
我认为每次我想通过 glUniform* 更新制服时我还必须发出对 glUseProgram 的调用,这是一个错误的理解。