glClearColor工作不正确(机器人的OpenGL)不正确、机器人、工作、glClearColor

2023-09-06 22:38:36 作者:烟酒成瘾

我想改变我的应用程序运行时的背景颜色。 因此,在按钮点击我第一次调用:

I want to change the background color of my app on runtime. So on button click I first call:

GLES20.glClearColor(color[0], color[1], color[2], color[3]);

然后我打电话:

Then I call:

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

和它什么也不做!它使当前的背景色 - 不改变它。但是,当我再暂停我的应用程序,并恢复它再次背景色变。

And it does nothing! It keeps the current background color - doesnt change it. But when I then pause my app and resume it again the background color is changed.

编辑: 我发现了一个办法做到这一点。每一帧我第一个电话 glClear 但我dident叫 glClearColor 。所以,如果我先打电话 glClearColor 每次我才打电话帧 glClear 它的工作原理。但是,这仍然没有道理给我,我想避免调用 glClearColor 在每一帧,认为​​这将是,如果我一旦把它足够的,当我想改变颜色。

I found out a way to do it. Each frame i first call glClear but I dident call glClearColor. So if I first call glClearColor each frame before I call glClear it works. But this still doesnt make sense to me, I wanted to avoid calling glClearColor at each frame, thought it would be enough if I call it once when I want to change the color.

推荐答案

您只能做,而你有一个当前OpenGL上下文OpenGL调用。当您使用 GLSurfaceView ,背景处理是照顾你,所以这一切只是神奇似乎工作。直到不顺心的事,像你的情况。而不是让你只解决方案,让我还是解释一下引擎盖下会发生什么,以避免将来出现意外。

You can only make OpenGL calls while you have a current OpenGL context. When you use GLSurfaceView, context handling is taken care for you, so it all just magically seems to work. Until something goes wrong, like in your case. Instead of giving you only the solution, let me still explain what happens under the hood, to avoid future surprises.

在你可以做任何OpenGL调用,OpenGL上下文需要创建,并设置为当前上下文。在Android上,这将使用EGL API。 GLSurfaceView 处理,对于你,而这一切发生之前, onSurfaceCreated()叫上你的渲染器。所以,当你的渲染实施方法被调用,你总是可以指望有一个当前上下文,而不必担心。

Before you can make any OpenGL calls, an OpenGL context needs to be created, and be set as the current context. On Android, this uses the EGL API. GLSurfaceView handles that for you, and this all happens before onSurfaceCreated() is called on your renderer. So when the methods on your Renderer implementation are called, you can always count on having a current context, without ever having to worry about it.

关键的方面是不过是在当前环境是每个线程。 GLSurfaceView 创建渲染线程,所有的渲染方法在该线程调用。

The crucial aspect is however that the current context is per thread. GLSurfaceView creates a rendering thread, and all the Renderer methods are invoked in this thread.

这样做的后果是,您不能让其他线程OpenGL调用,因为他们没有一个当前的OpenGL上下文。其中包括UI线程。这正是你所试图做的。如果您在 glClearColor()呼叫响应按钮点击,你是在UI线程,你没有一个当前的OpenGL上下文。

The consequence of this is that you cannot make OpenGL calls from other threads, because they do not have a current OpenGL context. Which includes the UI thread. This is exactly what you were attempting to do. If you make the glClearColor() call in response to a button click, you are in the UI thread, and you do not have a current OpenGL context.

您已经找到了解决方法实际上可能在这种情况下,最现实的解决方案。 glClearColor()应该是一个便宜的电话,所以使得它的每一个前 glClear()将不显著。如果这个动作你需要采取更贵,你也可以设置一个布尔标志时的值不同,而且只有在标志做 onDrawFrame()相应的工作置

The workaround you already found might in fact be the most realistic solution in this case. glClearColor() should be a cheap call, so making it before every glClear() will not be significant. If the action you needed to take were more expensive, you could also set a boolean flag when the value changed, and then only do the corresponding work in onDrawFrame() if the flag is set.

这里有另外​​一个微妙但很重要的方面:线程安全。当你在一个线程(UI线程)设置的值,并利用它们在另一个线程(线程渲染)很快,这一点你不用担心。说,如果你有3个值的背景色的RGB分量,而你在UI线程逐一设置。这有可能是渲染线程使用3个值,而UI线程设置它们,结束了新旧价值观的混合体。

There's another subtle but very important aspect here: thread safety. As soon as you set values in one thread (UI thread) and use them in another thread (rendering thread), this is something you have to worry about. Say if you have 3 values for the RGB components of the background color, and you set them in the UI thread one by one. It's possible that the rendering thread uses the 3 values while the UI thread is setting them, ending up with a mix of old and new values.

要说明这一切,我会用你的榜样,并勾勒出了一个工作和线程安全的解决方案。参与可能看起来像这样的类成员:

To illustrate all of this, I'll use your example, and sketch out a working and thread safe solution. The class members involved could look like this:

float mBackRed, mBackGreen, mBackBlue;
boolean mBackChanged;
Object mBackLock = new Object();

那么,你在UI线程设置的值:

Then where you set the value in the UI thread:

synchronized(mBackLock) {
    mBackRed = ...;
    mBackGreen = ...;
    mBackBlue = ...;
    mBackChanged = true;
}

而在 onDrawFrame()之前调用方法 glClear()

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);
}

请注意如何将所有访问由两个线程共享类的成员是锁里面。在最后code片段,还要注意的颜色值是如何被使用之前复制到局部变量。这可能是走得太远了这个简单的例子,但我想说明,该锁应保持尽可能简短的总体目标。如果你直接使用成员变量,你必须让 glClearColor()锁内调用。如果这是可能需要很长的时间的操作时,UI线程无法更新的值,并且可能被卡住了一段等待锁

Note how all access to the class members shared by the two threads is inside a lock. In the last code fragment, also note how the color values are copied to local variables before being used. This may be going too far for this simple example, but I wanted to illustrate the general goal that the lock should be held as briefly as possible. If you use the member variables directly, you would have to make the glClearColor() call inside the lock. If this is an operation that could take a long time, the UI thread could not update the values, and might be stuck for a while waiting for the lock.

有其它方式使用锁。 GLSurfaceView 有一个 queueEvent()方法,使您可以通过在的Runnable 将随后在渲染线程中执行。有这种情况的 GLSurfaceView 文档中的一个例子,所以我不会拼写出code代表在这里。

There is an alternate approach to using a lock. GLSurfaceView has a queueEvent() method that allows you to pass in a Runnable that will then be executed in the rendering thread. There is an example for this in the GLSurfaceView documentation, so I won't spell out code for it here.