渲染到iOS上的纹理OpenGL ES可以在模拟器上运行,但不能在设备上运行

时间:2011-09-06 00:00:48

标签: ios opengl-es render-to-texture

为了提高我的iPad OpenGL ES应用程序的性能,我计划在纹理上绘制一个很少更新但是渲染时重的元素,所以除非必须重新绘制元素,否则我可以使用纹理。但是,虽然纹理在模拟器和设备上都能正确映射,但只有在模拟器上才会实际渲染到纹理中。

以下是我添加到项目中的代码。在设置场景时,我创建了缓冲区和所需的纹理:

int width = 768;
int height = 270;

// Prepare texture for off-screen rendering.
glGenTextures(1, &wTexture);
glBindTexture(GL_TEXTURE_2D, wTexture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
glClearColor(.9f, .3f, .6f, 1.0f); // DEBUG
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
  GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);

// Depth attachment buffer, always needed.
glGenRenderbuffersOES(1, &wDepth);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, wDepth);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
  width, height);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, 0);

// Create FBO for render-to-texture.
glGenFramebuffersOES(1, &wBuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, wBuffer);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
  GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, wTexture, 0);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
  GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, wDepth);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

新FBO上的glFramebufferStatusOES(当然未绑定之前)会在模拟器和设备上产生'framebuffer complete'返回值。请注意,我为纹理设置了粉红色的清晰颜色,以确认纹理实际呈现,问题实际上只是纹理从未被绘制。

每当需要重新绘制纹理时,我会在渲染元素之前执行此操作:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, wBuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, width, height);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// ...

以及实际渲染后的以下内容:

// ...
glPopMatrix();
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

最后,每次重绘屏幕时,我都会将纹理映射到屏幕上适当位置的四边形,如下所示:

float Vertices[] = {
  -65.0f, -100.0f, .0f,
  -65.0f, 100.0f, .0f,
  -10.0f, -100.0f, .0f,
  -10.0f, 100.0f, .0f};
float Texture[] = {.0f, .0f, 1.0f, .0f, .0f, 1.0f, 1.0f, 1.0f};

glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glBindTexture(GL_TEXTURE_2D, wTexture);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glVertexPointer(3, GL_FLOAT, 0, Vertices);
glTexCoordPointer(2, GL_FLOAT, 0, Texture);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, 0);

在iPhone和iPad模拟器(4.2,4.3)上,代码按预期工作。我看到动态渲染的纹理显示在相应的位置,当然由于我的调试声明,粉红色而不是透明背景。但是,在我的iPad 4.2设备上,只渲染粉红色矩形,而不是在渲染到纹理步骤中应该绘制的粉红色矩形。因此,纹理正确地呈现在屏幕上,但由于某种原因,在设备上渲染到纹理代码无法实际渲染任何纹理。

我想我正在使用设备上没有的某些功能,或者在某处做出错误的假设,但我无法弄清楚它是什么。我也试过通过OpenGL ES Analyzer运行它,但它只给我一些基本的性能优化提示。我在哪里需要查找问题?

1 个答案:

答案 0 :(得分:9)

我在我的项目中使用了MSAA,并且发现当我禁用它时问题就消失了。这让我发现了this other question,其中讨论了相同的问题(但没有解决)。

问题似乎是如果为主帧缓冲区启用了多重采样,则所有自定义FBO也必须使用多重采样。您无法呈现为正常的非多重采样GL_TEXTURE_2D,并且OpenGL ES 2上无法使用多采样GL_TEXTURE_2D_MULTISAMPLE

为了解决这个问题,我修改了渲染到纹理代码,就像我修改主渲染代码以启用多重采样一样。除了在问题代码中创建的三个缓冲区对象之外,我还为多次采样渲染创建了三个缓冲区对象:

glGenFramebuffersOES(1, &wmBuffer);
glGenRenderbuffersOES(1, &wmColor);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, wmBuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, wmColor);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, 4, GL_RGBA8_OES, width, height);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, wmColor);
glGenRenderbuffersOES(1, &wmDepth);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, wmDepth);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, 4, GL_DEPTH_COMPONENT16_OES, width, height);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, wmDepth);

在渲染到纹理之前,我绑定了新的MSAA缓冲区:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, wmBuffer);

最后,在渲染之后,我将MSAA FBO解析为纹理FBO,就像我对主渲染帧缓冲区一样:

glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, wmBuffer);
glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, wBuffer);
glResolveMultisampleFramebufferAPPLE();
GLenum attachments[] = {GL_DEPTH_ATTACHMENT_OES, GL_COLOR_ATTACHMENT0_OES, GL_STENCIL_ATTACHMENT_OES};
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 3, attachments);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

现在可以正确渲染纹理(性能非常好!)