【问题标题】:OpenGL Video Rendering - How do I make the texels semi-transparent?OpenGL 视频渲染 - 如何使纹素半透明?
【发布时间】:2018-01-15 19:29:37
【问题描述】:

这是我目前用于渲染视频的GLVideoRendereronDrawFrame() 代码:

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 组件。

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

我该怎么做,我的代码是否遗漏了什么?

【问题讨论】:

    标签: android video opengl-es


    【解决方案1】:

    如果我正确理解您的目标,您就错过了混合。查看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 在那里被简单地忽略了。

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

    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)混合,这将发生:

    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 的变化。

    我希望这可以帮助您了解混合并解决您的问题。

    【讨论】:

    • 感谢您提供GLES20.glEnable(GLES20.GL_BLEND) 解决方案。这是我寻求的答案的一部分。另一部分实际上是关于保留前一帧的缓冲区而不是每帧清除它。是否涉及Framebuffers 的解决方案?
    • @Gensoukyou1337 如果我理解正确,您将需要 2 个非常标准的缓冲区。在您的情况下,将使用带有附加纹理的 FBO、帧缓冲区对象。基本上这意味着您将绘制纹理,然后使用该纹理进行后期处理。如果你看看它的一些例子可能是最好的......
    猜你喜欢
    • 1970-01-01
    • 2014-08-12
    • 1970-01-01
    • 2014-08-07
    • 2011-12-26
    • 2010-11-21
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    相关资源
    最近更新 更多