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 ++中,如果你有一个Object
types的实例,你可以改变它的状态,如下所示: 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
函数及其变体( glCompressedTexImage
, glCopyTexImage
,最近的glTexStorage
)。 各种SubImage
版本改变了纹理的内容,但是在技术上并没有改变它的状态 。 Image
函数分配纹理存储并设置纹理的格式; SubImage
函数只是复制像素。 这不被视为纹理的状态。
请允许我重复一遍:这些是修改纹理状态的唯一函数。 glTexEnv
修改环境状态; 它不会影响存储在纹理对象中的任何东西。
活跃的纹理
纹理的情况更为复杂,同样由于遗留的原因,最好不要公布。 这是glActiveTexture
进来的地方。
对于纹理,不仅有目标( GL_TEXTURE_1D
, GL_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_TEXTURE0
到GL_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_2D
, GL_TEXTURE_3D
, GL_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_MAP
, TEXTURE_3D
, TEXTURE_2D
, TEXTURE_1D
。 您可以将所有染料types同时填充到jar中,但由于只有一种溶剂进入,所以只会“稀释”染料匹配的种类。 所以你可以有各种质地的约束,但与“最重要的”溶剂的约束力,实际上会进入坦克,并与其所属的染料混合。
然后是染料本身,它来自一个仓库,并通过“绑定”到jar中。 那是你的质感。