GlActiveTexture 与 glBindTexture 的区别与联系

据我所知,glActiveTexture设置活动的“纹理单元”。每个纹理单元可以有多个纹理目标(通常是 GL _ TEXTURE _ 1D、2D、3D 或 CUBE _ MAP)。

如果我理解正确,你必须先调用 glActiveTexture来设置纹理单元(初始化为 GL_TEXTURE0) ,然后你绑定(一个或多个)“纹理目标”到该纹理单元?

可用的纹理单元的数量取决于系统。我在我的图书馆中看到多达32个枚举。我想这本质上意味着我可以有我的 GPU 的极限较小(我认为是 168)和32纹理在 GPU 内存在任何时候?我想还有一个额外的限制,我不超过我的 GPU 的最大内存(大概1GB)。

我是否正确理解了纹理目标和纹理单元之间的关系?假设我可以拥有16个单位,每个单位4个目标,这是否意味着可以容纳16 * 4 = 64个目标,或者它不是这样工作的?

接下来,您通常希望加载纹理。你可以通过 glTexImage2D做到这一点。第一个参数是纹理目标。如果这个 就像 glBufferData一样,那么我们实际上绑定“句柄”/“纹理名称”到纹理目标,然后将纹理数据加载到该目标中,从而间接地将它与该句柄关联起来。

glTexParameter呢?我们必须绑定一个纹理目标,然后再次选择相同的目标作为第一个参数?或者纹理目标不需要被绑定,只要我们有正确的活动纹理单元?

glGenerateMipmap操作一个目标太... 该目标仍然必须绑定到纹理名称为它成功?

然后,当我们想绘制我们的纹理对象,我们必须选择一个活动的纹理单元,然后纹理目标?或者我们选择一个纹理单元,然后我们可以从与该单元相关的4个目标中的任何一个获取数据?这就是让我困惑的地方。

40509 次浏览

想象一下 GPU 就像某个油漆加工厂。

有许多水箱,可以把染料送到喷漆机上。在绘画机中,然后将染料涂抹到物体上。那些坦克是 纹理单位

那些罐子可以配备不同种类的染料。每种染料都需要其他种类的溶剂。“溶剂”是 纹理目标。为了方便起见,每个储罐都连接到一些溶剂供应,但是每个储罐一次只能使用一种溶剂。有一个阀门/开关 TEXTURE_CUBE_MAP TEXTURE_3D TEXTURE_2D TEXTURE_1D。你可以在同一时间把所有的染料类型装入容器,但是由于只有一种溶剂进入,它将“稀释”只有那种染料匹配。所以你可以把每种质地都粘合起来,但是粘合剂中最重要的溶剂实际上会进入容器,和它所属的染料混合在一起。

然后是染料本身,它来自一个仓库,通过“捆绑”装入罐中。这就是你的口感。

我来试试!这一切并不复杂,只是一个术语的问题,希望我能说清楚。


您可以创建大约与系统中可用内存数量相当的 纹理对象。这些对象包含由 参数提供的纹理的实际数据(texel)和参数(参见 常见问题)。

在创建时,必须为一个纹理对象分配一个 纹理目标,该纹理对象表示纹理的类型(GL_TEXTURE_2DGL_TEXTURE_3DGL_TEXTURE_CUBE、 ...)。

这两个项目,纹理对象纹理目标代表了纹理数据。我们稍后再来看它们。

纹理单元

现在,OpenGL 提供了一个 纹理单位数组,可以在绘图时同时使用。数组的大小取决于 OpenGL 系统,你的系统有8个。

您可以 绑定一个纹理对象到一个纹理单元使用给定的纹理,而绘制。

在一个简单易行的世界里,为了用给定的纹理来绘制,你需要将一个纹理对象绑定到纹理单元,然后执行(伪代码) :

glTextureUnit[0] = textureObject

由于 GL 是一种状态机,所以它不能以这种方式工作。假设我们的 textureObjectGL_TEXTURE_2D纹理目标的数据,我们将把前面的赋值表示为:

glActiveTexture(GL_TEXTURE0);                   // select slot 0 of the texture units array
glBindTexture(GL_TEXTURE_2D, textureObject);    // do the binding

请注意,GL_TEXTURE_2D实际上取决于要绑定的纹理的类型。

纹理对象

在伪代码中,要设置纹理数据或纹理参数,例如:

setTexData(textureObject, ...)
setTexParameter(textureObject, TEXTURE_MIN_FILTER, LINEAR)

OpenGL 不能直接操作纹理对象、更新/设置其内容或更改其参数,必须首先将它们绑定到活动纹理单元(无论是哪个)。等效的代码是:

glBindTexture(GL_TEXTURE_2D, textureObject)       // this 'installs' textureObject in texture unit
glTexImage2D(GL_TEXTURE_2D, ...)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

着色器

着色器可以访问所有的纹理单元,他们不关心活动纹理。

采样器制服是代表用于采样器的纹理单元索引的 int值(以及用于采样器的纹理对象的 没有值)。

所以你必须将你的纹理对象绑定到你想要使用的单位。

采样器的类型将与纹理单元中使用的纹理目标进行匹配: Sampler2D代表 GL_TEXTURE_2D,依此类推..。

关于 OpenGL 对象

OpenGL 对象的标准模型如下。

对象有状态。把它们想象成一个 struct。所以你可能有一个这样定义的对象:

struct Object
{
int count;
float opacity;
char *name;
};

该对象有一定的值存储在其中,它有 国家。 OpenGL 对象也有状态。

改变状态

在 C/C + + 中,如果你有一个类型为 Object的实例,你可以像下面这样改变它的状态: obj.count = 5;你可以直接引用对象的一个实例,得到你想要改变的特定状态,然后把一个值放进去。

在 OpenGL 中,不要这样做。

出于遗留原因最好不要解释,要更改 OpenGL 对象的状态,必须首先将其 绑定到上下文中。这是完成了一些从 glBind*调用。

C/C + + 的等价物如下:

Object *g_objs[MAX_LOCATIONS] = {NULL};
void BindObject(int loc, Object *obj)
{
g_objs[loc] = obj;
}

纹理很有趣,它们代表了绑定的一种特殊情况。许多 glBind*调用都有一个“ target”参数。这表示 OpenGL 上下文中可以绑定该类型对象的不同位置。例如,可以绑定一个用于读(GL_READ_FRAMEBUFFER)或写(GL_DRAW_FRAMEBUFFER)的 framebuffer 对象。这会影响 OpenGL 使用缓冲区的方式。这就是上面的 loc参数所表示的。

纹理是特殊的,因为当您 第一将它们绑定到目标时,它们将获得特殊的信息。当您第一次将一个纹理绑定为 GL_TEXTURE_2D时,您实际上是在设置纹理中的特殊状态。你是说这个纹理是2D 纹理。它将 一直都是是一个2D 纹理,这种状态不能改变 永远不会。如果一个纹理最初是作为 GL_TEXTURE_2D绑定的,那么您必须将其作为 GL_TEXTURE_2D绑定; 尝试将其作为 GL_TEXTURE_1D绑定将导致错误(在运行时)。

绑定对象后,可以更改其状态。这是通过特定于该对象的泛型函数完成的。它们也采用一个表示要修改哪个对象的位置。

在 C/C + + 中,这看起来像是:

void ObjectParameteri(int loc, ObjectParameters eParam, int value)
{
if(g_objs[loc] == NULL)
return;


switch(eParam)
{
case OBJECT_COUNT:
g_objs[loc]->count = value;
break;
case OBJECT_OPACITY:
g_objs[loc]->opacity = (float)value;
break;
default:
//INVALID_ENUM error
break;
}
}

注意这个函数如何设置当前绑定的 loc值。

对于纹理对象,主要的纹理状态变化函数是 glTexParameter。改变纹理状态的其他函数只有 glTexImage函数及其变体(glTexImage0、 glTexImage1、最近的 glTexImage2)。不同的 SubImage版本改变了纹理的内容,但是他们在技术上并没有改变它的 glTexImage3。Image函数分配纹理存储并设置纹理的格式; SubImage函数只复制像素。这不被认为是纹理的状态。

请允许我重复: 这些是修改纹理状态的 只有函数。glTexEnv修改环境状态; 它不影响存储在纹理对象中的任何东西。

主动纹理

纹理的情况更加复杂,同样是由于遗留的原因,最好不要公开。这就是 glActiveTexture的用武之地。

对于纹理,不仅有目标(GL_TEXTURE_1DGL_TEXTURE_CUBE_MAP等)。也有纹理 单位。在我们的 C/C + + 例子中,我们得到的是:

Object *g_objs[MAX_OBJECTS][MAX_LOCATIONS] = {NULL};
int g_currObject = 0;


void BindObject(int loc, Object *obj)
{
g_objs[g_currObject][loc] = obj;
}


void ActiveObject(int currObject)
{
g_currObject = currObject;
}

注意,现在我们不仅有一个 Objects 的2D 列表,而且还有一个当前对象的概念。我们有一个设置当前对象的函数,我们有一个当前对象的最大数量的概念,并且我们所有的对象操作函数都可以从当前对象中进行选择。

更改当前活动对象时,将更改整个目标位置集。因此,您可以绑定进入当前对象0的内容,切换到当前对象4,并将修改一个完全不同的对象。

这种与纹理对象的类比是完美的... 几乎。

看,glActiveTexture不接受整数; 它接受 枚举数。这在理论上意味着它可以采取任何从 GL_TEXTURE0GL_TEXTURE31。但有一件事你必须明白:

这是假的!

glActiveTexture可以采取的实际范围由 GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS控制。这是一个实现所允许的同时多纹理的最大数量。这些都分为不同的着色器阶段不同的分组。例如,在 GL 3.x 类硬件上,可以得到16个顶点着色器纹理、16个片段着色器纹理和16个几何着色器纹理。因此,GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS将是48。

但并没有48个统计员。这就是为什么 glActiveTexture不使用枚举器。正确调用 glActiveTexture的方式如下:

glActiveTexture(GL_TEXTURE0 + i);

其中 i是介于0和 GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS之间的一个数字。

渲染

那么这些和渲染有什么关系呢?

在使用着色器时,您可以将采样器制服设置为一个纹理图像单元(glUniform1i(samplerLoc, i),其中 i是图像单元)。表示与 glActiveTexture一起使用的数字。取样器将根据取样器类型选择目标。因此 sampler2D将从 GL_TEXTURE_2D目标中选择。这就是为什么采样器有不同类型的原因之一。

现在,这听起来可疑的像你可以有两个 GLSL 采样器,不同的 类别使用相同的纹理图像单元。但是您不能; OpenGL 禁止这样做,并且在您尝试呈现时会给您一个错误。