Refrence:
- https://blog.csdn.net/n5/article/details/90045648
- https://blog.csdn.net/hankern/article/details/112058229
- https://blog.csdn.net/huazi5dgan/article/details/76160334
- https://blog.csdn.net/zhuyingqingfen/article/details/19968341
- https://blog.csdn.net/candycat1992/article/details/8974719
简单的理解:GPU(显卡)中有N个纹理单元texture unit
(具体数量依赖你的GPU能力),每个纹理单元(texture unit,GL_TEXTURE0、GL_TEXTURE1…)都有GL_TEXTURE_1D
、GL_TEXTURE_2D
等textureTarget
纹理目标接口。
Android系统默认有从GL_TEXTURE0
到GL_TEXTURE31
共32个纹理单元,不过不同的平台或不同的Android版本,可用的纹理单元数量可能是不同的。
比如,在Pixel3XL+Android11上,可用的TextureUnit共有96个:
// [GL_TEXTURE0 ~ GL_TEXTURE31 + 64]
// "(不在这个区间就会打印警告“bindTextureImage: clearing GL error: 0x500”)"
GLES20.glActiveTexture(GL_TEXTURE0);
glActiveTexture函数
默认情况下当前活跃的纹理单元为0,即如果不调用
glActiveTexture
函数,默认使用纹理单元0。
struct TextureUnit
{
GLuint targetTexture1D;
GLuint targetTexture2D;
GLuint targetTexture3D;
GLuint targetTextureCube;
...
};
TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS]
GLuint currentTextureUnit = 0;
从下面代码中可以看到:当调用glBindTexture
绑定纹理对象到纹理目标时,所作用的是当前活跃的纹理单元:
void glActiveTexture(GLenum textureUnit)
{
currentTextureUnit = textureUnit - GL_TEXTURE0 ;
}
void glBindTexture(GLenum textureTarget, GLuint textureObject)
{
TextureUnit *texUnit = &textureUnits[currentTextureUnit];
switch(textureTarget)
{
case GL_TEXTURE_1D: texUnit->targetTexture1D = textureObject; break;
case GL_TEXTURE_2D: texUnit->targetTexture2D = textureObject; break;
case GL_TEXTURE_3D: texUnit->targetTexture3D = textureObject; break;
case GL_TEXTURE_CUBEMAP: texUnit->targetTextureCube = textureObject; break;
}
}
glBindTexture函数
将一个命名的纹理绑定到一个纹理目标上
//这个更好理解 void glBindTexture(GLenum textureTarget, GLuint textureObject)
void glBindTexture(GLenum target, GLuint texture);
-
target
纹理对象要绑定到的目标。必须是下面中的一个:GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BUFFER, GL_TEXTURE_2D_MULTISAMPLE 或者 GL_TEXTURE_2D_MULTISAMPLE_ARRAY。
-
texture
纹理对象(纹理对象分配号),取值范围为从
Integer.MIN_VALUE
到Integer.MAX_VALUE
,可通过glGenTextures
得到。
int[] textures = new int[3];
GLES20.glGenTextures(textures.length, textures, 0);
// --------
// SurfaceTexture surfaceTextureTemp = new SurfaceTexture(textures[0]);
// surfaceTextureTemp.detachFromGLContext(); // 不调用会抛异常
// surfaceTextureTemp.attachToGLContext(textures[1]);
// --------
// 将SurfaceTexture纹理对象附加到当前的GLContext上下文中
// surfaceTexture纹理对象在当前GLContext上下文中的分配号是textures[0]
surfaceTexure.attachToGLContext(textures[0]);
...
// 选择当前活动的纹理单元,不调用此代码默认使用纹理单元0
GLES20.glActiveTexture(GL_TEXTURE0);
// 将分配号为textures[0]的纹理对象绑定到当前活动纹理单元的纹理目标GLES11Ext.GL_TEXTURE_EXTERNAL_OES上
// 纹理对象分配号的取值为Integer.MIN_VALUE~Integer.MAX_VALUE
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
...
当一张纹理被绑定后,GL对于这个纹理目标的操作都会影响到这个被绑定的纹理。也就是说,这个纹理目标成为了被绑定到它上面的纹理的别名,而纹理名称为0则会引用到它的默认纹理。之后可能还会调用如下方法:
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,512,512,0,GL_RGBA, GL_UNSIGNED_BYTE,NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
这些函数里面的GL_TEXTURE_2D就等价与我们之前绑定的纹理,所以我们对GL_TEXTURE_2D的操作就会影响到之前的纹理,这和C++中的引用有点类似。
glBindTexture的使用方法实际上体现在两个地方:
- 首先是第一次载入纹理对象时,将载入的纹理对象(int型的纹理分配号)与纹理单元绑定在一起。也就是给当前的纹理分配相应的纹理号。
- 其次是载入多个纹理之后,需要在多个纹理之间进行切换,此时也需要使用glBindTexture函数进行切换。(这里是有正确? 应该是可通过需要glActiveTexture切换)
-
在openGL中,存在一系列的
texture unit
,通过 glActiveTexture 选择当前的texture unit
,默认的unit是0。而当前的texture unit
中存在多个texture target
,例如GL_TEXTURE_2D, GL_TEXTURE_CUBEMAP…。 -
通过
glCreateTexture
创建texture object
后,第一次调用glBindTexture时,决定了texture object
的类型,比如调用的是glBindTexture(GL_TEXTURE_2D,0)
,那么这个texture object
就是一个2D texture,其内部状态被初始化为2D texture的状态,它不能再被bind到其他类型的texture target上,否则会产生运行时错误。 -
通过glBindTexture将一个texture object绑定到当前激活的texture unit的texture target上。然后通过glTexImage2D, glTexParameteri等函数改变texture object的状态。
-
创建texture object的时候需要指定texture unit吗?
并不需要,无论当前是哪个texture unit,不影响创建texture object。创建好的texture object可以绑定到其他texture unit的texture target上使用。
-
什么时候需要关心texture unit?
当使用多重纹理的时候,也就是说在shader里面要同时使用多于一个sampler的时候。通过glUniform1i将texture unit传给sampler,让sampler知道应该去哪个texture unit中获取texture object,那么应该获取哪个texture target指向的texture object呢?这就要看sampler的类型了。比如sampler2D,就会获取sampler被指向的texture unit中的GL_TEXTURE_2D texture targert。
总结:
openGL中纹理的状态分为texture unit和texture object包含的状态。texture unit的状态包括当前激活的unit,每个unit下面的各个target分别指向哪些texture object。texture object的状态包含type, texParam, format等等。
什么时候需要调用glActiveTexture以及glBindTexture就要看状态是否会改变。
对于shader来说,他可以访问所有的texture unit中指定的texture object。只要你告诉他某个sampler使用哪个unit就行。如果每个unit的内容指定后不需要改变,则即便shader使用了多个sampler也不需要来回切换unit的状态。当然更常见的是渲染完一个pass后,需要改变当前texture unit中某target中的texture object,也就是需要换贴图了。那么标准的操作就是先glActiveTexture,然后glBindTexture。当然如果你只使用unit0,则不需要调用glActiveTexture。
- glActiveTexture负责选择当前活跃的纹理单元
- glBindTexture负责使用纹理单元中的某一种纹理类型
一个纹理单元可以有多种纹理类型GL_TEXTURE_1D、GL_TEXTURE_2D等,但是只能使用一种,不可以多种同时使用。或者可以理解为纹理单元有多种绑定点,但一次只能绑定到一种类型上。给着色器上传的是纹理单元号。
调用顺序:先调用glActiveTexture,再调用glBindTexture
如果使用了帧缓冲区,则调用顺序为glBindFramebuffer、glBindTexture,不需要glActiveTexture
glBindFramebuffer后调用glActiveTexture会有什么效果,应该没有直接影响,但是glActiveTexture会影响纹理贴图,进而会影响渲染到帧缓冲区中的内容。
也就是glBindFramebuffer是确定画布,glActiveTexture是确定绘画的颜料。