Renderización a la textura en iOS OpenGL ES: funciona en el simulador, pero no en el dispositivo
opengl-es render-to-texture (1)
Para mejorar el rendimiento de mi aplicación OpenGL ES para el iPad, estaba planeando dibujar un elemento que rara vez se actualize pero que sea muy pesado en el tiempo para una textura, así que solo puedo usar la textura a menos que el elemento tenga que volver a dibujarse. Sin embargo, aunque la textura está asignada correctamente tanto en el simulador como en el dispositivo, solo en el simulador se representa algo realmente en la textura.
El siguiente es el código que agregué al proyecto. Al configurar la escena, creo los búferes y la textura necesaria:
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);
Un glFramebufferStatusOES
en el nuevo FBO (antes de ser desatado, por supuesto) produce un valor de retorno ''framebuffer complete'' tanto en el simulador como en el dispositivo. Tenga en cuenta que establezco el color rosa claro de la textura para confirmar que la textura se procesa realmente, y el problema es simplemente que la textura nunca se dibuja.
Cuando sea necesario volver a dibujar la textura, hago esto antes de representar el elemento:
glBindFramebufferOES(GL_FRAMEBUFFER_OES, wBuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, width, height);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// ...
y el siguiente después de la representación real:
// ...
glPopMatrix();
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
Finalmente, cada vez que se vuelve a dibujar la pantalla, asigno la textura a un cuadrángulo en la posición adecuada en la pantalla, así:
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);
En los simuladores de iPhone e iPad (4.2, 4.3), el código funciona como se esperaba. Veo la textura presentada de forma dinámica en la posición correspondiente, por supuesto con un fondo de color rosa en lugar de transparente debido a mi declaración de depuración. En mi dispositivo iPad 4.2, sin embargo, solo se representa el rectángulo rosa, no lo que debería haberse dibujado en él durante el paso de renderizar a textura. Por lo tanto, la textura se representa en la pantalla correctamente, pero por alguna razón, en el dispositivo, el código de renderizar a textura no puede representar nada en la textura.
Supongo que estoy usando alguna funcionalidad que no está disponible en el dispositivo, o hago una suposición errónea en alguna parte, pero no puedo entender cuál es. También intenté ejecutarlo a través del OpenGL ES Analyzer, pero no me brinda más que algunos consejos básicos de optimización del rendimiento. ¿Dónde debo buscar el problema?
Estaba usando MSAA en mi proyecto, y descubrí que el problema desapareció cuando lo deshabilité. Esto me ha llevado a descubrir esta otra pregunta donde se discute el mismo problema (pero no se resuelve).
El problema parece ser que si la multimuestreo está habilitada para su framebuffer principal, todas sus FBO personalizadas también tienen que usar multisampling. No se puede procesar en un GL_TEXTURE_2D_MULTISAMPLE
no multi-muestreo normal, y un GL_TEXTURE_2D_MULTISAMPLE
multi-muestreado no está disponible en OpenGL ES 2.
Para solucionar el problema, modifiqué mi código de renderizado a textura de la misma manera que modifiqué mi código de renderizado principal para habilitar la multimuestreo. Además de los tres objetos de búfer creados en el código de la pregunta, creo tres más para la representación de múltiples muestras:
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);
Antes de renderizar la textura, vinculo el nuevo buffer MSAA:
glBindFramebufferOES(GL_FRAMEBUFFER_OES, wmBuffer);
Finalmente, después del renderizado, resuelvo el MSAA FBO en la textura FBO de la misma manera que lo hago para mi principal framebuffer de renderizado:
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);
Las texturas ahora se representan correctamente (¡y el rendimiento es excelente!)