OpenGL视频渲染 - 如何使纹理像素半透明?

时间:2018-01-15 04:43:05

标签: android video opengl-es

这是我用于呈现视频的onDrawFrame()的当前GLVideoRenderer代码:

private void drawActual(boolean useFilter) {
    Log.i("current_pos", currentFilterPosition+" "+Constant.ILLUSION);
    /*
    if(currentFilterPosition != Constant.ILLUSION) {
        GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    } else {
        GLES20.glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    }
    */

    if(currentFilterPosition != storedFilterPosition)
    {
        /*Changing fragment shader and resetting the uniform locations*/

        storedFilterPosition = currentFilterPosition;
    }
    Log.e("use_filter", "true");
    GLES20.glUseProgram(mProgram);
    checkGlError("glUseProgram");

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
    checkGlError("glBindTexture");

    Log.i("current_float_timestamp", muTimestampFloatHandle+"");
    onPreDrawFrame();
    checkGlError("onPreDrawFrame");

    mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
    GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
            TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
    checkGlError("glVertexAttribPointer maPosition");
    GLES20.glEnableVertexAttribArray(maPositionHandle);
    checkGlError("glEnableVertexAttribArray maPositionHandle");

    mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
    GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
            TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
    checkGlError("glVertexAttribPointer maTextureHandle");
    GLES20.glEnableVertexAttribArray(maTextureHandle);
    checkGlError("glEnableVertexAttribArray maTextureHandle");

    Matrix.setIdentityM(mMVPMatrix, 0);
    GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
    checkGlError("glmuMVPMatrixHandleSetting");
    GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
    checkGlError("glmuSTMatrixHandleSetting");

    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    checkGlError("glDrawArrays");
    //GLES20.glFinish();
}

这是我用来尝试半透明地渲染视频帧的片段着色器:

#extension GL_OES_EGL_image_external : require
precision mediump float;

uniform samplerExternalOES u_Texture;

varying highp vec2 v_TexCoordinate;

uniform float uParamValue1; //filter strength

void main()
{
    vec4 texel = texture2D(u_Texture, v_TexCoordinate).rgba;
    texel.a *= 0.2;
    /*
    if(texel.a > 0.5)
        discard;
        */
    gl_FragColor += texel;
}

我的问题是我的视频帧中的alpha值必须全部为255或1.0,因为在片段着色器中使用该注释掉的代码进行深度测试时,它或者全部存在,或者全部消失。更糟糕的是,当我使用这段代码时,alpha缩减没有效果 - 渲染器只渲染RGB组件。

我对片段着色器的期望实际上是应用了一点运动模糊效果,所以我希望着色器半透明地渲染视频帧,删除清除函数以防止渲染器在之前清除帧。 / p>

我该怎么做,我的代码丢失了什么?

1 个答案:

答案 0 :(得分:0)

如果我正确理解你的目标,你就会缺少混合。查看glBlendFunc或更有可能glBlendFuncSeparate。另外,请不要忘记使用glEnable启用混合。在您的情况下,您可能会发现使用glColorMask掩盖alpha的有用。

因此,要查看通常的绘图管道,您将使用glClear或已经绘制的某个场景开始使用背景颜色。现在你希望绘制一些有效混合它的对象。

混合时最常见的功能是使用glBlendFunc(src_alpha, one_minus_src_alpha)。这意味着它将采用缓冲区中的任何内容,并仅使用即将到来的alpha将其与即将到来的颜色混合。因此,假设缓冲区已被清除为(0,0,0,0),并且您正在绘制(1.0, 1.0, 1.0, 0.5),这是一种半透明的白色。结果是:

(1.0, 1.0, 1.0, 0.5)*0.5 + (0,0,0,0)*(1.0-0.5) = (0.5, 0.5, 0.5, 0.25)

正如你所看到的那样,0.5的颜色都很好但是alpha版本很棒0.25这在导出到某些图片时会出现问题,但在查看时却不是问题在不透明的表面上(大多数情况下),因为在那里简单地忽略了alpha。

所以相比之下,如果我们清除不透明的黑色,我们得到

res = (1.0, 1.0, 1.0, 0.5)*0.5 + (0,0,0,1)*(1.0-0.5) = (0.5, 0.5, 0.5, 0.75)

这确实没有取得更好的结果。

一个解决方案显然是使用glBlendFuncSeparate,它仅对alpha组件使用单独的参数,可以用作glBlendFuncSeparate(src_alpha, one_minus_src_alpha, gl_zero, gl_one)。这将在颜色上完全相同,但alpha组件将保持不变,保持在它开始的任何位置。

但是,由于您不想在目标缓冲区上使用alpha,因此您可以使用glColorMask(true, true, true, false)将其屏蔽掉并处理RGB组件。这不会在任何时候禁用alpha,它只会禁止写入。

所以假设这样的事情应该有效:

glClearColor(0,0,0,1); // Set clear color to opaque black
glClear(); // Clear the buffer

glDisable(blend); // Disable blending to draw background
glColorMask(true, true, true, false); // Disable writing to alpha component

glDraw(originalVideoFrame); // Draw the background

glEnable(blend); // Enable blending
glBlendFunc(src_alpha, one_minus_src_alpha); // Set standard blending function
glColorMask(true, true, true, false); // Redundant call, may be removed

glDraw(semitransparentFrame); // Draw semitransparent part

要使用单个像素进行检查,我们假设第一部分将绘制一个有点透明的红色(1, 0, 0, 0.75),然后与黄色半透明像素(1, 1, 0, 0.5)混合,这将会发生:< / p>

  1. (0.0, 0.0, 0.0, 1.0)来自明确
  2. (1.0, 0.0, 0.0, 1.0)画出红色。 alpha和混合都被禁用,因此只复制颜色而忽略其他所有内容
  3. (1.0, 0.5, 0.0, 1.0)使用混合作为(1, 1, 0, 0.5)*0.5 + (1.0, 0.0, 0.0, 1.0)*(1.0-0,5) = (0.5, 0.5, 0.0, 0.25) + (0.5, 0.0, 0.0, 0.5) = (1.0, 0.5, 0.0, 0.75)绘制黄色,再次忽略对alpha的更改。
  4. 我希望这可以帮助您了解混合并解决您的问题。