OpenGL Alpha Mask

时间:2014-09-11 13:37:26

标签: opengl mask alpha freetype

我尝试使用glAlphaBlend及其所有参数进行alhpa遮蔽。我在stackoverflow上找到了很多类似的答案,但我没有意识到任何事情。

我有三张图片,一张背景(1),一张面具(2)和一张文字(3)。

我想要的是绘制背景,将我的蒙版减去文本以最终获得图像(A)。

Image Example

Draw background

glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glBegin(GL_QUADS);
glColor4ub(0,0,0,255);
glVertex2d(mxIncrust0 - bgwidth, myIncrust0);
glVertex2d(mxIncrust0, myIncrust0);
glVertex2d(mxIncrust0, myIncrust0 + textheight);
glVertex2d(mxIncrust0 - bgwidth, myIncrust0 + textheight);
glEnd();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ZERO, GL_ONE);

Draw Text

但是一切正常!

实现它的真正方法是什么?

我的问题不同,因为它不是基于OpenGL ES而是仅基于Open GL。

3 个答案:

答案 0 :(得分:7)

实现这一目标的“真正方式”是使用shader programsmodern OpenGL。为什么使用这个已弃用的API?我们是在2014年将OpenGL 4.5作为最新版本。你甚至不能声称你关心向后兼容性,因为即使是一些真正的旧卡(好吧,5岁)今天至少支持GL 3.2,它是完全可编程的。你知道吗,即使使用GL 2.0,你仍然可以使用着色器,但是如果您打算在不久的将来为您的生活编写OpenGL代码,那么使用已弃用的API将无处可去。

现在,使用着色器,屏蔽是最简单的任务之一。 您通常会将2个纹理传递到片段着色器中.1 - 漫反射纹理(覆盖表面的实际纹理)2 - 蒙版纹理(可能是单通道灰度纹理)

片段着色器看起来像这样:

#version 150
uniform sampler2D maskTex;
uniform sampler2D colorTex;

out vec4 colorOut;
in smooth vec2 texCoords;
void main(){ 

     vec4 color = texture(colorTex,texCoords);
     vec4 mask  = texture(maskTex,texCoords);

     colorOut =vec4(color.rgb,color.a * mask.r);//alpha value can be in any channel ,depends on texture format.
}

尽可能简单。希望它有所帮助。

P.S确保你也可以对alpha纹理进行mipmap,以防漫反射纹理被mipmap处理。 另请注意,我的示例并不是为了解决您的具体任务,而是向您展示如何在可编程OpenGL中完成基本屏蔽。从这里可以很容易地找出要做的事情;)

<强>附录:

下面的评论中有人想知道为什么在地球上会想要使用额外的纹理来保存alpha蒙版数据。这是我的答案:

在许多复杂的应用程序中,用户使用称为alpha贴图的附加纹理来遮盖主要纹理的像素,同时还要考虑到该过程中纹理的alpha值,并且没有理由将alpha通道“合并”到一个纹理贴图中因为它需要额外的步骤和浪费运行时才没有真正的目的。在一些复杂的渲染场景中,这些alpha贴图只能在请求的不同渲染过程中生成,然后被提取到片段着色器中以供最终使用。所以在这种情况下,应用程序在运行时之前甚至没有那些数据。此外,许多不同的颜色纹理可能需要相同的alpha蒙版。将它合并到所有这些中是否有意义?当然不是。上面的做法是众所周知的,并且与在CPU上合并透明度数据相比,只是为了避免额外的纹理采样。

答案 1 :(得分:1)

我终于找到了一个使用glClipPlane的解决方案,我没有找到任何带着色器的解决方案,因为它没有纹理。

那它是如何运作的? 1)我绘制背景(四边形,线条......) 2)我定义并激活了一个限制,在左边没有任何东西是绘制,并且在右边没有任何东西的限制 3)我绘制了我需要的一切(四,线,我的文字......) 4)我禁用了剪辑。

// Draw the background
double* leftMask = new double[4];
leftMask[0] = 1;
leftMask[3] = -mClipXL; // Be careful to use -Limit for the left limit
glClipPlane(GL_CLIP_PLANE0, leftMask);
delete[] leftMask;
glEnable(GL_CLIP_PLANE0);

double* rightMask = new double[4];
rightMask[0] = -1;  // Be careful to use -1 for the right limit
rightMask[3] = mClipXR;
glClipPlane(GL_CLIP_PLANE1, rightMask);
delete[] rightMask;
glEnable(GL_CLIP_PLANE1);

// draw the text

// disable the clip to draw anything else.
glDisable(GL_CLIP_PLANE0);
glDisable(GL_CLIP_PLANE1);

它的工作非常好!!!

答案 2 :(得分:0)

您也可以为此使用模板缓冲区。首先,您将启用模板测试:

glEnable(GL_STENCIL_TEST);

然后,您首先仅在模板缓冲区中绘制遮罩四边形:

//begin mask
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilFunc(GL_NEVER, 0, 0);
glStencilOp(GL_INCR, GL_KEEP, GL_KEEP);
glClear(GL_STENCIL_BUFFER_BIT);
//draw your mask, a quad in your case

然后绘制遮罩的部分:

    ////masked fill part
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glStencilFunc(GL_NOTEQUAL, 0, 1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    // draw the masked element, the text in your case

再次禁用模板测试

glDisable(GL_STENCIL_TEST);

根据您的需要,可以更改glStencilFunc()和glStencilOp()参数以获得所需的剪切效果。