我正在Android上的OpenGL ES 2.0中实现一个引擎。我有多个着色器,对象可以采用多个纹理。
我的目标是最小化对OpenGL的纹理绑定调用,因此我保留每个着色器的当前绑定纹理列表,并且只有在纹理发生变化时才调用glBindTexture()
。
例如我有一个天空盒着色器,我只用它绘制一个对象(天空盒立方体),然后这个着色器在其生命周期中只需要一个纹理绑定。
当我绘制具有相同纹理的多个对象时,也可以获得收益。
我的症状是,如果我在绘制之前没有重新绑定天空盒纹理,则在天空盒上使用不同的(先前绘制的)对象的纹理。此先前绘制的对象使用不同的着色器。
假设我再次使用着色器程序恢复纹理绑定是错误的吗?
ps。我对天空盒使用单个2d纹理,而不是立方体贴图。
答案 0 :(得分:7)
是的,假设在使用给定着色器程序时恢复纹理绑定将是错误的。
纹理绑定状态完全独立于当前着色器程序。每个纹理单元和目标都有一个当前绑定的纹理。假如您执行以下代码序列:
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D, myTexId);
您将纹理与id myTexId
绑定到纹理单元7的2D目标。此状态将保持不变,直到您将不同的纹理绑定到单元7的2D目标(或删除纹理) ,完全独立于着色器程序的活动。
纹理和着色器程序之间的关系是通过将程序的采样器统一变量的值设置为纹理单元来建立的。继续上面的示例,您指定给定程序应使用此纹理:
// needed only once after linking the program
GLint texLoc = glGetUniformLocation(myProgId, "MySampler");
// use the texture bound to unit 7
glUseProgram(myProgId);
glUniform1i(texLoc, 7);
基于此,如果要避免对纹理绑定进行冗余调用,则必须跟踪哪个纹理绑定到哪个单元。这些信息需要全局维护,即独立于特定的着色器程序。
请注意,每个程序的统一值 ,因此您不必在绑定特定程序后再次设置该值,除非您希望从绑定到不同单位的纹理中对其进行采样。
您可以做的一件事是减少绑定不同纹理所需的次数,即为每个着色器使用不同的纹理单元。作为简化示例,描绘了每个程序使用单个纹理并且每次使用程序时使用相同纹理的情况。然后,您可以为每个程序的采样器均匀设置不同的值,并将每个程序使用的纹理绑定到相应的纹理单元。每当您拨打glUseProgram()
时,每个程序都会使用相应的纹理,而不会调用glBindTexture()
。
当程序使用多个纹理时,这会变得更加复杂,并且您确实想要为特定程序切换纹理。但是如果你以一种聪明的方式管理纹理单元,你仍然可以大大减少纹理绑定操作的数量。在这样做的同时,请记住纹理单元的数量是有限的。可以通过以下方式查询限制:
glGetIntegerv(MAX_TEXTURE_IMAGE_UNITS, ...);
ES 2.0的限制可以低至8。
或者,从不同的角度看同样的事情:如果使用的纹理少于可用纹理单元的数量,则可以将每个纹理绑定到不同的纹理单元一次,并使用纹理绑定完成。然后,您可以通过将采样器制服设置为具有所需纹理边界的纹理单元来控制每个着色器在任何给定时间使用的纹理。
实际上,如果您的应用程序有些复杂,您很可能会使用这些方法的混合。例如,您可以将一些经常使用的纹理保持不断绑定到固定纹理单元,同时在必要时重新绑定其他纹理。
由于这可能听起来比实际上更复杂,让我总结一下这些关系:
glUniform1i()
在特定程序处于活动状态时调用,在着色器程序和纹理单元之间建立关系。glBindTexture()
在纹理单元和纹理对象之间建立关系。因此纹理单元提供了程序和纹理对象之间的间接级别,这解释了为什么纹理绑定状态不是特定程序的直接部分。