我正在 Android 上的 OpenGL ES 2.0 中实现一个引擎。我有多个着色器,对象可以采用多个纹理。
我的目标是尽量减少对 OpenGL 的纹理绑定(bind)调用,因此我保留了每个着色器当前绑定(bind)纹理的列表,并且仅在纹理发生变化时调用 glBindTexture()
。
例如我有一个天空盒着色器,我只用它绘制一个对象(天空盒立方体),那么这个着色器在其生命周期内只需要绑定(bind)一个纹理。
当我用相同的纹理绘制多个对象时,也会有收获。
我的症状是,如果我在绘制之前不重新绑定(bind)天空盒纹理,则会在天空盒上使用不同的(先前绘制的)对象的纹理。这个先前绘制的对象使用不同的着色器。
那么假设当我再次使用着色器程序时纹理绑定(bind)会恢复是错误的吗?
ps.:我为天空盒使用单个 2d 纹理,而不是立方体贴图。
最佳答案
是的,假设在使用给定着色器程序时恢复纹理绑定(bind)是错误的。
纹理绑定(bind)状态完全独立于当前着色器程序。每个纹理单元 和目标 当前绑定(bind)一个纹理。假设您执行以下代码序列:
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D, myTexId);
您正在将 ID 为 myTexId
的纹理绑定(bind)到纹理单元 7 的 2D 目标。此状态将一直保持,直到您将不同的纹理绑定(bind)到单元 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);
基于此,如果您想避免对纹理绑定(bind)进行冗余调用,则必须跟踪哪个纹理绑定(bind)到哪个单元。此信息需要“全局”维护,即独立于特定着色器程序。
请注意,统一值是每个程序,因此您不必在绑定(bind)特定程序后再次设置该值,除非您希望它从绑定(bind)到不同单元的纹理中采样。
为了减少需要绑定(bind)不同纹理的次数,您可以做的一件事是为每个着色器使用不同的纹理单元。作为一个简化的示例,想象这样一种情况,其中每个程序都使用一个纹理,并且每次使用该程序时都使用相同的纹理。然后,您可以为每个程序的采样器统一设置不同的值,并将每个程序使用的纹理绑定(bind)到相应的纹理单元。每当您调用 glUseProgram()
时,每个程序都会使用相应的纹理,而无需调用 glBindTexture()
。
当然,当程序使用多个纹理时,这会变得有点复杂,并且您确实希望为特定程序切换纹理。但是如果您以巧妙的方式管理纹理单元,您仍然可以大大减少纹理绑定(bind)操作的数量。这样做时,请记住纹理单元的数量是有限的。可以通过以下方式查询限制:
glGetIntegerv(MAX_TEXTURE_IMAGE_UNITS, ...);
ES 2.0 的限制可以低至 8。
或者,换个角度看同样的事情:如果你使用的纹理少于可用纹理单元的数量,你可以将每个纹理绑定(bind)到一个不同的纹理单元一次,然后通过纹理绑定(bind)完成。然后,您可以通过将每个着色器的采样器制服设置为具有所需纹理绑定(bind)的纹理单元来控制每个着色器在任何给定时间使用哪个纹理。
实际上,如果您的应用有些复杂,您很可能会混合使用这些方法。例如,您可以将一些经常使用的纹理始终绑定(bind)到一个固定的纹理单元,同时在必要时重新绑定(bind)其他纹理。
由于这听起来可能比实际情况更复杂,所以让我总结一下这些关系:
- 每个项目都有自己的一套统一值(value)观。
glUniform1i()
在特定程序处于 Activity 状态时调用,在着色器程序 和纹理单元 之间建立关系。。<glBindTexture()
在纹理单元 和纹理对象 之间建立关系。
因此纹理单元在程序和纹理对象之间提供了一种间接级别,这解释了为什么纹理绑定(bind)状态不是特定程序的直接部分。
关于android - glUseProgram 之间的 OpenGL 纹理绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26053851/