glActiveTexture和glBindTexture的区别和联系

原文链接

问:

据我所知,glActiveTexture 设置激活的纹理单元(texture unit)。每一个纹理单元有多个纹理目标(texture targets)选择(GL_TEXTURE_1D, 2D, 3D or CUBE_MAP之一)。

如果我没有理解错, 必须先调用glActiveTexture 设置纹理单元(初始化为GL_TEXTURE0), 然后绑定纹理目标(一个或多个)到纹理单元(译:只能为一个)。

不同的系统,纹理单元可用的数量是不同的。我在库文件中看到GL_TEXTURE0一直到32。

我理解的纹理单元和纹理目标正确吗? 假设有16个纹理单元和4个纹理目标,是否意味着一共有16*4个纹理目标的空间? 还是他们压根不是这样工作的?

下一步我们一般会加载纹理。可以调用glTexImage2D完成,他的第一个参数就时纹理目标。如果像glBufferData*(参数glBufferData(GL_ARRAY_BUFFER, sizeof(pts), pts, GL_STATIC_DRAW))一样工作的话,我们实际上绑定了 “纹理句柄”/“纹理名称”* 到纹理目标,加载纹理数据到纹理目标,然后关联到纹理句柄。

glTexParameter函数的作用是什么?我们必须绑定纹理目标,然后选择相同的纹理目标作为第一个参数?或者纹理目标不需要绑定直到激活正确地纹理单元?

glGenerateMipmap 也需要传入target …

当我们渲染物体时,必须要激活纹理单元(texture unit), 然后绑定纹理目标(texture target)吗?或者,我们选择纹理单元,然后可以获取四个纹理单元中的任意一个(GL_TEXTURE_1D, 2D, 3D or CUBE_MAP)? 这个很困扰我。


答 1:

所有的OpenGL对象都差不多,它们都有状态。想象一下它们是类似如下的结构体。

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

此结构体有几个状态成员,OpenGL同样也有状态。

改变状态

在 C/C++中,如果你有一个实例,可以按如下改变其状态obj.count = 5; 你可以直接去获取对象属性,然后取直接对它赋值。

但是在OpenGL中,你不能这么做。

由于历史遗留原因(或无法解释的其它原因),在OpenGL中你想改变对象的状态,你必须绑定它到上下文。这也就是为什么先要调用 glBind* 函数。

glBind* 函数类似于C/C++ 中如下操作:

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

纹理很有趣,它的绑定比较特殊。很多 glBind*函数都有“目标”(target)参数。这代表不同的位置在OpenGL上下文中。比如绑定帧缓时,有参数可选(GL_FRAMEBUFFER/GL_READ_FRAMEBUFFER/GL_Draw_FRAMEBUFFER), 这将会影响OpenGL对frame buffer的使用。这和上面的loc参数相对应。

纹理特别之处在于当你绑定到目标时,OpenGL得到一些别的信息。当你先绑定纹理到GL_TEXTURE_2D, 你实际上在告诉OpenGL,此纹理为2D纹理。然后它必须一直是2D纹理,不能更改。当你试图绑定它到GL_TEXTURE_1D 或者别的纹理目标时,都会在运行时报错。

在绑定之后,调用诸如 glTexImage2D/glTexParameteri等函数,更改的就是当前绑定的纹理对象。

在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, etc),还有纹理单元(texture units)。 类似于下面的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;
}

注意上面多了一个g_currObject 对象,它就是当前我们激活的纹理单元(ActiveObject和glActiveTexture 相对应)。我们可以设置当前激活的对象,当前激活的纹理单元数量是有最大值的。我们所有的操作都操作是当前纹理单元下的对象。

当你改变了当前激活的纹理单元,你改变了整个的纹理目标的位置(如一开始激活的问纹理单元0, 所有的操作都是更改g_objs[0][loc], 当你改变纹理单元为1时,所有的更改都是g_objs[1][loc]。前面说过,loc指的时纹理目标,即为GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAP等, 一旦绑定就不能更改 )。

上面代码的类比几近完美☺☺☺

glActiveTexture 的参数不是整数,而是枚举类型。如果你认为可用的纹理单元是从GL_TEXTURE0 到GL_TEXTURE31,那么你就错了

glActiveTexture 的实际范围可以通过GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS查询。这是最大可用的纹理单元数。它们被分为几组,给不同的shader阶段。比如GL 3.x, 顶点着色器分得16个纹理单元,几何着色器和片元着色器同样也有16个。因此,GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 查询得到的最大值为48。

glActiveTexture(GL_TEXTURE0 + i);
where i is a number between 0 and GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.
因此你应该这样调用glActiveTexture(GL_TEXTURE0 + i); i为0到GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS的整数。

渲染

渲染时,你需要做什么呢?

当使用shader,需要设置sampler uniforms到纹理单元。这代表你当前激活的纹理单元(用glActiveTexture设置)。

    glActiveTexture(GL_TEXTURE0 + index);
    glBindTexture(GL_TEXTURE_2D, texture);
    GLuint loc = glGetUniformLocation(program, name);
    glUniform1i(loc, index);

采样器会根据采样器类型自己选择采样目标(如sampler2D和GL_TEXTURE_2D对应)。这也是为什么采样器为什么有不同的类型,而不是统一叫sampler。

现在听起来你在glsl可以有2个不同类型的纹理采样器,绑定到同一个纹理单元。

glActiveTexture(GL_TEXTURE0);   
glBindTexture(GL_TEXTURE_2D,texture1);  
glBindTexture(GL_TEXTURE_2D, texture2);
然后在shader中有两个采样器分别跟他们对应。

但是OpenGL禁止这样做。
by Nicol Bolas


答 2

我试着答一下! 这个问题并不复杂,只是涉及到几个术语而已,希望我能说清楚。
你可以生成足够多的纹理对象,只要你内存用。这些纹理对象设置纹理数据和状态, 可以通过glTexParameter 和glTexImage2D设置。
当纹理对象被创建,你需要绑定到纹理目标(GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE, …)和纹理单元。

纹理单元(Texture units)

OpenGL提供一组可用的纹理单元,它们可以渲染时同时使用。它们的大小取决于OpenGL的不同实现,在我的电脑上是8。
要想在渲染时使用纹理对象,你必须绑定纹理对象到纹理单元。下面是伪代码:

glTextureUnit[0] = textureObject

上面的代码当然不能直接在OpenGL中使用,假设纹理对象是2D纹理。OpenGL中代码如下:

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

纹理对象(Texture objects)

在伪代码中,设置纹理数据和纹理状态,代码如下:

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)

Shaders

Shader可以直接访问所有的纹理单元,它们不关心激活的纹理单元。

采样器代表纹理单元的索引,这也是为什么需要绑定纹理对象到纹理单元。

不同类型的采样器对应不同的纹理目标:Sampler2D 和 GL_TEXTURE_2D,….

下面是给采样器uniform赋值:

    glActiveTexture(GL_TEXTURE0 + 1); //激活纹理单元1
    glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
    GLuint loc = glGetUniformLocation(program, name);
    glUniform1i(loc, 1);//通知采样器,让其知道它代表的时纹理单元1

by rotoglup


答 3

Imagine the GPU like some paint processing plant.

There are a number of tanks, which delivers dye to some painting machine. In the painting machine the dye is then applied to the object. Those tanks are the texture units

Those tanks can be equipped with different kinds of dye. Each kind of dye requires some other kind of solvent. The “solvent” is the texture target.

For convenience each tank is connected to some solvent supply, and but only one kind of solvent can be used at a time in each tank. So there’s a valve/switch “TEXTURE_CUBE_MAP”, “TEXTURE_3D”, “TEXTURE_2D”, “TEXTURE_1D”. You can fill all the dye types into the tank at the same time, but since only one kind of solvent goes in, it will “dilute” only the kind of dye matching. So you can have each kind of texture being bound, but the binding with the “most important” solvent will actually go into the tank and mix with the kind of dye it belongs to.

And then there’s the dye itself, which comes from a warehouse and is filled into the tank by “binding” it. That’s your texture.
by datenwolf

  • 15
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值