如何设置 GLSurfaceView 不清理我以前画过的东西?

Posted

技术标签:

【中文标题】如何设置 GLSurfaceView 不清理我以前画过的东西?【英文标题】:How to set GLSurfaceView not clean things I've draw before? 【发布时间】:2015-07-29 06:41:18 【问题描述】:

我试图在 android 中使用 opengl es 2.0。我使用 GLSurfaceView 作为我的主要视图。这是问题。我想连续画几个点。也就是当我点击视图时,我画了一个点并调用requestRender。但我不想存储我触发的所有点,因为它们会很多。所以我的onDrawFrame()只有一句话,大概是这样的:

GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);

问题是,这个逻辑在我的 android 虚拟机上运行良好,但是当我画一个新点时,我画的所有东西都消失了。

有没有办法保留我在 GLSurfaceView 上绘制的所有点,然后继续绘制而不保存所有点?

【问题讨论】:

【参考方案1】:

您需要每帧重绘整个屏幕。输出通常是双缓冲或三缓冲的,因此您不能依赖先前内容的可用性。始终以glClear() 呼叫开始。

执行您想要的操作的一种方法是渲染到 FBO,然后将 FBO blit 到屏幕上。您可以在 Grafika 的“record GL app”活动中找到一个示例,该活动实际上正在使用它,因此它可以将每个帧渲染两次(一次到屏幕,一次到视频编码器),并在网络上的各种示例中。基本思想是您渲染到 GLES 纹理上,而不是直接渲染到 Surface 中,因此无需应对双缓冲。

【讨论】:

【参考方案2】:

如果您在onFrameRedraw() 方法中使用了GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT),请将其删除,然后在渲染器的onSurfaceCreated() 方法中使用它。

【讨论】:

【参考方案3】:

我还在努力禁用双缓冲或三缓冲。最后,我找到了解决方案。 (参见Automatic buffer clear while using OpenGL on Android)

    原则上,此类配置与 OpenGL (ES) 无关。这是 EGL 的。 有一个名为EGL_BUFFER_PRESERVED 的配置可以启用附加绘图。 要使用此配置,您可以选择直接使用SurfaceView 而不是GLSurfaceView 来完全控制EGL,如@fadden 建议的above。或者, 您仍然可以使用 GLSurfaceViewsetEGLConfigChooser 自定义 EGLConfigChooser 对象,然后在适当的时间调用 EGL14.eglSurfaceAttrib

自定义 EGLConfigChooser 类:

启用EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT

/**
 * This class will choose a RGB_888 surface with or without a depth buffer.
 * (Choosing a RGB_888 with a depth buffer is GLSurfaceView's default behavior.)
 *
 * In addition to the default behavior, it will enable EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT
 * of EGL10.EGL_SURFACE_TYPE.
 *
 * cf. https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglChooseConfig.xhtml
 */
class SimpleConfigChooser(
    private val eglContextClientVersion: Int, withDepthBuffer: Boolean = true,
) : GLSurfaceView.EGLConfigChooser 

    private val value = IntArray(1)

    private val redSize = 8
    private val greenSize = 8
    private val blueSize = 8
    private val alphaSize = 0
    private val depthSize = if (withDepthBuffer) 16 else 0
    private val stencilSize = 0

    private val configSpec = filterConfigSpec(intArrayOf(
        EGL10.EGL_RED_SIZE, redSize,
        EGL10.EGL_GREEN_SIZE, greenSize,
        EGL10.EGL_BLUE_SIZE, blueSize,
        EGL10.EGL_ALPHA_SIZE, alphaSize,
        EGL10.EGL_DEPTH_SIZE, depthSize,
        EGL10.EGL_STENCIL_SIZE, stencilSize,
        EGL10.EGL_SURFACE_TYPE, (EGL10.EGL_WINDOW_BIT or EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT),
        EGL10.EGL_NONE
    ))

    private fun filterConfigSpec(configSpec: IntArray): IntArray 
        if (eglContextClientVersion != 2 && eglContextClientVersion != 3) 
            return configSpec
        
        /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
         * And we know the configSpec is well formed.
         */
        val len = configSpec.size
        val newConfigSpec = IntArray(len + 2)
        System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1)
        newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE
        if (eglContextClientVersion == 2) 
            newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT /* EGL_OPENGL_ES2_BIT */
         else 
            newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR /* EGL_OPENGL_ES3_BIT_KHR */
        
        newConfigSpec[len + 1] = EGL10.EGL_NONE
        return newConfigSpec
    

    override fun chooseConfig(egl: EGL10, display: EGLDisplay): EGLConfig 
        val numConfig = IntArray(1)
        require(egl.eglChooseConfig(
            display, configSpec, null, 0, numConfig
        ))  "eglChooseConfig#1/2 failed" 

        val numConfigs = numConfig[0]
        require(numConfigs > 0)  "No configs match configSpec" 

        val configs = arrayOfNulls<EGLConfig>(numConfigs)
        require(egl.eglChooseConfig(
            display, configSpec, configs, numConfigs, numConfig
        ))  "eglChooseConfig#2/2 failed" 

        return chooseConfig(egl, display, configs)
            ?: throw IllegalArgumentException("No config chosen")
    

    private fun chooseConfig(
        egl: EGL10, display: EGLDisplay, configs: Array<EGLConfig?>,
    ): EGLConfig? 
        for (config in configs) 
            if (config == null) 
                continue
            
            val d: Int = findConfigAttrib(egl, display, config,
                EGL10.EGL_DEPTH_SIZE, 0)
            val s: Int = findConfigAttrib(egl, display, config,
                EGL10.EGL_STENCIL_SIZE, 0)
            if (d >= depthSize && s >= stencilSize) 
                val r: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_RED_SIZE, 0)
                val g: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_GREEN_SIZE, 0)
                val b: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_BLUE_SIZE, 0)
                val a: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_ALPHA_SIZE, 0)
                if (r == redSize && g == greenSize
                    && b == blueSize && a == alphaSize
                ) 
                    return config
                
            
        
        return null
    

    private fun findConfigAttrib(
        egl: EGL10, display: EGLDisplay, config: EGLConfig, attribute: Int, defaultValue: Int,
    ): Int 
        return if (egl.eglGetConfigAttrib(display, config, attribute, value)) 
            value[0]
         else defaultValue
    

在您的 GLSurfaceView.Renderer 子类中:

调用EGL14.eglSurfaceAttrib

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) 
    EGL14.eglSurfaceAttrib(
        EGL14.eglGetCurrentDisplay(),
        EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW),
        EGL14.EGL_SWAP_BEHAVIOR, EGL14.EGL_BUFFER_PRESERVED
    )

    // some other work...

主活动:

将自定义EGLConfigChooser 设置为GLSurfaceView

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)

    val binding = MainActivityBinding.inflate(layoutInflater)

    val EGL_CONTEXT_CLIENT_VERSION = 2

    binding.glSurfaceView.setEGLContextClientVersion(
        EGL_CONTEXT_CLIENT_VERSION
    )
    binding.glSurfaceView.setEGLConfigChooser(
        SimpleConfigChooser(EGL_CONTEXT_CLIENT_VERSION)
    )
    binding.glSurfaceView.setRenderer(Renderer()) // this should be the last

    setContentView(binding.root)

    // some other work...

【讨论】:

以上是关于如何设置 GLSurfaceView 不清理我以前画过的东西?的主要内容,如果未能解决你的问题,请参考以下文章

TextureView 与 GLSurfaceView 或如何将 GLSurfaceView 与 EGL14 一起使用

GLSurfaceView 如何与surfaceflinger 连接?

如何将GLSurfaceView渲染保存到文件?

使用GLsurfaceview修改带效果的相机预览

使用 GLsurfaceview 修改带有效果的相机预览

Android:从 GLSurfaceView 返回主要活动