【问题标题】:Multitexturing theory with texture objects and samplers具有纹理对象和采样器的多重纹理理论
【发布时间】:2015-09-06 18:01:03
【问题描述】:

我找不到任何关于如何仅使用纹理对象或纹理对象加上采样器来编写多重纹理的好的理论文章。我只是不知道如何管理 glActiveTexture 函数以及它究竟做了什么。

glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0 + 0); // Number between 0 and GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img.getSize().x, img.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.getPixelsPtr()); // Not in sampler
glGenerateMipmap(GL_TEXTURE_2D); // Not in sampler

/* Values associated with the texture and not with sampler (sampler has priority over texture).
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);*/

glGenSamplers(1, &textureSampler);
glBindSampler(0, textureSampler);
glSamplerParameteri(textureSampler, GL_TEXTURE_WRAP_S, GL_REPEAT);
glSamplerParameteri(textureSampler, GL_TEXTURE_WRAP_T, GL_REPEAT);
glSamplerParameteri(textureSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(textureSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glUniform1i(glGetUniformLocation(colorShader->program, "textureSampler"), 0); // 0 pour GL_TEXTURE0

我有点困惑,如果多重纹理是指在片段代码中有多个采样器链接到多个纹理,还是可能只有一个采样器具有多个纹理?

【问题讨论】:

    标签: opengl textures


    【解决方案1】:

    其中大部分内容之前一定已经解释过,但让我尝试给出一个概述,希望能更清楚地说明所有不同部分是如何组合在一起的。我将从分别解释每个部分开始,然后解释它们是如何连接的。

    纹理目标

    这是指不同类型的纹理(2D、3D 等)。您可以同时将多个纹理(每种纹理类型之一)绑定到同一个纹理单元。例如,之后:

    glBindTexture(GL_TEXTURE_2D, texId1);
    glBindTexture(GL_TEXTURE_3D, texId2);
    

    texId1texId2 都将绑定到同一个纹理单元,这是可能的,因为它们绑定到不同的目标。

    这个细节有点令人费解和困惑,我不会在这个答案的其余部分考虑它。我建议您始终将不同的纹理绑定到不同的纹理单元。它会让您免于头痛和惊喜。

    纹理对象

    纹理对象的名称使用glGenTextures() 创建,它们与glBindTexture() 绑定,等等。纹理对象拥有:

    • 纹理数据。
    • 定义纹理数据采样方式的状态,例如使用glTexParameteri() 设置的过滤属性。

    它们还包含与数据一起指定的纹理格式/类型的信息。

    纹理单元

    作为当前 OpenGL 状态的一部分,您可以绘制一张当前绑定的纹理表。我们需要同时绑定多个纹理来支持多纹理。一个纹理单元可以看作是这个状态表中的一个条目。

    您使用glActiveTexture() 指定当前活动的纹理单元。需要对特定纹理单元进行操作的调用将在活动纹理单元上进行操作。例如:

    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D, texId);
    

    texId绑定到纹理单元3。再次描绘绑定纹理表,第4个条目(编号从0开始)现在指向纹理texId

    采样器对象

    这是 OpenGL 3.3 及更高版本中可用的一种较新的对象。对于大多数用例,您将不需要使用它,即使它们涉及从多个纹理中采样。为了完整起见,我将它们包括在此处,但在您牢牢掌握纹理对象和纹理单元之前,无需担心采样器。

    还记得我上面解释的纹理对象拥有纹理数据,以及定义数据采样方式的状态吗?采样器本质上所做的是将这两个方面解耦。采样器对象包含可以覆盖纹理对象中采样相关状态的状态。

    这允许您在同一着色器中使用不同的采样参数一个纹理进行采样。假设您想在单个着色器中对同一纹理进行 LINEAR 和 NEAREST 采样。如果没有采样器对象,如果没有相同纹理的多个副本(具有多个数据副本),您将无法做到这一点。采样器对象支持这种功能。

    纹理视图

    这是 OpenGL 4.3 中引入的一项功能。甚至不仅仅是纹理采样器,我只是为了完整性而提及它。

    在采样器将纹理数据(及其相关格式)与采样参数分离的情况下,纹理视图将原始纹理数据与格式分离。它们使使用不同格式的相同原始纹理数据成为可能。我怀疑您可以在不使用此功能的情况下走很长一段路。

    拼凑起来

    您最终想要做的是指定着色器应该从哪些纹理中采样。纹理单元是在着色器和纹理之间建立联系的关键部分。

    从着色器的侧面看,着色器知道它从哪个纹理单元中采样。这是由采样器统一变量的值给出的。例如,如果“MyFirstTexture”是着色器代码中采样器变量的名称,则以下指定该变量与纹理单元 3 相关联:

    GLint loc = glGetUniformLocation(prog, "MyFirstTexture");
    glUniform1i(loc, 3);
    

    纹理单元和纹理对象之间的关联是通过上面已经显示的代码片段建立的:

    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D, texId);
    

    这两部分是将纹理连接到着色器代码中的采样器变量的关键部分。注意uniform变量的值是纹理单元的索引(3),而glActiveTexture()的参数是对应的枚举(GL_TEXTURE3)。我认为这是不幸的 API 设计,但你必须习惯它。

    一旦你理解了这一点,你如何在着色器中使用多个纹理(又名“多重纹理”)就有望非常明显:

    • 您的着色器代码中有多个采样器变量。
    • 您调用glUniform1i() 将采样器变量的值设置为不同纹理单元的索引。
    • 您将纹理绑定到每个匹配的纹理单元。

    使用纹理单元 0 和 1 显示两个纹理:

    glUseProgram(prog);
    
    GLint loc = glGetUniformLocation(prog, "MyFirstTexture");
    glUniform1i(loc, 0);
    loc = glGetUniformLocation(prog, "MySecondTexture");
    glUniform1i(loc, 1);
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texId0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texId1);
    

    另一种看待这一点的方式是,着色器中的采样器变量与纹理对象之间存在一定程度的间接性。着色器与纹理对象没有直接连接。相反,它有一个纹理对象表的索引(该索引是统一变量的值),而该表又包含指向纹理对象的“指针”(其中表条目填充有glActiveTexture()/glBindTexture( )`。

    或者使用通信术语对同一事物进行最后一个类比:您可以将纹理单元视为端口。您告诉着色器从哪些端口读取数据(统一变量的值)。然后将纹理插入端口(通过将其绑定到纹理单元)。着色器现在将从您插入端口的纹理中读取数据。

    【讨论】:

      【解决方案2】:

      每个纹理对象中都包含一个默认的采样器对象,当没有采样器对象绑定到相应的采样器单元时,它将用于从纹理中读取。修改这个对象的参数,提供了类似的glTexParameter函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-05
        • 1970-01-01
        相关资源
        最近更新 更多