3

我正在使用 OpenGL 和 C++ 编写一个简单的 2D 框架,现在遇到了与透明纹理和混合相关的问题。我已将我的问题减少到以下问题。

我有两种纹理:地砖和鱼骨。后者包含透明像素。我将我的 OpenGL 清除颜色设置为“透明绿色”并启用深度测试和混合,如下所示:

glClearColor(0, 1, 0, 0)
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

通过两次调用,glDrawElements()我画了鱼,然后画了地板。鱼具有较高的 Z 值,因此应放置在地砖前面。这就是结果

显然,我不希望像这样围绕鱼的绿色框。我认为发生的情况是鱼像素在绘制时与帧缓冲区的颜色渲染缓冲区中的任何内容混合,并且恰好是纯绿色(由于glClearColorglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))。对于鱼纹理中的每个透明像素,我希望这种混合能够解析为透明(因为我设置了透明的清晰颜色),但正如您所见,这不是正在发生的事情。

如果我先画地板,然后画鱼,它会按预期工作。

我的片段着色器非常简单:

varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;

void main(void) {
    gl_FragColor = texture2D(Texture, TexCoordOut);
}

我真的必须手动管理绘图顺序(按 Z 坐标排序)还是有办法解决这个问题?OpenGL深度缓冲系统不是设计来解决这个问题的吗?

我正在通过 Xcode 在 iOS 模拟器上测试我的程序。

4

3 回答 3

3

我确实相信深度排序是必要的——即使混合产生的像素是透明的,深度缓冲区也会被写入。因此,任何要在这些透明像素后面(更高深度)绘制的东西都会被丢弃。不幸的是,深度缓冲区没有考虑透明度。

如果您的纹理中没有任何半透明像素,您可以使用 alpha 测试,如果它是透明的,它会丢弃一个像素,因此它不会写入深度缓冲区。这显然不适用于半透明像素,因为它们要么变得完全不透明,要么被丢弃,具体取决于您的实现/设置。您可以通过将着色器更改为:

varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;

void main(void) {
   vec4 tc = texture2D(Texture, TexCoordOut);
   if (tc.a < 0.5) //for example, change to any value suitable
     discard;
   gl_FragColor = tc;
}

除此之外,除了排序之外,我不知道用深度缓冲来修复透明度。

您可以在此处阅读有关透明度排序和 alpha 测试的更多详细信息:http ://www.opengl.org/wiki/Transparency_Sorting

于 2013-06-30T12:55:32.323 回答
2

遇到了这个确切的问题,并尝试了各种方法来纠正它,但没有可靠的结果,试图处理大多数以改变计算为中心的建议。最后,它相当简单!

我的代码中的罪魁祸首是这样的:

glClearColor(1, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);

尽管它在背景和主体颜色之间混合时是透明的,但它会流血。为我解决的问题是包装此代码以掩盖 glClear 步骤。它看起来像这样:

glColorMask(false, false, false, true);
glClearColor(1, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(true, true, true, true);

这似乎通过从清除过程中消除除 alpha 通道之外的所有通道来消除出血问题。

于 2015-12-22T19:29:00.423 回答
0

Z 值用于剔除。如果已经有另一个具有较小 z 值的片段或者它在视锥体之外,则不会绘制片段。但是所有顶点缓冲区对象都是按顺序绘制的。对于 2D,您可以使用任何排序“按 z”算法。如果您想稍后添加一些过滤器,如模糊或边缘抗锯齿,丢弃片段将是有问题的。

于 2013-07-01T15:03:40.897 回答