opengl 将纹理传递给程序:一次还是每次渲染?

opengl pass texture to program: once or at every rendering?

我有一个程序有两种纹理:一种来自视频,另一种来自图像。

对于图像纹理,我是否必须在每次渲染时将其传递给程序,还是只传递一次?我可以做吗

glActiveTexture(GLenum(GL_TEXTURE1))
glBindTexture(GLenum(GL_TEXTURE_2D), texture.id)
glUniform1i(textureLocation, 1)

就一次?我相信是这样,但在我的实验中,如果不涉及视频纹理,这就可以正常工作,但是一旦我添加了我在每个渲染过程中附加的视频纹理(因为它正在改变),获取图像的唯一方法是在每个渲染帧 运行 以上代码。

您应该在每次绘制前绑定纹理。您只需要设置一次位置。您也可以为此在着色器代码中执行 layout(binding = 1) 。位置制服与程序保持一致。纹理绑定是全局 GL 状态。还要注意 ActiveTexture:它是全局 GL 状态。

好的做法是:

  • 创建程序时,一次,设置纹理位置(统一)
  • 绘制时:SetActive(i)、Bind(i)、Draw、SetActive(i) Bind(0)、SetActive(0)

然后稍后针对冗余调用进行优化。

让我们剖析一下您在做什么,包括一些不必要的东西,以及 GL 做了什么。

首先,您在代码中进行的 none 的 C 风格转换是必要的。只需使用 GL_TEXTURE_2D 等代替 GLenum(GL_TEXTURE_2D)

glActiveTexture(GL_TEXTURE0 + i),其中 i[0, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1] 范围内,选择当前活动的 纹理单元 。只要您不使用另一个有效的单元标识符调用 glActiveTexture,改变纹理单元状态的命令将影响单元 i

一旦用当前活动的纹理单元i调用glBindTexture(target, name),纹理单元的状态就会更改为引用name指定的target ] 在着色器中使用适当的采样器对其进行采样时(即 name 可能绑定到 TEXTURE_2D 并且相应的样本必须是 sampler2D)。您只能将一个纹理对象绑定到当前活动纹理单元的特定目标 - 因此,如果您需要在着色器中采样两个 2D 纹理,则需要使用两个纹理单元。

从上面,glUniform1i(samplerLocation, i)的作用应该很明显了。

因此,如果您有两个 2D 纹理需要在着色器中采样,则需要两个纹理单元和两个采样器,每个都引用一个特定单元:

GLuint regularTextureName = 0;
GLunit videoTextureName = 0;

GLint regularTextureSamplerLocation = ...;
GLint videoTextureSamplerLocation = ...;

GLenum regularTextureUnit = 0;
GLenum videoTextureUnit = 1;

// setup texture objects and shaders ...

// make successfully linked shader program current and query
// locations, or better yet, assign locations explicitly in
// the shader (see below) ...

glActiveTexture(GL_TEXTURE0 + regularTextureUnit);
glBindTexture(GL_TEXTURE_2D, regularTextureName);
glUniform(regularTextureSamplerLocation, regularTextureUnit);

glActiveTexture(GL_TEXTURE0 + videoTextureUnit);
glBindTexture(GL_TEXTURE_2D, videoTextureName);
glUniform(videoTextureSampleLocation, videoTextureUnit);

你的片段着色器,我假设你将在其中进行采样,必须有相应的采样器:

layout(binding = 0) uniform sampler2D regularTextureSampler;
layout(binding = 1) uniform sampler2D videoTextureSampler;

就是这样。如果绑定到上述单元的两个纹理对象都正确设置,那么在每次片段着色器调用之前纹理内容是否动态变化并不重要——在许多场景中这是常见的地方,例如延迟渲染或任何其他渲染到纹理算法,因此您并没有完全用某些视频纹理开辟新天地。

关于你需要多久做一次这个问题:你需要在需要做的时候做——不要改变不需要改变的状态。如果您从不更改相应纹理单元的绑定,则根本不需要重新绑定纹理。正确设置它们一次,不要管它们。

采样器绑定也是如此:如果您不使用着色器对其他纹理对象进行采样,则根本不需要更改着色器程序状态。设置一次,不用管它。

简而言之:如无必要,请不要更改状态。

编辑:我不太确定是否是这种情况,但是如果您使用同一个着色器和一个采样器 both 纹理在单独的着色器调用中,你必须改变一些东西,但你猜怎么着,这就像让采样器引用另一个纹理单元一样简单:

// same texture unit setup as before
// shader program is current 

while (rendering)
{
  glUniform(samplerLocation, regularTextureUnit);
  // draw call sampling the regular texture 

  glUniform(samplerLocation, videoTextureUnit);
  // draw call sampling teh video texture
}