所有关于OpenGL对象
OpenGL对象的标准模型如下。
对象具有状态。认为他们是一个struct
。因此,您可能有一个这样定义的对象:
struct Object
{
int count;
float opacity;
char *name;
};
该对象中存储有某些值,并且具有state。OpenGL对象也具有状态。
改变状态
在C / C ++中,如果您具有type的实例,则可以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;
}
纹理很有趣。它们代表绑定的特例。许多glBind*
调用都有一个“目标”参数。这表示OpenGL上下文中可以绑定该类型对象的不同位置。例如,您可以绑定帧缓冲区对象以进行读取(GL_READ_FRAMEBUFFER
)或写入(GL_DRAW_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;
}
请注意,现在,我们不仅具有的2D列表Object
,而且还具有当前对象的概念。我们有一个设置当前对象的函数,我们有一个最大数量的当前对象的概念,并且我们所有的对象操作函数都经过了调整,可以从当前对象中进行选择。
更改当前活动对象时,将更改整个目标位置集。因此,您可以绑定进入当前对象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
。采样器将根据采样器类型选择目标。因此,a sampler2D
将从GL_TEXTURE_2D
目标中选择。这就是采样器具有不同类型的原因之一。
现在,这听起来令人怀疑,就像您可以拥有两个GLSL采样器一样,它们的不同类型使用相同的纹理图像单元。但是你不能;OpenGL禁止这样做,并且在尝试渲染时会给您一个错误。
GL_TEXTURE0 + i
-我的意思是检查枚举值以查看其是否有效。最后一段-不知道那是否合法。优秀的!我正在为您的所有答案添加书签,以便我再次参考它们。