使用透明度时渲染到纹理时渲染覆盖透明度

Posted

技术标签:

【中文标题】使用透明度时渲染到纹理时渲染覆盖透明度【英文标题】:Rendering Overwriting Transparency When Rendering to Texture When Using Transparency 【发布时间】:2019-04-02 02:08:01 【问题描述】:

OpenGL 中渲染到纹理(辅助帧缓冲区)时,任何透明的都会覆盖下面的透明度,而不是像透明度一样添加透明度。这意味着如果我在现有的不透明对象之上渲染一个透明对象,结果是一个透明的纹理,即使它应该是不透明的。

在这张图片中,空间正常渲染为背景,然后更改framebuffer,然后渲染不透明的蓝色,然后渲染绿色/红色。 framebuffer 被渲染到的纹理用于渲染到原始的framebuffer(窗口)上,结果如图所示。

一些代码:

帧缓冲区/纹理创建:

        int texture = glGenTextures();
        framebufferID = glGenFramebuffers();
        glBindFramebuffer(GL_FRAMEBUFFER,framebufferID);
        glBindTexture(GL_TEXTURE_2D, texture);
        int width,height;
        width = (int)(getMaster().getWindow().getWidth()*(xscale/(16f*getMaster().getGuiCamera().getWidth()))*1.2f);
        height = (int)(getMaster().getWindow().getHeight()*(yscale/9f)*1.15f);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,0);
        int depth = glGenTextures();
        glBindTexture(GL_TEXTURE_2D, depth);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0);
        this.texture = new Texture(texture,width,height,true);
        glDrawBuffer(GL_FRONT_AND_BACK);

这里的深度缓冲区是我试图让它工作的东西,但它对输出纹理没有影响。

渲染代码:

        getMaster().returnToViewportAfterFunction(new Vector2i(texture.getWidth(),texture.getHeight()),
            () -> getMaster().returnToFramebufferAfterFunction(framebufferID, () -> 

                shapeShader.bind();
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                model.bind();
                shapeShader.setUniform("color", 0f, 0f, 1f, 1f); //Blue
                shapeShader.setUniform("projection", this.camera.getProjectionForGuiWindow(vec2zero, xscale, yscale));
                glDrawElements(GL_TRIANGLES, model.getVerticeCount(), GL_UNSIGNED_INT, 0);

                shapeShader.setUniform("color", 0f, 1f, 0f, 0.5f); //Green
                shapeShader.setUniform("projection", this.camera.getProjectionForGuiWindow(vec2zero, xscale/2f, yscale/2f));
                glDrawElements(GL_TRIANGLES, model.getVerticeCount(), GL_UNSIGNED_INT, 0);

                shapeShader.setUniform("color", 1f, 0f, 0f, .2f); //Red
                shapeShader.setUniform("projection", this.camera.getProjectionForGuiWindow(vec2zero, xscale/4f, yscale/2f));
                glDrawElements(GL_TRIANGLES, model.getVerticeCount(), GL_UNSIGNED_INT, 0);

                glDisable(GL_BLEND);

                model.unbind();
            )
        );
        //Renders the texture we just made
        shader.bind();
        model.bind();
        textureCoords.bind(texture);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        shader.setUniform("color", 1f, 1f, 1f, 1f);
        shader.setUniform("projection", camera.getProjectionForGuiWindow(getPosition(), xscale, yscale));
        glDrawElements(GL_TRIANGLES, model.getVerticeCount(), GL_UNSIGNED_INT, 0);
        glDisable(GL_BLEND);

        model.unbind();

用透明对象渲染framebuffer 0 中的不透明对象不会导致生成的像素是透明的,否则它们会显示为glClearColor 和不透明对象的混合,那么为什么会在framebuffer 用于渲染纹理?不应该是一致的吗?我觉得也许我错过了我的framebuffer 的一些附件,但我不能确定是什么。我看到一个解决方案说使用glColorMask(true,true,true,false),最后一个错误意味着没有alpha写入,乍一看似乎可以工作,但是当我想要透明度时为什么要禁用透明度?如果保持打开状态,它似乎也会导致许多问题。任何人都可以提供一些见解吗?提前致谢!

编辑:经过进一步分析,glColorMask 不是解决方案。

【问题讨论】:

【参考方案1】:

您必须使用一个可分离的混合函数,它分别处理 RGB 和 Alpha 值:glBlendFuncSeparate。对于您继续使用GL_SRC_ALPHA, GL_SRC_ONE_MINUS_ALPHA 的RGB 值。对于 Alpha 值,请使用 GL_ONE, GL_ONE 之类的东西,这样事情就会简单地相加。

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

【讨论】:

哇,这似乎有效!我最大的问题是为什么?让我感到困惑的是,使用 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) 的透明度适用于普通帧缓冲区(fbo #0)中的透明度,那么为什么它在链接到纹理的帧缓冲区中不起作用呢?我需要在帧缓冲区上编辑一些东西以使更简单的功能工作,还是我误解了一些基本的东西?我刚才检查了一下,可以确认两个函数在 fbo 0 中看起来相同,但两个函数在任何其他 fbo 中看起来都不一样。请原谅我问,但我需要理解。 @PoisonedPorkchop:原因是,除非指定了 GL_DST_… 混合函数,否则在混合过程中根本不会考虑目标 alpha 值,因此如果目标 alpha 变得“疯狂”没有看到效果。但是,如果将生成的目标 Alpha 值作为源 Alpha 值输入到稍后的渲染过程中,则情况会发生变化。这就是在您的情况下发生的情况:您正在将某些东西渲染到纹理并且您拥有的混合函数只会将透明 alpha 传递给目标像素,如果您不使用分离,则会覆盖不透明。 那么,是这样的吗? glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_DST_ALPHA); 上面的代码修复了覆盖现有 alpha 值的问题,但它似乎没有堆叠 alpha 值(也就是两个半透明对象在彼此顶部渲染会产生一个半透明对象,而一个不透明的对象和一个半透明的对象会产生一个不透明的对象)。 @PoisonedPorkchop:使用您建议的值,源 alpha 与自身相乘,目标 alpha 也与自身相乘,然后相加。 IE。你会得到s² + d²【参考方案2】:

对于任何想知道的人,我的问题的最佳解决方案是使用 glBlendFuncSeparate(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE_MINUS_DST_ALPHA,GL_ONE) 而不是 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

【讨论】:

以上是关于使用透明度时渲染到纹理时渲染覆盖透明度的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL渲染到纹理透明度问题

OpenGL:渲染具有大量纹理透明度的模型,没有绘制顺序?

如何在 Xcode 中渲染具有半透明纹理的 3D 模型?

OpenGL纹理覆盖材质颜色

OpenGL 四 - 002OpenGL 图形渲染之颜色混合

OpenGL透明纹理问题