OpenGL模板 - 排除透明像素

时间:2015-03-27 15:30:05

标签: opengl shader mask stencil-buffer

我有这个纹理,我想使用Stencil缓冲区作为掩码:

然后我想在屏幕上绘制一个IMG图像,该图像只出现在上面的图像可见的位置(有颜色的地方),因此排除了上边缘上方和渐变下方的透明像素。

问题在于,每当我在屏幕上绘制IMG图像时,无论像素是否透明,它都会出现在图像的任何位置。

所以我考虑使用ALPHA_TEST,但它已经在最新的OpenGL版本中消失了,所以我尝试在我的片段着色器中丢弃带有v_color.a == 0v_color.r == 0的片段,但它什么也没做。 ..

片段着色器:

#ifdef GL_ES
    #define LOWP lowp
    precision mediump float;
#else
    #define LOWP
#endif

uniform sampler2D u_texture;

varying LOWP vec4 v_color;
varying vec2 v_texCoords;

void main() {
    if (v_color.a == 0) {
        discard;
    }

    gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
}

顶点着色器:

attribute vec4 a_color;
attribute vec4 a_position;
attribute vec2 a_texCoord0;

uniform mat4 u_projTrans;

varying vec4 v_color;
varying vec2 v_texCoords;

void main() {
    v_color = a_color;
    v_texCoords = a_texCoord0;
    gl_Position = u_projTrans * a_position;
}

代码:

@Override
public void draw(final DrawConfig drawConfig) {
    this.applyTransform(drawConfig.getBatch(), this.computeTransform());

    // Draw mask to stencil buffer

    drawConfig.end();
    final GL20 gl = Gdx.gl;

    gl.glEnable(GL20.GL_STENCIL_TEST);
    gl.glStencilFunc(GL20.GL_ALWAYS, 1, 0xFF);
    gl.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);
    gl.glStencilMask(0xFF);
    gl.glColorMask(false, false, false, false);
    gl.glClearStencil(0);
    gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);

    drawConfig.begin(Mode.BATCH);
    this.mBackground.setClippingEnabled(true); // The clipping area is a rectangle centered in the screen
    this.mBackground.draw(drawConfig);
    this.mBottomBorder.draw(drawConfig); // Border below the rectangle area
    this.mTopBorder.draw(drawConfig); // Border above the rectangle area
    drawConfig.end();

    gl.glColorMask(true, true, true, true);
    gl.glStencilFunc(GL20.GL_EQUAL, 1, 0xFF);
    gl.glStencilMask(0x00);

    // Draw elements

    this.mBackground.setClippingEnabled(false); // No clipping area, use stencil test
    this.mBackground.draw(drawConfig);
    this.mBottomBorder.draw(drawConfig);
    this.mTopBorder.draw(drawConfig);

    drawConfig.end();
    Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);

    this.resetTransform(drawConfig.getBatch());
}

结果:

红色箭头表示出现在边界之外且我想要消失的部分。 为方便起见,黄色方块是剪裁区域。

1 个答案:

答案 0 :(得分:1)

您尝试复制的固定功能alpha测试(GL_ALPHA_TEST)是每片段测试。在片段着色器完成之后,它已经发生,您可以使用discard自己实现它。

但是,您当前的解决方案是使用插值的每顶点alpha值,这与您尝试用作蒙版的纹理无关。您需要测试分配给gl_FragColor的Alpha值才能正确执行此操作:

void main() {
    gl_FragColor = v_color * texture2D(u_texture, v_texCoords);

    if (gl_FragColor.a == 0.0) {
        discard;
    }
}

这是基于现代着色器的等价物:

glAlphaFunc (GL_NOTEQUAL, 0.0f); // Reject fragments with alpha == 0.0
glEnable    (GL_ALPHA_TEST);