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

时间:2015-06-21 01:19:08

标签: 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

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

2 个答案:

答案 0 :(得分:12)

之前必须解释的大部分内容,但让我试着概述一下,希望能让所有不同的部分更加清晰。我将首先分别解释每个部分,然后解释它们是如何连接的。

纹理目标

这指的是不同类型的纹理(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"是着色器代码中的sampler变量的名称,以下指定变量与纹理单元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()填充。

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

答案 1 :(得分:0)

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