大家好,我是程序员kenney,今天我们来讨论一下alpha
预乘问题,主要是介绍alpha
预乘的概念以及有alpha
预乘和无alpha
预乘时在渲染颜色混合阶段时的处理方式。
所谓alpha
预乘就是将图片的alpha
值,在使用这个图片前,预先乘到RGB
值上,假设图片中有这样一个像素点RGBA=(0.9, 0.8, 0.6, 0.5)
,如果进行了alpha
预乘,那么预乘后的像素颜色为RGBA=(0.45, 0.4, 0.3, 0.5)
。
alpha
预乘的概念还是比较好理解的,那么它在实际使用的时候,有什么影响呢?
我的另一篇文章《OpenGL ES 高级进阶:颜色混合》给大家介绍过颜色混合,alpha
预乘和颜色混合有关系,如果没有正确地处理alpha
预乘,会造成颜色混合的结果不符合预期,我们来看一些例子,假设原始颜色是RGBA(0, 1, 0, 0.5)
:
1. 非预乘,源混合因子GL_ONE,目标混合因子GL_ONE_MINUS_SRC_ALPHA
此时混合后的G
道通值已经超过了1,也就是说这种情况下混合的结果颜色值是有可能会超出1的,一般来说这不是我们希望的结果。颜色值越界实际上已经无法表示了,上面只是放了一个示意图,实际会出来什么效果要看具体的越界处理实现。
2. 预乘,源混合因子GL_ONE,目标混合因子GL_ONE_MINUS_SRC_ALPHA
混合结果是一个偏白的绿色,比较符合半透明绿色叠加到不透明白色上的视觉感受。
3. 非预乘,源混合因子GL_SRC_ALPHA,目标混合因子GL_ONE_MINUS_SRC_ALPHA
此时的混合结果中,得到了一个半透明绿色,前一种情况得到的结果还比较像,只不过带了一些透明度。
4. 预乘,源混合因子GL_SRC_ALPHA,目标混合因子GL_ONE_MINUS_SRC_ALPHA
此时的混合结果中,得到了一个比较暗淡的半透明绿色。
从上面的各种组合中可以看到,不同的混合模式配上预乘或者非预乘的纹理,会得到不一样的混合结果,要根据具体的需求来评估哪种是符合要求的,不能简单地认为哪种是对的哪种是错的。
我个人认为第2种混合方式在多数情况下是比较理想的,有以下几点原因:
1)混合出来的颜色从视觉上看比较符合实际。
2)当把一个半透明的纹理混合到一个不透明的纹理上时,混合结果也一定是不透明的,这一般来说也比较符合实际的半透明叠加需求。
3)混合结果的颜色值不会越界(小于0或者大于1)。
那么如果给的纹理是非预乘的纹理,如何能得到第2种的混合效果呢?有两种方法:
第一种比较简单,就是自己预乘一下,让它变成预乘的情况,可以在fragment shader
里处理完时,把RGA
乘一下A
就可以了,因为fragment shader
是在OpenGL
颜色混合之前执行完的,此时颜色混合阶段拿到的颜色值就是已经预乘了的。
第二种方法稍微复杂些,我们仔细看一下,在预乘情况下源混合因子用GL_ONE
,那么源颜色乘以源因子的结果就是RGB
乘上了A
,A
保持不变,这和使用GL_SRC_ALPHA
的结果很像,RGB
都乘上了A
,只不过GL_SRC_ALPHA
会把A
也乘上了A
,那有什么办法让A
保持不变?那就是把A
通道和RGB
通道分开混合,分开混合的方法在《OpenGL ES 高级进阶:颜色混合》这篇文章中也有提到,所以最终的方法为源RGB
混合因子用GL_SRC_ALPHA
,源A
混合因子用GL_ONE
,目标RGBA
混合因子都用GL_ONE_MINUS_SRC_ALPHA
:
上面说了预乘和非预乘时的各种颜色混合方式的影响,不同的颜色混合方式的性能是不一样的,我们以上面的第2种混合效果为例,要达到第2种混合方式的效果,如果给的纹理是已经预乘过的,那么此时就可以将源混合因子设为GL_ONE
,此时源颜色就是乘1,和没乘是一样的,可以把计算量省略,而如果是非预乘纹理,无论是用上面说的第一种还是第二种方式,都是有计算量的。
好了,这篇文章就先说到这,感谢阅读!