glClearColor无法正常工作(android opengl)
我想在运行时更改我的应用程序的背景颜色。 所以点击按钮我先打电话:
GLES20.glClearColor(color[0], color[1], color[2], color[3]);
然后我打电话给:
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
它什么都不做! 它保持当前的背景颜色 – 不会改变它。 但是当我暂停我的应用并再次恢复时,背景颜色会发生变化。
编辑:我发现了一种方法。 每个帧我首先调用glClear
但我dident调用glClearColor
。 因此,如果我在调用glClear
之前每次调用glClear
它都可以工作。 但是这对我来说仍然没有意义,我想避免在每一帧调用glClearColor
,认为如果我想改变颜色就调用它就足够了。
您只能在拥有当前OpenGL上下文时进行OpenGL调用。 当您使用GLSurfaceView
,上下文处理会得到照顾,所以这一切似乎都神奇地起作用。 直到出现问题,就像你的情况一样。 而不是只给你解决方案,让我仍然解释在幕后发生的事情,以避免未来的惊喜。
在进行任何OpenGL调用之前,需要创建一个OpenGL上下文,并将其设置为当前上下文。 在Android上,它使用EGL API。 GLSurfaceView
为您处理,这一切都发生在渲染器上调用onSurfaceCreated()
之前。 因此,当调用Renderer
实现上的方法时,您始终可以依赖于当前上下文,而无需担心它。
然而,关键的方面是当前上下文是每个线程 。 GLSurfaceView
创建一个渲染线程,并在此线程中调用所有Renderer
方法。
这样做的结果是你无法从其他线程进行OpenGL调用 ,因为它们没有当前的OpenGL上下文。 其中包括UI线程。 这正是你试图做的。 如果您在响应按钮单击时调用glClearColor()
,则表示您处于UI线程中,并且您没有当前的OpenGL上下文。
在这种情况下,您已经找到的解决方法实际上可能是最现实的解决方案。 glClearColor()
应该是一个廉价的调用,所以在每个glClear()
之前创建它不会很重要。 如果您需要采取的操作更昂贵,您还可以在值更改时设置布尔标志,然后仅在设置标志时在onDrawFrame()
执行相应的工作。
这里有另一个微妙但非常重要的方面:线程安全。 只要在一个线程(UI线程)中设置值并在另一个线程(渲染线程)中使用它们,就必须担心这一点。 假设您有背景颜色的RGB组件的3个值,并逐个在UI线程中设置它们。 渲染线程可能在UI线程设置时使用3个值,最后混合使用旧值和新值。
为了说明所有这些,我将使用您的示例,并草拟一个工作和线程安全的解决方案。 涉及的class级成员可能如下所示:
float mBackRed, mBackGreen, mBackBlue; boolean mBackChanged; Object mBackLock = new Object();
然后在UI线程中设置值的位置:
synchronized(mBackLock) { mBackRed = ...; mBackGreen = ...; mBackBlue = ...; mBackChanged = true; }
在调用glClear()
之前的onDrawFrame()
方法中:
Boolean changed = false; float backR = 0.0f, backG = 0.0f, backB = 0.0f; synchronized(mBackLock) { if (mBackChanged) { changed = true; backR = mBackRed; backG = mBackGreen; backB = mBackBlue; mBackChanged = false; } } if (changed) { glClearColor(backR, backG, backB, 0.0f); }
请注意两个线程共享的类成员的所有访问权限是如何锁定的。 在最后一个代码片段中,还要注意在使用之前如何将颜色值复制到局部变量。 对于这个简单的例子,这可能太过分了,但我想说明锁定应该尽可能短暂地保持的总体目标。 如果直接使用成员变量,则必须在锁内部进行glClearColor()
调用。 如果这是一个可能需要很长时间的操作,则UI线程无法更新值,并且可能会等待一段时间等待锁定。
有一种使用锁的替代方法。 GLSurfaceView
有一个queueEvent()
方法,允许您传入Runnable
,然后在渲染线程中执行该Runnable
。 在GLSurfaceView
文档中有一个例子,所以我不会在这里拼出代码。
- 在Room中创建Generic DAO接口时,“参数的类型必须是使用@Entity注释的类”
- 从PauseHandler实现调用SupportFragmentManager
- android oreo上的永久服务
- 将数组从JNI传递到Java
- Android – Fragment内部的RecyclerView – NullPointerException
- 如何在React Native for Android Apps中设置canOverrideExistingModule = true?
- OnClickListener没有响应
- LibGDX 0.9.9 – 在环境中应用立方体贴图
- 如何查看Pocketsphinx字典中是否存在单词?