libgdx open gles 2.0 模板 alpha 遮罩

Posted

技术标签:

【中文标题】libgdx open gles 2.0 模板 alpha 遮罩【英文标题】:libgdx open gles 2.0 stencil alpha masking 【发布时间】:2014-07-18 08:14:26 【问题描述】:

我正在寻找一种解决方案,通过 open gles 2.0 在 libgdx 中使用模板缓冲区实现 alpha 屏蔽。

我已经设法使用模板缓冲区和着色器实现简单的 alpha 掩码,如果片段的 alpha 通道大于某个指定值,它就会被丢弃。效果很好。

问题是当我想使用一些渐变图像蒙版或绑定 png 蒙版时,我没有得到我想要的(我得到没有 alpha 通道的“填充”矩形蒙版),而是我想要平滑淡出蒙版.

我知道问题是在模板缓冲区中只有 0 和 1,但我想写入模板一些其他值,这些值表示在片段着色器中传递的片段的实际 alpha 值,并使用该值来自模板以某种方式进行一些混合。 我希望我已经解释了我想要得到的东西,如果可能的话。

我最近开始玩OpenGL ES,所以还是有一些误解。

我的问题是:如何设置和模板缓冲区来存储除 0 和 1 以外的值,以及如何在以后使用这些值进行 alpha 屏蔽?

提前发送。

这是我目前的模板设置:

    Gdx.gl.glClearColor(1, 1, 1, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_STENCIL_BUFFER_BIT |   GL20.GL_DEPTH_BUFFER_BIT);

    // setup drawing to stencil buffer
    Gdx.gl20.glEnable(GL20.GL_STENCIL_TEST);
    Gdx.gl20.glStencilFunc(GL20.GL_ALWAYS, 0x1, 0xffffffff);
    Gdx.gl20.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);
    Gdx.gl20.glColorMask(false, false, false, false);
    Gdx.gl20.glDepthMask(false);

    spriteBatch.setShader(shaderStencilMask);
    spriteBatch.begin();

    // push to the batch
    spriteBatch.draw(Assets.instance.actor1, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, Assets.instance.actor1.getRegionWidth(), Assets.instance.actor1.getRegionHeight());

    spriteBatch.end();

    // fix stencil buffer, enable color buffer
    Gdx.gl20.glColorMask(true, true, true, true);
    Gdx.gl20.glDepthMask(true);
    Gdx.gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);

    // draw where pattern has NOT been drawn
    Gdx.gl20.glStencilFunc(GL20.GL_EQUAL, 0x1, 0xff);

    decalBatch.add(decal);
    decalBatch.flush();

    Gdx.gl20.glDisable(GL20.GL_STENCIL_TEST);

    decalBatch.add(decal2);
    decalBatch.flush();

【问题讨论】:

Stenciling 不允许部分 alpha 值。为了获得介于两者之间的值,我认为您必须将您的东西绘制到 FrameBuffer 中,然后使用 FrameBuffer 的颜色纹理和表示 alpha 的纹理通过多重纹理绘制到屏幕上。但根据您的具体需求,可能会有更简单的解决方案。 我试图掩盖 3d 平面,例如一个平面代表面具,另一个平面在面具平面后面。我希望在面罩的纹理透明的地方变得透明。我目前在 Libgdx 中使用贴花。如果没有 fps 下降,这是否可能? 平面是指 3D 空间中的矩形?如果两个矩形的大小、方向和位置完全相同,那么您可以使用多纹理来实现,尽管这需要对贴花进行一些变通。 是的,平面基本上是 3D 空间中的矩形。面具应该是静态的,面具后面的平面可以移动。并且重叠部分应该被屏蔽。 我只能想到涉及FrameBuffers的方法。是否影响 FPS 取决于你的瓶颈是什么。 【参考方案1】:

我能想到的唯一方法是使用 FrameBuffer。

选项 1

将场景的背景(不会被遮罩的东西)绘制到 FrameBuffer 中。然后将没有蒙版的整个场景绘制到屏幕上。然后使用 FrameBuffer 的颜色附件将您的蒙版贴花绘制到屏幕上。这种方法的缺点是,在 android 上的 OpenGL ES 2.0 中,FrameBuffer 可以具有 RGBA4444,而不是 RGBA8888,因此在颜色位深度发生变化的遮罩边缘会出现可见的接缝。

选项 2

将您的蒙版贴花绘制为对您的 FrameBuffer 不透明的 B&W。然后将背景绘制到屏幕上。当你绘制任何可以被遮罩的东西时,用多重纹理绘制它,乘以 FrameBuffer 的颜色纹理。潜在的缺点是,任何可以被遮罩的东西都必须使用自定义着色器进行多纹理绘制。但是,如果您只是使用贴花,那么这实际上并不比选项 1 复杂。

以下内容未经测试...可能需要一些调试。

在这两个选项中,我将继承 CameraGroupStrategy 以在绘制蒙版贴花时与 DecalBatch 一起使用,并覆盖 beforeGroups 以同时设置第二个纹理。

public class MaskingGroupStrategy extends CameraGroupStrategy

    private Texture fboTexture;

    //call this before using the DecalBatch for drawing mask decals
    public void setFBOTexture(Texture fboTexture)
        this.fboTexture = fboTexture;
    

    @Override
    public void beforeGroups () 
        super.beforeGroups();
        fboTexture.bind(1);
        shader.setUniformi("u_fboTexture", 1);
        shader.setUniformf("u_screenDimensions", Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    

在您的着色器中,您可以像这样获得 FBO 纹理颜色:

vec4 fboColor = texture2D(u_fboTexture, gl_FragCoord.xy/u_screenDimensions.xy);

那么对于选项 1:

gl_FragColor = vec4(fboColor.rgb, 1.0-texture2D(u_texture, v_texCoords).a);

或者对于选项 2:

gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
gl_FragColor.a *= fboColor.r;

【讨论】:

嘿,它在桌面上完美运行!但是在android上什么都没有画出来。你知道为什么吗?我尝试过使用帧缓冲格式 8888 和 4444,但它是一样的。 我已经调试...绘图到帧缓冲区工作,但使用自定义组策略的步骤不会在屏幕上绘制任何内容。这是我的 beforeGroupsMethod: Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);着色器.开始(); shader.setUniformMatrix("u_projTrans", camera.combined); gdx.graphics.getGL20().glActiveTexture(GL20.GL_TEXTURE1); fboTexture.bind(1); shader.setUniformi("u_fboTexture", 1); shader.setUniformf("u_screenDimensions", Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); gdx.graphics.getGL20().glActiveTexture(GL20.GL_TEXTURE0); shader.setUniformi("u_texture", 0); 当某些东西在桌面上运行而不是在 Android 上运行时,对我来说这通常是因为着色器中的某些东西导致着色器无法编译。编译着色器后,将其用于调试目的:if (!shader.isCompiled())Gdx.app.log("shader compile failed", shader.getLog());。这将告诉您编译时发生的任何警告或错误。它实际上为您提供了错误发生的行号和列号,而不是它们是否发生在顶点着色器或片段着色器中,因此有时您必须同时检查两者。 嘿,我已经在手机上尝试过我的代码,它正在工作......我也在 STB 设备上尝试过,但没有任何东西被绘制出来。着色器中没有错误或警告日志。我什至尝试将其替换为绘制简单的红色框,但这没有任何区别。你知道一些可能有帮助的设置吗? STB 设备是我的目标平台,所以它必须在它上面工作。有什么建议么?这是果冻豆 4.2.2。 我找到了解决方案:我唯一需要添加的是 calulus 中的 v_color 用于片段着色器中的片段颜色。事件没用,我猜STB上的编译器版本有问题或类似的东西。

以上是关于libgdx open gles 2.0 模板 alpha 遮罩的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL ES 2.0 剪裁测试

OpenGL ES 2.0 shader开发

使用 Open GL ES 2.0 创建 OFF SCREEN 表面

OpenGL ES 2.0 卷绕和背面剪裁

Android GLES 2.0:帧缓冲渲染到半浮点纹理适用于某些设备,而不是其他设备

如何设置 Open GLES2.0 与 Android 相机配合使用?