Android OpenGL ES 2.0 重用前一帧

Android OpenGL ES 2.0 reuse previous frame

我正在寻找一个最小的例子,其中每一帧都会重复使用前一帧的三角形。我试了一遍又一遍都没有成功,所以我没有任何值得展示的代码,尽管我有一个程序可以通过绘制到默认帧缓冲区来工作。

在绘制期间,我会向帧缓冲区对象添加新的三角形,并且帧会被复制到默认的帧缓冲区。因为我不会清除帧缓冲区对象,它会保留它的 rgba 值和深度,这样当我在下一帧添加更多三角形时,之前的那些仍然会保留。 (稍后我会减少之前帧中三角形的 alpha 值以产生淡入淡出效果,但为了简单起见,完全按照它们的原样重用之前的三角形就可以了。)

我发现很难理解帧缓冲区对象的工作原理,以及我是否需要创建渲染、深度和纹理缓冲区。我怀疑我需要渲染和深度缓冲区,因为我想在绘制之间保留该信息但不需要纹理缓冲区。

我认为 onDrawFrame 方法看起来像这样:

  1. 新数据已添加到帧缓冲区对象。
  2. 清除默认帧缓冲区。
  3. 将帧缓冲区对象中的信息复制到默认帧缓冲区,然后渲染默认帧缓冲区。

我相信我通过绑定帧缓冲区对象、渲染缓冲区和深度缓冲区正确地执行了步骤 1 和 2,但是我无法找到从一个帧缓冲区复制到另一个帧缓冲区的方法。

帧缓冲区对象实际上只是一个元对象,它是它包含的表面(纹理或渲染缓冲区)的容器。您将需要创建您需要的 color/depth/stencil 并将它们 "attach" 帧缓冲区对象中的相关附着点。

就从一个表面复制到另一个表面而言,您可以使用 glBlitFramebuffer,或者只渲染一个 2D 四边形,将屏幕外表面加载为纹理并设置纹理坐标,使其成为 1:1 副本.

请注意,"rendering over the top of what is already in memory" 在大多数基于图块的移动 GPU 上相对昂贵(它们必须将旧状态读入 GPU 本地内存),尤其是当您随后需要单独的副本来 blit 屏幕外缓冲区时进入屏幕上的那个。我建议进行分析以确保此方案确实比重新渲染更快,因为听起来可能并非如此。

对于第 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]。

还有一个选择。不幸的是,它不是便携式的,但可以在某些设备上使用。以下是从我自己最近的回答中复制过来的:.

对于这种方法,您调用:

eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);

同时设置 context/surface。这要求在 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,
...

但同样,这在所有设备上都不支持。因此,只有当您针对特定设备时,它才是真正的选择,或者如果它不受支持,则有功能回退。