Android OpenGL ES 2.0 重用前一帧
Posted
技术标签:
【中文标题】Android OpenGL ES 2.0 重用前一帧【英文标题】:Android OpenGL ES 2.0 reuse previous frame 【发布时间】:2015-03-12 19:25:47 【问题描述】:我正在寻找一个最小的示例,其中每个帧都将重用前一帧中的三角形。我一直在尝试一遍又一遍都没有成功,所以我没有任何值得展示的代码,尽管我确实有一个通过绘制到默认帧缓冲区来工作的程序。
在绘制过程中,我会将新三角形添加到帧缓冲区对象,并且帧将被复制到默认帧缓冲区。由于我不会清除帧缓冲区对象,它会保留其 rgba 值和深度,因此当我在下一帧添加更多三角形时,之前的三角形仍然会保留。 (稍后我将减少前一帧中三角形的 alpha 值以产生淡入淡出效果,但为简单起见,完全按原样重复使用以前的三角形即可。)
我发现很难理解帧缓冲区对象的工作原理,以及是否需要创建渲染、深度和纹理缓冲区。我怀疑我需要渲染和深度缓冲区,因为我想在两次绘制之间保留该信息,但不需要纹理缓冲区。
我认为 onDrawFrame 方法看起来像这样:
-
新数据被添加到帧缓冲区对象中。
默认帧缓冲区被清除。
将帧缓冲区对象中的信息复制到默认帧缓冲区,然后渲染默认帧缓冲区。
我相信我通过绑定帧缓冲区对象、渲染缓冲区和深度缓冲区正确地执行了第 1 步和第 2 步,但我无法找出从一个帧缓冲区复制到另一个帧缓冲区的方法。
【问题讨论】:
【参考方案1】:对于第 3 步,您使用渲染到的纹理(用作 FBO 颜色附件的纹理),并在绘制屏幕尺寸四边形时从中采样。您可以为此使用非常简单的着色器。副本的顶点着色器如下所示:
#version 100
attribute vec2 Pos;
varying vec2 TexCoord;
void main()
TexCoord = 0.5 * Pos + 0.5;
gl_Position = vec4(Pos, 0.0, 1.0);
和片段着色器:
#version 100
uniform sampler2D Tex;
varying vec2 TexCoord;
void main()
gl_FragColor = texture2D(Tex, TexCoord);
然后绘制一个覆盖 x 和 y 范围 [-1.0, 1.0] 的四边形。
还有另一种选择。不幸的是,它不是便携式的,但可以在某些设备上运行。以下是从我自己最近的回答中复制的:Fast Screen Flicker while NOT drawing on android OpenGL。
对于这种方法,您调用:
eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
在设置上下文/表面时。这要求在eglSwapBuffers()
之后保留缓冲区内容。但是,它有一个很大的警告:并非所有设备都支持此功能。您可以测试它是否支持:
EGLint surfType = 0;
eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfType);
if (surfType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT)
// supported!
您也可以在选择配置时请求此功能。作为传递给eglChooseConfig()
的属性的一部分,添加:
...
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
...
但同样,所有设备都不支持。因此,仅当您针对特定设备时,它才是一个真正的选择,或者如果它不受支持,则具有功能回退。
【讨论】:
【参考方案2】:帧缓冲区对象实际上只是一个元对象,它是它所包含的表面(纹理或渲染缓冲区)的容器。您将需要创建所需的颜色/深度/模板并将它们“附加”到 帧缓冲区对象中的相关连接点。
就从一个表面复制到另一个表面而言,您可以使用 glBlitFramebuffer,或者只渲染一个加载屏幕外表面作为纹理的 2D 四边形并设置纹理坐标,使其成为 1:1 副本。
请注意,在大多数基于 tile-based 的移动 GPU 上,“在内存中的顶部进行渲染”相对昂贵(它们必须将旧状态读入 GPU 本地内存),尤其是如果您需要单独的副本将屏幕外缓冲区blit到屏幕缓冲区中。我建议进行分析,以确保该方案确实比重新渲染更快,因为听起来可能不是。
【讨论】:
在费用上:至少,Apple 甚至支持丢弃扩展,它明确允许渲染目标进入未定义状态,因为将计算的缓冲区流回存储以供重用的成本通常是'不可取。想要显示然后丢弃中间数据更为常见。 是的——它是 OpenGL ES 3.0 的标准部分,所以我想现在每个人都支持它——但 OP 明确表示他想保留旧内容。在写出之前使它们无效与“保留的愿望”要求相冲突。 感谢您的意见。不幸的是,android 中的 openGL ES 2.0 不支持 blitting。根据您的建议,我将尝试使用顶点缓冲区对象,因为它们可能比 blitting 更有效,而且我无法更早地绘制到纹理。 Blit = 渲染两个三角形,纹理大小 == 屏幕大小。如果所有的用户界面和 2D 游戏都不能在屏幕上写入纹理,你认为它们如何在 Android 中运行?顶点缓冲区对象是关于发送几何数据的,所以不确定这对这里有什么帮助...... @Isogen74:他的意思是不支持glBlitFramebuffer (...)
。没错,直接将像素从一个帧缓冲区复制到另一个帧缓冲区的机制从未在 OpenGL ES 2.0 中实现(这使得渲染缓冲区很多不太有用)。但是,全屏纹理四边形(三角形条)可以用来做大致相同的事情。如果没有glBlitFramebuffer (...)
,您唯一会错过的就是 MSAA 解析,但无论如何这对于 ES 2.0 级移动 GPU 来说可能并不是什么大问题。以上是关于Android OpenGL ES 2.0 重用前一帧的主要内容,如果未能解决你的问题,请参考以下文章
如何在Android上将OpenGL ES 1.0代码转换为OpenGL Es 2.0?
Android openGL ES 2.0里Surfaceview背景透明