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

И он ничего не делает! Он сохраняет текущий цвет фона – не меняет его. Но когда я приостанавливаю приложение и возобновляю его, цвет фона меняется.

EDIT: Я узнал, как это сделать. Каждый кадр я сначала вызываю glClear но я вызываю вызов glClearColor . Поэтому, если я сначала вызову glClearColor каждого кадра, прежде чем я вызову glClear он будет работать. Но это все еще не имеет смысла для меня, я хотел избежать вызова glClearColor в каждом фрейме, подумал, что этого будет достаточно, если я назову его один раз, когда хочу изменить цвет.

Вы можете делать вызовы OpenGL только при наличии текущего контекста OpenGL. Когда вы используете GLSurfaceView , обработка контекста позаботится о вас, так что все это просто волшебным образом работает. Пока что-то пойдет не так, как в вашем случае. Вместо того, чтобы дать вам только решение, позвольте мне объяснить, что происходит под капотом, чтобы избежать будущих сюрпризов.

Прежде чем вы сможете совершать любые вызовы OpenGL, необходимо создать контекст OpenGL и установить его в качестве текущего контекста. На Android это использует API EGL. GLSurfaceView обрабатывает это для вас, и все это происходит до того, как onSurfaceCreated() вызывается на вашем рендерере. Поэтому, когда вы Renderer методы реализации Renderer , вы всегда можете рассчитывать на наличие текущего контекста, не беспокоясь об этом.

Однако критический аспект заключается в том, что текущий контекст относится к потоку . GLSurfaceView создает поток рендеринга, и все методы Renderer вызывается в этом потоке.

Следствием этого является то, что вы не можете делать вызовы OpenGL из других потоков , потому что у них нет текущего контекста OpenGL. Что включает в себя поток пользовательского интерфейса. Это именно то, что вы пытались сделать. Если вы glClearColor() вызов glClearColor() в ответ на нажатие кнопки, вы находитесь в потоке пользовательского интерфейса, и у вас нет текущего контекста OpenGL.

Обходной путь, который вы уже нашли, на самом деле может быть самым реалистичным решением в этом случае. glClearColor() должен быть дешевым вызовом, поэтому сделать его до каждого glClear() не будет значительным. Если действие, которое вам нужно было сделать, было более дорогостоящим, вы могли бы также установить булевский флаг, когда значение изменилось, а затем выполнить соответствующую работу в onDrawFrame() если флаг установлен.

Здесь есть еще один тонкий, но очень важный аспект: безопасность потоков. Как только вы задаете значения в одном потоке (поток пользовательского интерфейса) и используете их в другом потоке (поток рендеринга), это то, о чем вам нужно беспокоиться. Скажем, если у вас есть 3 значения для компонентов RGB цвета фона, и вы устанавливаете их в потоке пользовательского интерфейса один за другим. Вполне возможно, что поток рендеринга использует 3 значения, в то время как поток пользовательского интерфейса устанавливает их, заканчивая сочетанием старых и новых значений.

Чтобы проиллюстрировать все это, я буду использовать ваш пример и набросать рабочее и потокобезопасное решение. Участвующие участники могли бы выглядеть так:

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

Затем, где вы устанавливаете значение в потоке пользовательского интерфейса:

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

Обратите внимание, что весь доступ к членам класса, разделяемым двумя потоками, находится внутри блокировки. В последнем фрагменте кода также обратите внимание, как значения цвета копируются в локальные переменные перед использованием. Это может зайти слишком далеко для этого простого примера, но я хотел бы проиллюстрировать общую цель, заключающуюся в том, что замок следует держать как можно короче. Если вы используете переменные-члены напрямую, вам нужно будет вызвать glClearColor() внутри блокировки. Если это операция, которая может занять много времени, поток пользовательского интерфейса не может обновлять значения и может застрять в ожидании блокировки.

Существует альтернативный подход к использованию блокировки. GLSurfaceView имеет метод queueEvent() который позволяет вам передать Runnable , который затем будет выполнен в потоке рендеринга. В документации GLSurfaceView , поэтому здесь я не буду писать код.

Intereting Posts
Анализ XML-Android – разбор тегов <description> Какую версию Android sdk выпустить на рынок? Использует AsyncTask по-прежнему рекомендуется для загрузки элементов спискаView в фоновом режиме? Android управляет несколькими экранами … (Относительная компоновка или линейная компоновка)? Вычислить меру измерения перед отображением Как установить приложение для Android снова и снова, не удаляя предыдущую версию? Проблемы с быстрой прокруткой в ​​Android 4.4 Как сделать System.out.print в проекте Android? Как изменить размер веб-представления android после добавления в него данных Pubnub Push Notification не работает для кросс-платформенного мобильного приложения Почему я получаю Некоторое хрустение файла не удалось после того, как я переименовал файл с возможностью рисования в .9.png? Ошибка при попытке использовать привязку данных к переменной контекста с помощью BaseObservable Android – как установить изображение обоев Как вызвать метод каждый раз при просмотре активности? Преобразование 2-строкового массива в ArrayList в android