多纹理理论与纹理对象和采样器

The*_*ver 3 opengl textures

我找不到任何关于如何使用纹理对象或纹理对象加上采样器来编码多纹理的理论文章.我只是不知道如何管理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
Run Code Online (Sandbox Code Playgroud)

我有点困惑的是,多纹理是否在链接到多个纹理的片段代码中有多个采样器,或者是否可能只有一个具有多个纹理的采样器?

Ret*_*adi 13

其中大部分内容必须在之前解释过,但让我试着给出一个概述,希望能更清楚地说明所有不同的部分是如何组合在一起的.我将首先分别解释每个部分,然后解释它们是如何连接的.

纹理目标

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

glBindTexture(GL_TEXTURE_2D, texId1);
glBindTexture(GL_TEXTURE_3D, texId2);
Run Code Online (Sandbox Code Playgroud)

双方texId1texId2会被绑定到同一个纹理单元,因为它们绑定到不同的目标是可能的.

这个细节有点令人费解和困惑,我不会在其余的答案中考虑它.我建议你总是将不同的纹理绑定到不同的纹理单元.它将为您免除头痛和惊喜.

纹理对象

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

  • 纹理数据.
  • 定义如何对纹理数据进行采样的状态,如过滤使用的属性设置glTexParameteri().

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

纹理单位

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

您用于glActiveTexture()指定当前活动的纹理单位.然后,需要在特定纹理单元上操作的调用将在活动纹理单元上操作.例如:

glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, texId);
Run Code Online (Sandbox Code Playgroud)

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

采样器对象

这是OpenGL 3.3及更高版本中提供的一种新对象.你会不会需要这对于大多数使用情况下,即使它们涉及采样来自多个纹理.我把它们包括在这里是为了完整,但是在你牢牢掌握纹理对象和纹理单元之前,没有必要担心采样器.

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

这允许您做的是在同一着色器中采样具有不同采样参数的单个纹理.假设您想在单个着色器中对相同纹理进行LINEAR和NEAREST采样.如果没有采样器对象,如果没有相同纹理的多个副本(具有多个数据副本),则无法执行此操作.采样器对象启用此类功能.

纹理视图

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

当采样器从采样参数中去除纹理数据(及其相关格式)时,纹理视图将原始纹理数据与格式分离.它们使得可以使用具有不同格式的相同原始纹理数据.我怀疑你可以在没有使用此功能的情况下走很长的路.

把碎片放在一起

您最终想要做的是指定着色器应该采样的纹理.纹理单元是在着色器和纹理之间建立连接的关键部分.

从着色器的侧面看它,着色器知道它采样的纹理单元.这由采样器均匀变量的值给出.例如,如果"MyFirstTexture"是着色器代码中的采样器变量的名称,则以下内容指定该变量与纹理单元3相关联:

GLint loc = glGetUniformLocation(prog, "MyFirstTexture");
glUniform1i(loc, 3);
Run Code Online (Sandbox Code Playgroud)

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

glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, texId);
Run Code Online (Sandbox Code Playgroud)

这两个部分是将着色器连接到着色器代码中的采样器变量的关键部分.请注意,uniform变量的值是纹理unit(3)的索引,而参数glActiveTexture()是相应的enum(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);
Run Code Online (Sandbox Code Playgroud)

另一种看待这种情况的方法是着色器和纹理对象中的采样器变量之间存在一定程度的间接.着色器没有与纹理对象的直接连接.相反,它有一个纹理对象表的索引(其中这个索引是统一变量的值),而这个表又包含纹理对象的"指针"(表条目用glActiveTexture()/glBindTexture()填充).

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