我正在用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值较高,因此应放在地砖前面。 This is the result
显然,我不希望那条鱼周围的绿色盒子。我认为发生的事情是鱼像素在绘图时与帧缓冲区的颜色渲染缓冲区中的任何东西混合,并且恰好是纯绿色(由于glClearColor
和glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
) 。对于鱼纹理中的每个透明像素,我都希望这种混合能够解析为透明(因为我设置了透明的清晰颜色),但是你可以看到它并没有发生什么。
如果我首先绘制地板,然后是鱼,它会起作用as expected。
我的片段着色器很简单:
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void) {
gl_FragColor = texture2D(Texture, TexCoordOut);
}
我是否真的需要手动管理绘图顺序(按Z坐标排序)还是有办法解决这个问题?是不是旨在解决这个问题的OpenGL深度缓冲系统?
我通过Xcode在iOS模拟器上测试我的程序。
答案 0 :(得分: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
答案 1 :(得分:1)
有这个确切的问题并尝试各种方法来纠正它没有可靠的结果试图与大多数建议集中在改变正在进行的计算。最后它很简单!
我的代码中的罪魁祸首看起来像这样:
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通道之外的所有通道来否定出血问题。
答案 2 :(得分:0)
Z值用于剔除。如果已经有另一个具有较小z值的片段或者它位于视锥体之外,则不会绘制片段。但是按顺序绘制所有Vertex缓冲区对象。对于2D,您可以使用任何排序&#34; by z&#34;算法。如果您希望稍后添加一些滤镜(如模糊或边缘消除锯齿),则丢弃片段会有问题。