ios - opengl es2 预乘与直接 alpha + 混合

标签 ios opengl-es alphablending

iPhone 上的 Opengl es2。一些对象由多层带有 alpha 的 sprite 组成。

然后我还有 UI 元素,这些元素也是由各种 sprite 合成在一起的,我在其他所有元素之上淡入/淡出。我通过调整着色器中的 alpha 来实现淡化。

我的纹理是 PNG,带有 alpha,是在 photoshop 中制作的。我不会故意预乘它们。我希望它们是直接的 alpha,但在我的应用程序中,它们就像预乘一样,我可以看到在白色背景上绘制的白色 Sprite 周围有黑色边缘。

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

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

元素很好地组合在一起,没有奇怪的边缘。但是当元素淡出时,它们会在最后弹出。它们将开始褪色,但不会一直下​​降到 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);
}

这让我可以在我的 sprite 表中获取一个白色对象,并将其设为我想要的任何颜色。或者使用灰色的 baseColor 使对象变暗。

每当我认为我理解了这些混合模式时,类似这样的事情就会出现,我开始怀疑自己。

是否有其他一些混合模式组合可以在我的 Sprite 上具有平滑的边缘并且还支持着色器中的 alpha 淡化/混合?

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

或者我的问题是我需要使用 PSD 以外的东西来保存我的 sprite 表?因为在这一点上这几乎是不可能的。

更新:

我认为答案可能是否定的,因为没有办法做我想做的事。第二种混合模式应该是正确的。但它可能在某处将 RGB 与 alpha 相乘,或者它在源文件中被预乘。我什至尝试通过添加以下内容在上面的着色器中自己预乘 alpha:

gl_FragColor.rgb *= glFragColor.a;

但这只会让淡入淡出看起来很糟糕,因为它们在淡出时会变成灰色。如果我自己预乘 alpha 并使用上面的其他混合模式,看起来和我原来的一样。它们淡出而不弹出,但您仍然可以看到光晕。

最佳答案

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

如果您正在使用 mip-maps,这可能就是为什么您的直 alpha 纹理有暗边的原因——过滤直 alpha 图像可能会导致这种情况发生,最好的解决方案是在 mip 之前预乘图像- map 被创建。在那篇文章中也描述了一个常见的 hack 修复,但请认真考虑预乘。

使用直接 alpha 来创建纹理通常是必要和首选的,但作为构建步骤的一部分或在纹理加载期间预乘它们仍然比将它们作为直接 alpha 保留在内存中更好。我不确定 OpenGL ES,但我知道 WebGL 允许您在加载期间使用 gl.pixelStorei 和 gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL 参数动态预乘纹理。

另一种可能性是,如果您要合成许多元素,那么您的直接 alpha 混合函数是不正确的。为了对直接的 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 channel ,您必须在混合直接时对颜色和 alpha 使用单独的混合函数alpha 图像源,如果您打算使用“过度运算符”合成多个图层。 (想想你可能不想插入 alpha channel ,它应该总是比源和目标更不透明。)混合时要特别小心,因为直接 alpha 混合的结果是预乘图像!所以如果后面要用到这个结果,还是要做好预乘混合的准备。为了获得更长的解释,我在这里写了这篇文章:https://limnu.com/webgl-blending-youre-probably-wrong/

使用预乘图像和混合的好处是您不必为颜色和 alpha 使用单独的混合函数,并且您会自动避免很多此类问题。您可以并且应该创建直接的 alpha 纹理,然后在加载之前或加载期间将它们预乘,并在整个代码中使用 premult 混合 (glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA))。

关于ios - opengl es2 预乘与直接 alpha + 混合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19674740/

相关文章:

ios - Facebook Audience Network 在 XCode 7 上有太多警告

iOS 8 在应用程序启动时检测设备方向

android - Android 2D 游戏使用 Canvas 还是 OpenGL ES2?

c++ - 即使在混合后也想要透明图像

SVG:防止相邻多边形之间的透明间隙

iphone - 存储应用程序参数,如 key

ios - 当应用程序未运行时,通过静默推送通知在后台更新本地数据库

android - OpenGL ES 2.0 : Unable to perform simple rotation

android - 不同混合模式的 OpenGL ES 性能

ios - 为什么 UIBezierPath strokeWithBlendMode 什么都不做?