OpenGL - 多纹理蒙版

19

我已经按照以下概念在OpenGL中实现了掩蔽:

  • 掩膜由黑色和白色颜色组成。
  • 前景纹理只应在遮罩的白色部分可见。
  • 背景纹理只应在遮罩的黑色部分可见。

我可以通过使用glBlendFunc()使白色部分或黑色部分按预期工作,但不能同时使用两种颜色,因为前景层不仅会混合到掩模层,还会混合到背景层。

有没有人知道如何以最佳方式完成这项任务?我已经在网上搜索并阅读了一些关于片元着色器的内容。这是正确的方法吗?


我知道有一种方法可以做到这一点,我非常确定它是一个简单的“掩码”函数,但自从我上次使用OpenGL已经过去很久了。你看过这个网站吗:http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=20 - Chris Pfohl
感谢您的快速回复。是的,我的实现基于那个教程,并且适用于一个遮罩和一个纹理,但是当我使用上述描述的两个纹理来制作一个遮罩时,结果并不是我所期望的:前景图像颜色也会混合到背景层而不仅仅是遮罩。我应该发布一张截图来说明我的问题吗? - red
这对于片段着色器来说是非常基础(简单)的任务。不过,我不能指向一个适当的固定功能解决方案。 - kvark
2个回答

43

这应该可以正常工作:

glEnable(GL_BLEND);
// Use a simple blendfunc for drawing the background
glBlendFunc(GL_ONE, GL_ZERO);
// Draw entire background without masking
drawQuad(backgroundTexture);
// Next, we want a blendfunc that doesn't change the color of any pixels,
// but rather replaces the framebuffer alpha values with values based
// on the whiteness of the mask. In other words, if a pixel is white in the mask,
// then the corresponding framebuffer pixel's alpha will be set to 1.
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ZERO);
// Now "draw" the mask (again, this doesn't produce a visible result, it just
// changes the alpha values in the framebuffer)
drawQuad(maskTexture);
// Finally, we want a blendfunc that makes the foreground visible only in
// areas with high alpha.
glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
drawQuad(foregroundTexture);

这有点棘手,如果有不清楚的地方请告诉我。
创建GL上下文时,不要忘记请求alpha缓冲区。否则可能会得到没有alpha缓冲区的上下文。 编辑: 这里我画了一个插图。 illustration 编辑: 自从写这个答案以来,我学到了更好的方法:
  • 如果你受限于OpenGL的固定管线,请使用纹理环境
  • 如果你可以使用着色器,请使用片段着色器。
本答案描述的方法可行且性能并不比这两种更好的选项差,但不够优雅和灵活。

谢谢Stefan!我再也找不到比这更好的答案了!在我的实现中,我用glBlendFuncSeparate替换了glBlendFunc(GL_SRC_COLOR, GL_ZERO),现在它可以工作了。所以我的错误是glBlendFunc立即替换颜色。 - red
有没有不调用glBlendFuncSeparate的方法来实现这个?当我加入那行代码时,它会导致我的显卡崩溃。 - Skyler
它对我也不起作用 :( 只显示前景 - Post Self
你会如何制作一个片段着色器?它如何了解遮罩层? - Post Self
@Skyler:你可能需要使用像GLEW这样的扩展加载机制。 - Stefan Monov
显示剩余3条评论

0

Stefan Monov的回答非常好!但是对于那些仍然无法使他的答案工作的人:

  1. 你需要检查GLES20.glGetIntegerv(GLES20.GL_ALPHA_BITS, ib) - 你需要非零的结果。
  2. 如果你得到了0 - 前往EGLConfig并确保你传递了alpha bits

    EGL14.EGL_RED_SIZE, 8,
    EGL14.EGL_GREEN_SIZE, 8,
    EGL14.EGL_BLUE_SIZE, 8,
    EGL14.EGL_ALPHA_SIZE, 8, <- 我没有这个并且浪费了很多时间
    EGL14.EGL_DEPTH_SIZE, 16,
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接