本文介绍透明度叠加算法(Alpha Blending Algorithm),并用 C#/WPF 的代码,以及像素着色器的代码 HLSL 来实现它。
算法
对于算法,我只是搬运工,可以随意搜索到。算法详情请查看:Alpha compositing - Wikipedia。
对于完全不透明的背景和带有透明度的前景,合并算法为:
1
float r = (foreground.r * alpha) + (background.r * (1.0 - alpha));
这是红色。然后绿色 g
和蓝色 b
通道进行一样的计算。最终合成图像的透明通道始终设置为 1。
在 C# 代码中实现
多数 UI 框架对于颜色值的处理都是用一个 byte
赛表单个通道的一个像素。于是计算会采用 0xff 即 255。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (int i = 0; i + 4 < length; i = i + 4)
{
var backB = background[i];
var backG = background[i + 1];
var backR = background[i + 2];
var foreB = foreground[i];
var foreG = foreground[i + 1];
var foreR = foreground[i + 2];
double alpha = foreground[i + 3];
blue = 0;
output[i] = (foreB * alpha) + (backB * (1.0 - alpha));
output[i + 1] = (foreG * alpha) + (backG * (1.0 - alpha));
output[i + 2] = (foreR * alpha) + (backR * (1.0 - alpha));
output[i + 3] = 1.0;
}
这段代码当然是跑不起来的,因为是下面两篇博客的魔改代码。你需要阅读以下两篇博客了解如何在 WPF 中按像素修改图像,然后应用上面的透明度叠加代码。
话说,一般 UI 框架都自带有透明度叠加,为什么还要自己写一份呢?
当然是因为某些场景下我们无法使用到 UI 框架的透明度叠加特性的时候。例如使用 HLSL 编写像素着色器的一个实现。
下面使用像素着色器的实现是我曾经写过的一个特效的一个小部分,我把透明度叠加的部分单独摘取出来。
在像素着色器中实现
以下是 HLSL 代码的实现。Background 是从采样寄存器 0 取到的颜色采样,Foreground 是从采样寄存器 1 取到的颜色采样。
这里的计算中,背景是不带透明度的,而前景是带有透明度的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <description>透明度叠加效果。</description>
sampler2D Background : register(s0);
sampler2D Foreground : register(s1);
float4 main(float2 uv : TEXCOORD) : COlOR
{
float4 background = tex2D(Background, uv);
float4 foreground = tex2D(Foreground, uv);
float alpha = foreground.a;
float r = (foreground.r * alpha) + (background.r * (1.0 - alpha));
float g = (foreground.g * alpha) + (background.g * (1.0 - alpha));
float b = (foreground.b * alpha) + (background.b * (1.0 - alpha));
float a = 1.0;
return float4(r, g, b, a);
}
如果要测试的图片都是不带透明度的,那么可以通过自己设一个透明度来模拟,传入透明度值 Alpha。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// <description>透明度叠加效果。</description>
/// <type>Double</type>
/// <summary>采样 2 的叠加透明度。</summary>
/// <minValue>0.0</minValue>
/// <maxValue>1.0</maxValue>
/// <defaultValue>0.75</defaultValue>
float Alpha : register(C0);
sampler2D Background : register(s0);
sampler2D Foreground : register(s1);
float4 main(float2 uv : TEXCOORD) : COlOR
{
float4 background = tex2D(Background, uv);
float4 foreground = tex2D(Foreground, uv);
float alpha = Alpha;
float r = (foreground.r * alpha) + (background.r * (1.0 - alpha));
float g = (foreground.g * alpha) + (background.g * (1.0 - alpha));
float b = (foreground.b * alpha) + (background.b * (1.0 - alpha));
float a = 1.0;
return float4(r, g, b, a);
}
参考资料
- Alpha compositing - Wikipedia
- algorithm - Manually alpha blending an RGBA pixel with an RGB pixel - Stack Overflow
本文会经常更新,请阅读原文: https://blog.walterlv.com/post/alpha-blending-algorithm.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected]) 。