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

时间:2016-09-30 07:38:05

标签: opengl textures

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

对于图像纹理,我是否必须在每次渲染时将其传递给程序,或者我可以只执行一次?即我能做什么

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

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

2 个答案:

答案 0 :(得分:2)

让我们分析你的所作所为,包括一些不必要的东西,以及GL所做的事情。

首先,您在代码中进行的C风格演员表都不是必需的。只需使用GL_TEXTURE_2D等,而不是GLenum(GL_TEXTURE_2D)

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

只要使用当前活动纹理单元glBindTexture(target, name)调用i,纹理单元的状态就会更改,以便在指定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;

就是这样。如果绑定到上述单元的两个纹理对象都被正确设置,则在每个片段着色器调用之前纹理的内容是否动态地改变并不重要 - 在许多场景中这是常见的,例如,延迟渲染或任何其他渲染到纹理算法,所以你并没有完全突破一些视频纹理。

关于你需要多长时间执行此操作的问题:你需要在需要时执行此操作 - 不要更改不需要更改的状态。如果您从未更改相应纹理单元的绑定,则根本不需要重新绑定纹理。正确设置它们并让它们独自存在。

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

简而言之:如果没有,请不要改变状态。

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

// 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
}

答案 1 :(得分:0)

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

良好的做法是:

  • 关于程序创建,一次,设置纹理位置(统一)
  • 平局:SetActive(i),Bind(i),Draw,SetActive(i)Bind(0),SetActive(0)

然后优化冗余呼叫。