OpenGL Alpha混合问题,混合被忽略了吗(也许)?

6

编辑+更好的解决方案:

如果有人碰到我遇到的问题,有两个解决方案。一个是被接受的解决方案,但只适用于你像我一样做事情的时候。让我解释一下我在做什么:

1.)将星星背景渲染到屏幕上

2.)先将飞船,然后是粒子渲染到FBO上

3.)将FBO渲染到屏幕上

这个问题和因此解决这个问题的解决方案之所以出现,是因为我正在将FBO与星星背景混合。

真正的解决方案据说也稍微快一些,就是简单地将星星背景渲染到FBO上,然后禁用混合将FBO渲染到屏幕上。使用这种方法,我不需要去处理glBlendFuncSeparate...

1.)将星星,然后飞船,然后粒子渲染到FBO上

2.)将禁用混合的FBO渲染到屏幕上

----------原始问题:----------

从我对该问题的理解来看,混合被某种方式忽略了。具有alpha透明度的粒子纹理完全覆盖了下面的像素。

我正在创建一个俯视角度的游戏。相机略微倾斜,以便有一些深度感。我先渲染我的飞船,然后再上面渲染粒子...

开始OpenGL上下文之后

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_BACK);

在渲染循环中,我这样做:
glBindFramebuffer(GL_FRAMEBUFFER,ook->fbo);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);

entitymgr_render(ook); //Render entities. Always 1.0 alpha textures.

glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);

//glBlendFunc(GL_SRC_ALPHA,GL_ONE); //This would enable additive blending for non-premult
particlemgr_render(ook); //Render particles. Likely always <1.0 alpha textures
//glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

glBindFramebuffer(GL_FRAMEBUFFER,0);

如果我使用上面的代码运行,会得到如下结果...
粒子纹理: Particle tex OGL Profiler(Mac 工具)截图: Screenshot from OGL Profiler 没有任何粒子渲染的 FBO 截图: Screenshot of the FBO without any particle rendered 在顶部渲染了一些粒子的 FBO 截图: Screenshot of the FBO with some particles rendered on top 如您所见,尽管粒子具有 alpha 透明度,但它不会与下方渲染的船体混合。相反,它只是完全覆盖像素。
最后注意,设置着色器中的像素透明度可以正确混合——船体出现在下方。这是我的着色器:
#version 150
uniform sampler2D s_tex1;
uniform float v4_color;
in vec4 vertex;
in vec3 normal;
in vec2 texcoord;

out vec4 frag_color;

void main()
{
    frag_color=texture(s_tex1,texcoord)*v4_color;
    if(frag_color.a==0.0) discard;
}

如果有什么我可以提供的,请告诉我。

3个回答

8

我觉得它将alpha通道呈现到帧缓冲区中,因此当你写入粒子时,源alpha通道会与目标alpha通道混合在一起,这不是你想要的。

这就是为什么glblendfuncseparate()函数被创建的原因。尝试使用这个函数...

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

因此,您的粒子的阿尔法通道将用于确定最终像素的颜色,但源和目标的阿尔法通道将被加在一起。

5
我的猜测是FBO的rgb通道被正确渲染,但由于它还具有alpha通道,并且启用了混合绘制,因此当粒子重叠飞船时,最终结果具有不正确的透明度。
要么使用glBlendFuncSeparate (在这里描述)为绘制粒子时的alpha通道使用不同的混合因子:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

或者在将您的FBO绘制到屏幕上时完全关闭混合。


2
我接受了此回答之后发布的其他回答,因为他/她更好地描述了问题和glBlendFuncSeparate在这里所做的事情。这仍然是正确的答案。 - flagoworld

0
为了获得纹理透明度,除了以下方法之外:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

你还应该确保:

  1. 使用glTexImage2D创建粒子纹理时,将format设置为GL_RGBA(如果使用灰度纹理,则设置为GL_LUMINANCE_ALPHA)

  2. 在绑定纹理后,调用以下命令绘制粒子纹理:

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);

你也可以根据glTexEnv参考文档中的描述,使用正确的纹理函数代替GL_BLEND:http://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml


错误。我明确地给我的问题打了 opengl-3 的标签。这个函数是针对小于 3 版本的。 - flagoworld
我正在大量使用OpenGL SC,因此我很了解这个版本。我的错是没有注意到opengl-3标签。 - Zac

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