OpenGL:在背景图像上使用蒙版绘制颜色

Posted

技术标签:

【中文标题】OpenGL:在背景图像上使用蒙版绘制颜色【英文标题】:OpenGL: Draw color with mask on a background image 【发布时间】:2019-09-03 09:56:36 【问题描述】:

我需要在图像上绘制具有某种形状的颜色。我的想法是提供一个具有给定形状(例如心形)的蒙版,然后用颜色填充矩形区域并使用蒙版将其渲染到最终图像上。

蒙面:

等于:

矩形的颜色是在运行时决定的——这就是为什么我不自己画彩色的心。

除了心脏(alpha 为 255)之外,黑色心脏图像在任何地方都是透明的(alpha 为 0)。 我尝试使用:

glBlendFunc(GL_DST_ALPHA, GL_ZERO)

源是纯色,目标是 Alpha 通道图像。 我使用https://www.andersriggelsen.dk/glblendfunc.php 寻求帮助。

但是底部图像(树)被用作 DST 图像... 似乎我需要一个中间缓冲区来首先渲染蓝心,然后在树上进行第二次渲染。

有什么办法呢?

【问题讨论】:

如果你知道最终心脏的颜色(比如蓝色),那么将它作为 uniform(你使用 OGL >1.1,对吗?)传递给着色器并使用它作为片段的颜色。并在绘制树后绘制心脏,禁用深度测试或将心脏向相机移动一点。 我不知道颜色 - 每次都会是不同的颜色,在运行时决定...... 但你在画心之前就知道了,不是吗? 【参考方案1】:

如果树是之前画的,它会出现在dest的颜色中,改变你的最终结果。

你是对的,你需要一个中间缓冲区来存储应该渲染 quand 的哪个部分,以及你的心形。

OpenGL为此提供了一个完美的工具,称为模板缓冲区。

在你的情况下,我将像往常一样渲染我的场景(树)

然后我将启用模板缓冲区glEnable(GL_STENCIL_TEST);

禁用对 colorBuffer glColorMask(false, false, false, false); 的写入,

只用适当的蒙版画心。 glStencilMask(0xFF);

然后,您使用glStencilFunc(GL_EQUAL, 1, 0xFF) 启用模板测试来绘制彩色四边形

不要忘记每帧清除模板缓冲区glClear(GL_STENCIL_BUFFER_BIT);

你可以在网上找到一些不错的教程:https://learnopengl.com/Advanced-OpenGL/Stencil-testing

【讨论】:

【参考方案2】:

这是在不需要模板缓冲区的旧版 OpenGL(我假设您正在使用)中执行此操作的一种非常简单的方法:

public void render() 
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);

    glLoadIdentity();
    glOrtho(0, 1, 1, 0, 1, -1);

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    // Regular blending
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_ALPHA_TEST);
    // Discard transparent pixels. Not strictly necessary but good for performance in this case.
    glAlphaFunc(GL_GREATER, 0.01f);

    glColor3f(1,1,1);
    glBindTexture(GL_TEXTURE_2D, treeTexture);
    drawQuad();

    glColor3f(1,0,1); // Your color goes here
    glBindTexture(GL_TEXTURE_2D, maskTexture);
    drawQuad();


private void drawQuad() 
    glBegin(GL_QUADS);
    glTexCoord2f(0,0);
    glVertex2f(0,0);
    glTexCoord2f(0,1);
    glVertex2f(0,1);
    glTexCoord2f(1,1);
    glVertex2f(1,1);
    glTexCoord2f(1,0);
    glVertex2f(1,0);
    glEnd();

这里,treeTexture 是树纹理,maskTexturewhite-on-transparent 心形。

结果:

原理是在遗留的 OpenGL 管道中,您可以在 glVertex* 之前使用 glColor* 来指定纹理颜色(在本例中为白色或透明)与组件相乘的颜色。

请注意,使用此方法,您可以轻松地以多种不同颜色渲染多种颜色的形状,而无需清除模板缓冲区(相对昂贵)。我建议将遮罩纹理裁剪到实际遮罩形状的边界,以节省 GPU 丢弃所有透明片段的小工作。

【讨论】:

谢谢!是不是说,当升级到现代 OpenGL 时,这个方法就不能用了? 使用现代 OpenGL,您会做完全相同的事情,但是从片段着色器内部而不是使用固定功能管道,这使得代码看起来完全不同。本质上,您会将所需的颜色作为统一变量传递给片段着色器,并将其与您从蒙版纹理采样器获得的颜色相乘以生成最终的片段颜色。如果您使用的是现代 OpenGL,您可以将片段着色器粘贴到您的问题中,我将向您展示如何修改它。 无论哪种情况,这都是正确的做法。不需要额外的缓冲区。

以上是关于OpenGL:在背景图像上使用蒙版绘制颜色的主要内容,如果未能解决你的问题,请参考以下文章

photoshop怎么抠图换背景

OpenGL/GLSE alpha 遮罩

带有黑色背景的 alpha 图像绘制其他白色...?

Android opengl 画文字,怎么把文字后面的黑色背景去掉

UISlider - 将线条/图像绘制到轨道(条形图)

Alpha 混合和 UIView 背景颜色