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 模拟器

如何在Android上使用OpenGL ES 2.0绘制点

Android openGL ES 2.0里Surfaceview背景透明

Android:了解 OnDrawFrame、FPS 和 VSync (OpenGL ES 2.0)

Android设备上的OpenGL ES 2.0扩展[关闭]