OpenGL ES 2.0: значение альфа-канала во фрагментном шейдере игнорируется (используется glReadPixels())

Я пытаюсь отобразить очень простую вещь (по крайней мере, я так думал) с помощью OpenGL ES 2.0 на Android: монохромный четырехугольник с прозрачностью. Мой фрагментный шейдер в основном просто делает это:

gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);

Таким образом, четырехугольник должен отображаться чисто красным, но с 50% прозрачностью, чтобы фон (черный) проступал, что должно сделать его немного темнее. Однако у четырехугольника будет точно такой же вид, независимо от того, какое значение я выберу для элемента альфа-канала в векторе. Даже если я установлю альфу на 0,0, она будет отображаться чистым красным цветом.

Я включил GL_BLEND. Вот мои инициализации:

GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);        
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);

Функция рисования настолько проста, насколько это возможно:

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
shader.use(); // will select the vertex and fragment shader
GLES20.glViewport(0, 0, resX, resY); // sets the viewport to the current resolution
GLES20.glEnableVertexAttribArray(v_aPosId);
GLES20.glVertexAttribPointer(v_aPosId, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 0, vertexBuf);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(v_aPosId);

Вид поверхности создается следующим образом:

glView = new GLSurfaceView(this);
glView.setEGLContextClientVersion(2);
glView.setRenderer(new GLRenderer(this));

Почему альфа-канал просто игнорируется? Я пропустил что-то включить или выключить? Используемое оборудование: Google Nexus 10 с Android 4.2.1.

Редактировать: когда я использовал glReadPixels() для чтения кадрового буфера, значения пикселей RGBA также были 255, 0, 0, 255, а не 255, 0, 0, 127, как должно быть. Однако я исправил это. Если вы хотите правильно отображать прозрачность, см. ответ @martijn-courteaux. Если вы хотите вернуть правильные значения пикселей (как через glReadPixels()), см. мой ответ.


person IsaacKleiner    schedule 27.11.2013    source источник


Ответы (2)


При выборе функции смешивания glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); вам придется предварительно умножить свой цвет. Итак, это означает, что вам придется умножать каждый цветовой компонент (красный, зеленый и синий) на значение альфа-канала. В вашем случае это будет: (1.0 * alpha, 0.0, 0.0, alpha). Функция смешивания описывает, как графический процессор будет вычислять окончательный цвет в результате двух смешанных цветов. Если вы выберете GL_ONE, GL_ONE_MINUS_SRC_ALPHA, у вас будет следующая формула:

finalColor = srcColor + (1 - srcAlpha) * dstColor;

Если вы заполните свой красный цвет, вы увидите:

finalColor = (1.0, 0.0, 0.0) + (1 - 0.5) * (0.0, 0.0, 0.0)
           = (1.0, 0.0, 0.0)

Вот почему он выглядит чисто красным.

Если вам это не нравится, то измените функцию смешивания на:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Если вы используете эту функцию, формула будет очевидной:

finalColor = srcAlpha * srcColor + (1 - srcAlpha) * dstColor;

Подключи свой красный:

finalColor = 0.5 * (1.0, 0.0, 0.0) + (1.0 - 0.5) * (0.0, 0.0, 0.0)
           = (0.5, 0.0, 0.0)

Обратите внимание, что предварительное умножение полезно при работе с изображениями: оно экономит GPU три умножения на фрагмент.

person Martijn Courteaux    schedule 27.11.2013
comment
Так что это правильная функция смешивания для меня: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Однако то, что я делаю, это считываю полученные пиксели с помощью glReadPixels(). Я использую формат GL_RGBA типа GL_UNSIGNED_BYTE, но получаю следующее: px value 0 = 127,0,0,255 и так далее. Таким образом, вывод glReadPixels() уже является окончательным результатом умножения. Но поскольку я использую OpenGL для генерации изображений, мне действительно нужен альфа-канал, а это значит, что я хочу, чтобы приведенный выше фрагментный шейдер создавал значения пикселей RGBA 255, 0, 0, 127. Как я могу этого добиться? - person IsaacKleiner; 28.11.2013

Я узнал это. Ответ @martijn-courteaux (glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)) совершенно верен в отношении отображения четырехугольника с правильной прозрачностью. Однако я пытался добиться чего-то другого, о чем еще не упоминал. Я не просто хотел правильно отобразить четырехугольник, но хотел получить правильные значения пикселей RGBA моего сгенерированного изображения, используя glReadPixels(). Если вы хотите сделать это, вам нужно выбрать правильную конфигурацию EGL в вашем GLSurfaceView, вызвав setEGLConfigChooser(8, 8, 8, 8, 0, 0). Обратите внимание на 4-ю «8». В документации Android говорится, что «по умолчанию представление выберет поверхность RGB_888», поэтому мне также пришлось установить альфа-канал на 8. Теперь вам нужно отключить смешивание через glDisable(GL_BLEND). Теперь поверхность GL будет рисовать четырехугольник без прозрачности, но если вы используете glReadPixels(), она вернет значения пикселей RGBA 255, 0, 0, 127, как и должно быть для приведенного выше фрагментного шейдера. Это полезно, если вы создаете изображения вне экрана на графическом процессоре.

person IsaacKleiner    schedule 28.11.2013
comment
Вам нужно смешивание. Выключение этого параметра перезаписывает предыдущее значение буфера. Вам просто нужно очистить кадр с альфа-каналом, установленным на 0, а также определить glAlphaFunc. - person Martijn Courteaux; 28.11.2013