【问题标题】:opengl pass texture to program: once or at every rendering?opengl将纹理传递给程序:一次还是每次渲染?
【发布时间】:2016-09-30 16:59:27
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: opengl textures


    【解决方案1】:

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

    首先,您在代码中所做的 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 可能绑定到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
    }
    

    【讨论】:

    • (我需要 C 风格的强制转换,因为我正在编写 Swift,如果我不这样做编译器会抱怨)我知道这很愚蠢..
    • @Guig:实际上,假设您正在那里编写 C 或 C++ 代码,我对此很不利。只要你不使用 Objective-C ... ;)
    • 我想每个人学习和教授 GL 的方式都不同。我仍然认为了解活动纹理和绑定是全局 gl 状态但统一(位置)绑定是每个程序是非常重要的。
    • @starmole: 重要的是有 context stateobject state (除了服务器和客户端状态的区别)和当前活动纹理单元是上下文状态。纹理绑定是明确的纹理单元状态,这使得它们有点混合——它们是上下文状态,但你不改变全局TEXTURE_BINDING_*,你改变的是属于活动纹理的那个单元。这与使用绑定修改方法更改对象状态的效果基本相同。
    【解决方案2】:

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

    好的做法是:

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

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

    【讨论】:

    • 你的解释自相矛盾。你不需要在每次绘制调用之前绑定纹理,因为这是一个全局状态。你必须为每个程序提交统一的位置,至少一次,需要从该纹理中采样。
    • @MichaelIvanov:你会如何更好地解释它?我有一个合理的假设,即有不止一张画,每一张都有不同的纹理。我看不出矛盾吗?我试图指出该位置是每个程序的,并且是全局绑定的。处理全局状态最简单(不是最有效)的方法就是设置它。
    • 是的,对,现在对我来说似乎更合理; ) 你说,“如果我们需要在每次重新绑定的绘制调用上绑定不同的纹理,但每个程序只能设置一次位置”,对吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    • 1970-01-01
    • 2014-08-09
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多