opengl es2预乘与直接alpha +混合

时间:2013-10-30 05:46:01

标签: ios opengl-es alphablending

iphone上的Opengl es2。有些对象是由多层精灵和alphas组成的。

然后我也有UI元素,这些元素也是由各种精灵组合在一起的,我淡入/淡出其他所有东西。我通过调整着色器中的alpha来进行淡入淡出。

我的纹理是PNG,在photoshop中制作alpha。我没有故意为他们做好准备。我希望它们是直接的alpha,但在我的应用程序中,它们表现得好像它们被预先乘以我可以看到在白色背景上绘制的白色精灵周围的黑色边缘。

如果我将混合模式设置为:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

元素很好地融合在一起,没有奇怪的边缘。但是当元素逐渐消失时,它们最终会成为POP。它们将开始消退但不会一直下​​降到alpha零。所以在淡出动画结束时,当我删除元素时,它们会“弹出”,因为它们并没有完全消失。

如果我将混音模式切换为:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

这些元素很好地淡化和淡化。但是白色元素上的任何白色都有一个看起来像alpha预乘的暗边缘。请参阅白框顶部绘制的白色圆圈:

enter image description here

但是场景中的其他混合物对我来说很好看。其他透明物体很好地融合在一起。

另一个重要的注意事项是我的着色器处理元素和颜色元素的不透明度。对于绘制的每个东西,我乘以元素颜色,最后的alpha乘以不透明度值:

void main(void)
{
    gl_FragColor = texture2D(u_textureSampler,v_fragmentTexCoord0);
    gl_FragColor = vec4(gl_FragColor.r*u_baseColor.r,gl_FragColor.g*u_baseColor.g,gl_FragColor.b*u_baseColor.b,gl_FragColor.a*u_baseColor.a * u_opacity);
}

这允许我在我的精灵表中拍摄一个白色物体并使其成为我想要的任何颜色。或者使用灰色的baseColor使对象变暗。

每当我想到我理解这些混合模式时,就会出现这种情况,我开始怀疑自己。

是否有其他混合模式组合在我的精灵上有光滑的边缘,并且还支持着色器中的alpha淡化/混合?

我假设需要GL_SRC_ALPHA在着色器中使用alpha进行混合。

或者我的问题是我需要使用除PSD以外的其他东西来保存我的精灵表吗?这一点几乎是不可能的。

更新:

我认为答案可能只是否定,没有办法做我想做的事情。第二种混合模式应该是正确的。但它有可能是将RGB与某个地方的alpha相乘,或者它在源文件中被预乘。我甚至尝试通过添加:

在上面的着色器中自行预测alpha
gl_FragColor.rgb *= glFragColor.a;

但这会让渐变看起来很糟糕,因为当它们消失时,它们会变成灰色。如果我自己预先设定alpha并使用上面的其他混合模式,那么事情就像我原来的一样。它们在没有弹出的情况下消失,但你仍然可以看到光环。

2 个答案:

答案 0 :(得分:5)

这是一篇关于如何避免带有直纹alpha纹理的暗条纹的精彩文章http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/

如果您正在使用mip-maps,这可能就是您的直线Alpha纹理有深色条纹的原因 - 过滤直线Alpha图像可能会导致这种情况发生,最佳解决方案是在此之前预先乘以图像创建了mip-maps。这篇文章中描述了一个常见的黑客攻击,但认真考虑预先乘法。

使用直线α来创建纹理通常是必要且首选的,但是在构建步骤的一部分或纹理加载期间将它们预先乘以比将它们保持为直线α更好更好。记忆。我不确定OpenGL ES,但是我知道WebGL允许你在加载过程中通过使用带有gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL参数的gl.pixelStorei来实时预加倍纹理。

另一种可能性是,如果您要合成许多元素,则直接Alpha混合功能不正确。为了做一个正确的" over operator"使用直接的alpha图像,你需要使用这个混合功能,也许这就是你要找的东西:

gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

您引用的常见直线Alpha混合函数(gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA))无法正确处理目标Alpha通道,在混合直线时必须使用单独的颜色和alpha混合函数alpha图像源,如果您打算使用" over运算符"来合成多个图层。 (想想你可能不想插入alpha通道,它应该总是比source& dest更加不透明。)在混合时要特别小心,因为直alpha的结果混合是一个预乘的图像!因此,如果您稍后使用结果,您仍然必须准备好进行预乘混合。对于更长的解释,我在这里写了这个:https://limnu.com/webgl-blending-youre-probably-wrong/

使用预乘图像& amp;混合是你不必使用单独的混合功能来实现颜色和alpha,你自动避免了很多这些问题。你可以&应该创建直的alpha纹理,但是在加载之前或加载期间预加倍,并在整个代码中使用复合混合(glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA))。

答案 1 :(得分:3)

AFAIK,glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);用于预乘alpha,如果按原样使用颜色,它应该可以正常工作。 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);用于直接alpha。

许多纹理加载框架隐式地将图像转换为预乘的alpha格式。这是因为他们中的许多人正在将图像重新绘制成新图像,而CGBitmapContext不支持直线(非倍增)图像。因此,它们通常会生成预乘的α图像。因此,请查看纹理加载代码,并检查它是否已转换为预乘格式。

此外,Photoshop(当然是Adobe的)在导出到PNG时隐式擦除完全透明(alpha = 0)像素上的颜色信息。如果使用线性或其他纹理过滤,则GPU将对相邻像素进行采样,透明像素中的颜色将影响边缘处的像素。但Photoshop已经删除了颜色信息,因此它们会有一些随机颜色值。

理论上,可以通过在透明像素上保持正确的颜色值来修复这种颜色渗色。无论如何,使用Photoshop,我们没有实用的方法来导出PNG文件并保持正确的颜色值,因为Photoshop不尊重隐形的东西。 (需要编写一个专用的PNG导出器Photoshop插件来正确导出它们,我不能很好地支持它的现有的一个)

预乘alpha只是为了显示图像,但如果使用任何着色器颜色魔法它将无法正常工作,因为颜色以整数形式存储,因此它通常没有足够的精度来恢复原始颜色值。如果您需要精确的颜色魔术,请使用直接alpha - 避免使用Photoshop。


更新

这是我在iPhone模拟器(64位/ 7.x)上的@ SlippD.Thompson测试代码的测试结果

 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 24 bits/pixel; 3-component color space; kCGImageAlphaNone; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 0: (null)
    cgContext with CGImageAlphaInfo 1: <CGContext 0x1092301f0>
    cgContext with CGImageAlphaInfo 2: <CGContext 0x1092301f0>
 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaLast; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 3: (null)
 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaFirst; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 4: (null)
    cgContext with CGImageAlphaInfo 5: <CGContext 0x1092301f0>
    cgContext with CGImageAlphaInfo 6: <CGContext 0x1092301f0>
 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 24 bits/pixel; 0-component color space; kCGImageAlphaOnly; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 7: (null)