三原色光模式又称RGB颜色模型,是一种加色模型,将RGB三原色的色光以不同的比例相加,以产生多种多样的色光。而计算机中更为常见的还有RGBA颜色模型。A 一般称为alpha通道,一些人也称为透明度、不透明度等。
在电脑中,假设有两个跟窗口尺寸一样的单色图片:
图片A:红色,RGB(255,0,0)
图片B:绿色,RGB(0,255,0)
1.先不考虑透明度
初始窗口是空白(可以认为是纯黑色,RGB(0,0,0)),先绘制图片A,此时窗口中的颜色就是红色,RGB(255,0,0);接着图片B写入窗口,此时窗口中颜色直接被覆盖成了RGB(0,255,0) ;如果记窗口的颜色为D,那这个流程图就是
D = RGB(0,0,0)
绘制图片A: D = RGB(255,0,0)
绘制图片B: D = RGB(0,255,0)
记当前绘制的颜色为S ,可以简单的归纳绘制颜色的公式为:
D = S
2.引入透明度
先取两个极限:全透、全不透。以图片A举例,全不透就是直接覆盖,完整的显示A的颜色(红色);全透就是显示之前的颜色,完全丢弃A的颜色,即显示黑色。
这里可以认为透明度的两个极限值就是0,1;看上去透明度,可以作为RGB值的系数,一般来说使用不透明度
(opacity)更符合习惯,不透明度 = 1 - 透明度
,即最终颜色是 opacity * RGB:
opacity = 0 显示颜色 RGB(255x0,0x0,0x0) = RGB(0,0,0) 显示纯黑色
opacity = 1 显示颜色 RGB(255x1,0x1,0x1) = RGB(255,0,0) 显示纯红色
opacity = 0.5 显示颜色 RGB(255x0.5,0x0.5,0x0.5) = RGB(128,0,0) 半红色?
0-1渐变过程大概是这个样子:
看上去已经达到了我们想要的效果,此时绘制颜色的公式为:
D = S x opacityS。
这个公式在窗口本身没有颜色时没有问题,但我们引入透明度就是为了能在绘制时保留窗口之前的颜色,当opacity不为1时,我们希望能看到一定程度窗口原始的颜色,最典型的计算公式就是:
D = D x (1 - opacityS) + S x opacityS
这个公式也是Opengl 中最常用的颜色混合模式, 随着颜色透明度的变化,可以混合出比较理想的颜色显示。
如果图片的每个像素都自由设定透明度,就是现代流行的RGBA颜色表示法 ,其中A代表alpha,也就是不透明度。
3.关于预乘alpha
RGBA 表示法中, 半透红色一般表示为 (255,0,0,0.5)。在上边的颜色计算中我们都会将alpha 乘以各个R、G、B分量,如果我们保存的时候就将值提前乘以alpha ,这样就可以直接进行相加了,这种情况下半透明红色就记为 (255 x 0.5,0 x 0.5,0 x 0.5,0.5) = (128,0,0,0.5),这就是 预乘alpha。
预乘alpha,能够有限的提升性能。一个更重要的原因是(后续内容来自
https://www.cnblogs.com/xiaonanxia/p/9448444.html ):
没有 Premultiplied Alpha 的纹理无法进行 Texture Filtering(除非使用最近邻插值)。
以最常见的 filtering 方式线性插值为例,一个宽2px高1px的图片,左边的像素是红色,右边是绿色10%透明度:
,如果把这个图片缩放到1x1的大小,那么缩放后1像素的颜色就是左右两个像素线性插值的结果,也就是把两个像素各个通道加起来除以2。如果使用没有 Premultiplied Alpha 的颜色进行插值,那么结果就是:
( (255,0,0,1) + (0,255,0,0.1) ) x 0.5 = (127,127,0,0.55)
如果绿色 Premultiplied Alpha,也就是 (0, 255 * 0.1, 0, 0.1),和红色混合后:
( (255,0,0,1) + (0,25,0,0.1) ) x 0.5 = (127,25,0,0.55)
对比上边的两个混合后的的颜色,显然后一个颜色更符合直觉,前边那个颜色太绿了,因为绿色通道没有乘以透明度,所以在线性插值的时候占了过大的权重。
所以 Premultiplied Alpha 最重要的意义是使得带透明度图片纹理可以正常的进行线性插值。这样旋转、缩放或者非整数的纹理坐标才能正常显示,否则就会像上面的例子一样,在透明像素边缘附近产生奇怪的颜色。