如何设置 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。或者,
您仍然可以使用 GLSurfaceView
和 setEGLConfigChooser
自定义 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 一起使用