问题描述
为了提高我在 iPad 上的 OpenGL ES 应用程序的性能,我计划在纹理上绘制一个很少更新但渲染时间很长的元素,所以我可以只使用纹理,除非必须重新绘制元素.然而,虽然纹理在模拟器和设备上都正确映射,但只有在模拟器上才能真正渲染到纹理中.
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);
新 FBO 上的 glFramebufferStatusOES
(当然是在它未绑定之前)在模拟器和设备上产生帧缓冲区完成"返回值.请注意,我将纹理设置为粉红色透明颜色是为了确认纹理实际上已被渲染,而问题实际上只是纹理从未被绘制到.
A glFramebufferStatusOES
on the new FBO (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);
在 iPhone 和 iPad 模拟器(4.2、4.3)上,代码按预期工作.我看到动态渲染的纹理显示在相应的位置,由于我的调试语句,当然是粉红色而不是透明背景.然而,在我的 iPad 4.2 设备上,只渲染了粉红色的矩形,而不是在渲染到纹理步骤中应该绘制的内容.因此,纹理被正确地渲染到屏幕上,但由于某种原因,在设备上,渲染到纹理的代码实际上无法将任何东西渲染到纹理.
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.
我想我正在使用设备上不可用的某些功能,或者在某处做出错误假设,但我无法弄清楚它是什么.我还尝试通过 OpenGL ES Analyzer 运行它,但它只给了我一些基本的性能优化技巧.我需要从哪里寻找问题?
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?
推荐答案
我在我的项目中使用MSAA,发现禁用后问题消失了.这让我发现其他问题 讨论相同的问题(但未解决).
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).
问题似乎是,如果为您的主帧缓冲区启用了多重采样,那么您的所有自定义 FBO 也必须使用多重采样.您无法渲染到普通的非多重采样 GL_TEXTURE_2D
,并且多重采样 GL_TEXTURE_2D_MULTISAMPLE
在 OpenGL ES 2 上不可用.
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);
在渲染到纹理之前,我绑定了新的 MSAA 缓冲区:
Before rendering to the texture, I bind the new MSAA buffer:
glBindFramebufferOES(GL_FRAMEBUFFER_OES, wmBuffer);
最后,在渲染之后,我将 MSAA FBO 解析为纹理 FBO,就像我对主渲染帧缓冲区所做的那样:
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!)
这篇关于在 iOS OpenGL ES 上渲染到纹理——适用于模拟器,但不适用于设备的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!