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

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

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

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

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

【问题讨论】:

    标签: android opengl-es glsurfaceview fbo


    【解决方案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

        1. 原则上,此类配置与 OpenGL (ES) 无关。这是 EGL 的。
        2. 有一个名为EGL_BUFFER_PRESERVED 的配置可以启用附加绘图。
        3. 要使用此配置,您可以选择直接使用SurfaceView 而不是GLSurfaceView 来完全控制EGL,如@fadden 建议的above。或者,
        4. 您仍然可以使用 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...
        }
        

        【讨论】:

          猜你喜欢
          • 2020-07-04
          • 1970-01-01
          • 1970-01-01
          • 2012-12-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-25
          • 1970-01-01
          相关资源
          最近更新 更多