glActiveTexture和glBindTexture之间的差异和关系

mpe*_*pen 129 opengl textures

从我收集的内容中,glActiveTexture设置活动的"纹理单元".每个纹理单元可以具有多个纹理目标(通常为GL_TEXTURE_1D,2D,3D或CUBE_MAP).

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

可用的纹理单元的数量取决于系统.我在我的图书馆看到最多32个枚举的枚举.我想这实际上意味着我可以拥有GPU的极限(我认为是这样)168)在任何时候GPU内存中都有32个纹理?我想还有一个额外的限制,我没有超过GPU的最大内存(据说是1 GB).

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

接下来,您通常要加载纹理.你可以通过这样做glTexImage2D.第一个参数是纹理目标.如果这样工作glBufferData,那么我们基本上将"句柄"/"纹理名称"绑定到纹理目标,然后将纹理数据加载到该目标中,从而间接地将其与该句柄相关联.

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

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

然后,当我们要引起我们的对象上有一个纹理,我们要不要选择积极的纹理单元,然后一个纹理目标?或者我们选择一个纹理单元,然后我们可以从与该单元相关的4个目标中的任何一个中获取数据?这是让我很困惑的部分.

Nic*_*las 246

关于OpenGL对象

OpenGL对象的标准模型如下.

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

struct Object
{
    int count;
    float opacity;
    char *name;
};
Run Code Online (Sandbox Code Playgroud)

该对象具有存储在其中的某些值,并且具有状态.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;
}
Run Code Online (Sandbox Code Playgroud)

纹理很有趣; 它们代表了绑定的特殊情况.许多glBind*调用都有一个"目标"参数.这表示OpenGL上下文中可以绑定该类型的对象的不同位置.例如,您可以绑定framebuffer对象以进行reading(GL_READ_FRAMEBUFFER)或写入(GL_DRAW_FRAMEBUFFER).这会影响OpenGL使用缓冲区的方式.这是loc上面的参数所代表的.

纹理是特殊的,因为当您第一次将它们绑定到目标时,它们会获得特殊信息.首次将纹理绑定为a时GL_TEXTURE_2D,实际上是在纹理中设置特殊状态.你是说这个纹理是2D纹理.它将始终是2D纹理; 这种状态永远无法改变.如果你的纹理首先被绑定为a 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

注意这个函数如何设置当前绑定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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,现在,我们不仅拥有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);
Run Code Online (Sandbox Code Playgroud)

其中i是0到0之间的数字GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.

渲染

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

使用着色器时,将采样器制服设置为纹理图像单元(图像单元glUniform1i(samplerLoc, i)在哪里i).这表示您使用的数字glActiveTexture.采样器将根据采样器类型选择目标.所以一个人sampler2D会从GL_TEXTURE_2D目标中挑选.这是采样器具有不同类型的一个原因.

现在这听起来很可疑,就像你可以有两个GLSL采样器,不同的类型使用相同的纹理图像单元.但你不能; OpenGL禁止此操作,并在您尝试渲染时给出错误.

  • 哇!另一个很棒的答案 - 感谢Nicol!我特别喜欢关于2D纹理的那个段落*总是*是2D纹理.我现在正在围绕其中的一些东西构建一个包装器,我不确定是否应该保持这种改变.关于`GL_TEXTURE0 + i`的部分 - 我的意思是检查枚举值,看看它是否有效.最后一段 - 不知道这是否合法.优秀!我正在为你的所有答案加书签,所以我可以再次参考它们. (11认同)
  • @Nicol Bolas:这个解释得非常好.您应该将其中的一部分复制到在线opengl书中的纹理章节中.我认为这更加清晰,可以很好地赞美这一章. (6认同)
  • @Nicol Bolas我刚开始学习OpenGL,这个答案给了我很多帮助.谢谢! (3认同)
  • @Nicol:哇,我之前对此有最好的定义来自你的arcsynthesis教程,现在你已经超越了那个出色的资源.谢谢 (3认同)
  • 嘿nico,只是想指出你的小错字:它是GL_DRAW_FRAMEBUFFER而不是GL_WRITE_FRAMEBUFFER (2认同)

rot*_*lup 18

我试试吧!所有这一切都不是那么复杂,只是一个条款问题,希望我能说清楚.


您可以创建大致与系统中可用内存一样多的纹理对象.这些对象包含纹理的实际数据(纹素)以及glTexParameter提供的参数(请参阅常见问题解答).

在创建时,你必须指定一个纹理目标到一个纹理对象,它代表纹理(类型GL_TEXTURE_2D,GL_TEXTURE_3D,GL_TEXTURE_CUBE,...).

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

纹理单位

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

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

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

glTextureUnit[0] = textureObject
Run Code Online (Sandbox Code Playgroud)

由于GL是一个状态机,唉,它不会这样工作.假设我们textureObjectGL_TEXTURE_2D纹理目标的数据,我们将以前的分配表达为:

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

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

纹理对象

在伪代码中,要设置纹理数据或纹理参数,您可以这样做:

setTexData(textureObject, ...)
setTexParameter(textureObject, TEXTURE_MIN_FILTER, LINEAR)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

着色器

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

采样器制服是int表示用于采样器的纹理单元的索引(而不是要使用的纹理对象)的值.

因此,您必须将纹理对象绑定到要使用的单位.

采样器的类型将与纹理单元中使用的纹理目标匹配:Sampler2Dfor GL_TEXTURE_2D,等等......


dat*_*olf 12

想象一下GPU就像一些油漆加工厂.

有许多坦克,它们为一些喷漆机提供染料.然后在涂漆机中将染料施加到物体上.那些坦克是纹理单位

这些坦克可以配备不同种类的染料.每种染料都需要其他种类的溶剂."溶剂"是纹理目标.为方便起见,每个罐连接到一些溶剂供应,但每个罐中一次只能使用一种溶剂.所以这是一个阀/开关TEXTURE_CUBE_MAP,TEXTURE_3D,TEXTURE_2D,TEXTURE_1D.您可以同时将所有染料类型填充到罐中,但由于只有一种溶剂进入,它将仅"稀释"染料匹配的种类.因此,您可以将各种纹理绑定,但与"最重要"溶剂的结合实际上会进入罐中并与其所属的染料混合.

然后是染料本身,它来自仓库,并通过"绑定"它填充到罐中.那是你的质地.

  • 有点奇怪的比喻...我不确定它是否真的可以清除所有内容。特别是有关“稀释”和“最重要的溶剂”的部分。您是说如果我同时绑定2d纹理和3d纹理,则只能使用其中之一,还是什么?哪一个最重要? (2认同)
  • @Mark:嗯,我试图用一个使用文字染料(比如油基和水基)的画家说话.无论如何,是的,如果绑定并启用多个纹理目标,则优先:CUBE_MAP> 3D> TEXTURE_ARRAY> 2D> 1D. (2认同)