开发者

Rendering to texture on iOS OpenGL ES—works on simulator, but not on device

In order to improve the performance of my OpenGL ES application for the iPad, I was planning to draw a rarely updated but rendertime-heavy element to a texture, so I can just use the texture unless the element has to be redrawn. However, while the texture is mapped correctly on both the simulator and the device, only on the simulator is something actually rendered into the texture.

The following is the code that I added to the project. While setting up the scene, I create the buffers and the texture needed:

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);

A glFramebufferStatusOES on the new FB开发者_如何学运维O (before it is unbound of course) yields a 'framebuffer complete' return value on both the simulator and the device. Note that I set the pink clear colour for the texture in order to confirm that the texture is actually rendered, and the problem is in fact simply that the texture is never drawn into.

Whenever the texture needs to be redrawn, I do this before rendering the element:

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

and the following after the actual rendering:

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

Finally, every time the screen is redrawn I map the texture to a quad at the appropriate position on the screen, like so:

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);

On the iPhone and iPad simulators (4.2, 4.3), the code works as expected. I see the dynamically rendered texture displayed at the respective position, of course with a pink instead of a transparent background due to my debugging statement. On my iPad 4.2 device, however, only the pink rectangle is rendered, not what should have been drawn into it during the render-to-texture step. Thus, the texture is rendered to the screen correctly, but for some reason, on the device the render-to-texture code fails to actually render anything to the texture.

I suppose I am using some functionality that is not available on the device, or make an erroneus assumption somewhere, but I can't figure out what it is. I also tried running it through the OpenGL ES Analyzer, but it gives me nothing but some basic performance optimisation tips. Where do I need to look for the problem?


I was using MSAA in my project, and have found out that the problem disappeared when I disabled it. This has lead me to discover this other question where the same problem is discussed (but not solved).

The problem seems to be that if multisampling is enabled for your main framebuffer, all of your custom FBOs have to use multisampling as well. You cannot render to a normal non-multisampled GL_TEXTURE_2D, and a multi-sampled GL_TEXTURE_2D_MULTISAMPLE is not available on OpenGL ES 2.

In order to fix the problem, I modified my render-to-texture code the same way I modified my main rendering code to enable multisampling. In addition to the three buffer objects created in the code from the question, I create three more for the multi-sampled rendering:

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);

Before rendering to the texture, I bind the new MSAA buffer:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, wmBuffer);

Finally, after rendering, I resolve the MSAA FBO into the texture FBO the same way I do for my main rendering framebuffer:

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);

The textures are now rendered correctly (and the performance is great!)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜