如何使用单个着色器程序正确混合绘图调用和采样器值的更改?

时间:2012-12-03 01:47:02

标签: android opengl-es opengl-es-2.0

我正在尝试使用两个不同的纹理在OpenGL ES 2.0 for Android中使用一个着色器程序绘制两个对象。第一个对象应该有texture0,第二个对象应该有texture1

在片段着色器中,我有:

uniform sampler2D tex;

并在java代码中:

int tiu0 = 0;
int tiu1 = 1;
int texLoc = glGetUniformLocation(program, "tex");

glUseProgram(program);

// bind texture0 to texture image unit 0
glActiveTexture(GL_TEXTURE0 + tiu0);
glBindTexture(GL_TEXTURE_2D, texture0);

// bind texture1 to texture image unit 1
glActiveTexture(GL_TEXTURE0 + tiu1);
glBindTexture(GL_TEXTURE_2D, texture1);

glUniform1i(texLoc, tiu0);
// success: glGetError returns GL_NO_ERROR, glGetUniformiv returns 0 for texLoc

drawFirstObject(); // should have texture0

glUniform1i(texLoc, tiu1);
// success: glGetError returns GL_NO_ERROR, glGetUniformiv returns 1 for texLoc

drawSecondObject(); // should have texture1

使用Android 2.3.3在Samsung Galaxy Ace上运行两个对象具有相同的texture0。类似的代码在我的桌面计算机上的OpenGL 2.0中正确运行。

如果我删除drawFirstObject,则第二个对象将有texture1

如果我删除drawSecondObject,则第一个对象将有texture0

如果介于drawFirstObjectdrawSecondObject之间,我会改变程序一段时间:

glUseProgram(0); // can be also any valid program other than the program from the next call
glUseProgram(program);

然后两个对象都有texture1

始终正确设置与sampler2D不同的制服值。

我知道我可以使用一个纹理图像单元绘制具有不同纹理的两个对象,并在绘制对象之前将适当的纹理绑定到该纹理图像单元,但我也想知道这里发生了什么。

我的代码有问题吗?是否可以在OpenGL ES 2.0中通过仅在代码中显示的纹理图像单元之间切换来绘制具有不同纹理的对象?如果不可能,那么OpenGL 2.0(可能的地方)和OpenGL ES 2.0之间的区别是什么?我找不到了。

1 个答案:

答案 0 :(得分:3)

经过数小时的深入研究后,我发现这个问题特定于我的三星Galaxy Ace(GT-S5830)中使用的Adreno 200 GPU。似乎Adreno 200驱动程序在第一次调用绘图函数时将纹理分配给采样器,然后忽略对采样器值(glUniform1i(samplerLocation, textureImageUnit))的任何更改,直到出现其中一个:

    使用不同的着色器程序调用
  • glUseProgram
  • 将不同的纹理绑定到着色器程序使用的任何纹理图像单元。

在Adreno 200 GPU制造商的论坛中有thread描述了同样的问题。

因此,如果您使用相同的着色器程序多次调用绘图函数并且之前绑定了不同的纹理,则所述问题有两种解决方法:

  1. 在每个绘图功能之前调用glUseProgram(0); glUseProgram(yourDrawingProgram);

  2. 在每次绘图调用之前,将不同的纹理绑定到着色器程序使用的至少一个纹理图像单元。此解决方案可能难以维护,因为如果绑定已绑定到纹理图像单元的相同纹理,则问题仍将存在。因此,在这种情况下,最简单的解决方案是在每次绘图调用之前,不要更改采样器值并绑定着色器程序使用的所有纹理图像单元的纹理。