glActiveTexture和glBindTexture之间的差异和关系

从我所收集的, glActiveTexture设置活动的“纹理单元”。 每个纹理单元可以有多个纹理目标(通常是GL_TEXTURE_1D,2D,3D或CUBE_MAP)。

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

可用纹理单元的数量取决于系统。 我在库中看到多达32个枚举。 我想这基本上意味着我可以拥有我的GPU的极限(我认为是这样) 16 8)和32个纹理在GPU内存中的任何一个时间? 我想有一个额外的限制,我不超过我的GPU的最大内存(据说1 GB)。

我是否正确理解纹理目标和纹理单元之间的关系? 假设我有16个单位和4个目标,这是否意味着有16 * 4 = 64个目标的空间,还是不能这样工作?

接下来你通常要加载一个纹理。 你可以通过glTexImage2D来做到这一点。 其中的第一个参数是纹理目标。 如果像glBufferData这样工作 ,那么我们将“handle”/“texture name”绑定到纹理目标,然后将纹理数据加载到该目标中,从而间接将其与该句柄关联。

那么glTexParameter呢? 我们必须绑定纹理目标,然后再select同一个目标作为第一个参数? 或者只要我们拥有正确的活动纹理单元,纹理目标就不需要绑定了。

glGenerateMipmap也在目标上运行…目标必须仍然绑定到纹理名称才能成功?

那么当我们想要绘制具有纹理的对象时,我们是否必须同时select一个活动的纹理单元,然后select一个纹理目标? 或者我们select一个纹理单元,然后我们可以从与这个单元相关的4个目标中的任何一个中获取数据? 这是真的让我困惑的部分。

所有关于OpenGL对象

OpenGL对象的标准模型如下。

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

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

该对象具有存储在其中的某些值并且具有状态 。 OpenGL对象也有状态。

改变状态

在C / C ++中,如果你有一个Objecttypes的实例,你可以改变它的状态,如下所示: 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*调用有一个“目标”参数。 这表示OpenGL上下文中可以绑定该types的对象的不同位置。 例如,您可以绑定一个帧缓冲区对象进行读取( GL_READ_FRAMEBUFFER )或写入( GL_WRITE_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函数及其变体( glCompressedTexImageglCopyTexImage ,最近的glTexStorage )。 各种SubImage版本改变了纹理的内容,但是在技术上并没有改变它的状态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; } 

请注意,现在,我们不仅拥有Object的2D列表,而且还具有当前对象的概念。 我们有一个函数来设置当前对象,我们有最大数量的当前对象的概念,并且我们所有的对象操作函数都被调整来从当前对象中进行select。

当您更改当前活动的对象时,将更改整个目标位置集。 所以你可以绑定进入当前对象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 。 采样器将根据采样器typesselect目标。 所以sampler2D会从GL_TEXTURE_2D目标中select。 这是采样器具有不同types的原因之一。

现在这听起来像可以有两个GLSL采样器,不同的types使用相同的纹理图像单元。 但你不能; OpenGL禁止这个,当你尝试渲染时会给你一个错误。

我会试试看! 所有这些都不是那么复杂,只是一个术语问题,希望我能说清楚。


您可以创build大致与您的系统中可用的内存一样多的纹理对象 。 这些对象包含您的纹理的实际数据(texels)以及由glTexParameter提供的参数 (参见FAQ )。

创build时,必须将一个纹理目标指定给一个纹理对象,该纹理对象表示纹理的types( GL_TEXTURE_2DGL_TEXTURE_3DGL_TEXTURE_CUBE ,…)。

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

纹理单元

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

您可以纹理对象绑定到纹理单元以在绘制时使用给定的纹理。

在一个简单而简单的世界中,要使用给定的纹理进行绘制,您需要将纹理对象绑定到纹理单元,然后执行(伪代码):

 glTextureUnit[0] = textureObject 

由于GL是一个状态机,它唉,不能这样工作。 假设我们的textureObject具有GL_TEXTURE_2D纹理目标的数据,我们将把前面的赋值表示为:

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

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

纹理对象

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

 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值,表示用于采样器(而不是要使用的纹理对象)的纹理单元的索引。

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

采样器的types将与纹理单元中使用的纹理目标进行匹配: Sampler2D for GL_TEXTURE_2D ,等等…

想象一下像一些油漆加工厂的GPU。

有一些坦克,它把染料送到一些喷漆机器上。 然后在涂漆机中将染料涂抹在物体上。 那些坦克是纹理单位

那些坦克可以配备不同种类的染料。 每种染料都需要一些其他种类的溶剂。 “溶剂”是纹理目标 。 为了方便起见,每个jar连接到一些溶剂供应,但是每个jar中一次只能使用一种溶剂。 所以有一个阀门/开关TEXTURE_CUBE_MAPTEXTURE_3DTEXTURE_2DTEXTURE_1D 。 您可以将所有染料types同时填充到jar中,但由于只有一种溶剂进入,所以只会“稀释”染料匹配的种类。 所以你可以有各种质地的约束,但与“最重要的”溶剂的约束力,实际上会进入坦克,并与其所属的染料混合。

然后是染料本身,它来自一个仓库,并通过“绑定”到jar中。 那是你的质感。