在玩很多flash网页游戏的时候,看到它们都有非常清晰的宋体字,并且有漂亮的描边效果。如图,这是战将传奇的登录界面中的文字。
对比之下,silverlight要弄文字可让人头痛无比,别的不说,默认的字体怎么看怎么歪瓜裂枣。我就不贴图了,大家都明白。
那么怎么办捏,我们要祭出法宝:像素着色器!
- 需要的工具:Shazzam Shader Editor 。 这个是调试Silverlight着色器的神器。只要下载安装了它,其他的一切都好办了。
- 简单介绍一下像素着色器的工作原理。 对某个UIElement应用一个Effect,可以是自定义的。UIElement最终会呈现为一个位图。这个位图会被当成参数传入我们编写的着色器。着色器程序入口有一个参数,是当前的像素位置,另外还有一个注册的传入的位图。程序要求返回一个颜色,就是指定当前像素的颜色。 程序执行一次,只能对传入的当前像素进行着色,但是,有几个像素着色器程序就会执行几次,并且各个像素之间不冲突。所以,GPU硬件往往会并行计算。 因此,如果我们写一个最最简单的着色器,就是不管3721,一律返回红色,那么这个UIElemnet就会被画成一个红色方块。
- 首先我们打开Shazzam Shader Editor.然后新建一个Shader。这个时候就已经给建立了一个默认的将原输入图像原封不动地输出的Shader。事实上我们只要将return 改为return float4(1,0,0,1),就会输出一个红色矩形。
解释一下主要语句的功能:
- sampler2D input : register(s0); 注册输入的位图参数。 float a:register(c1); 注册一个输入的浮点型参数。 float4 c:register(c2); 注册一个输入的颜色。 一个float4类型里面包括了4个浮点数。 可以用rgba来访问,比如说, c.r ,c.g 等。 其中颜色的各分量都是 0-1之间而不是 0-255,这个要注意。 以上这三种类型可以在silverlight里当参数传进来。 其中smpler2D就是一个bursh, float就是一个浮点数, float4就是一个Color。 float2 包含两个浮点数的类型, 一般用作坐标。在main函数有一个传入的参数,类型就是float2 . 有x,y两个成员用以访问2个浮点数。传入的坐标的x,y分量都是在0-1之间,也就是说,把位图的宽和高都映射到0-1之间了。 如果我们知道位图的实际宽度的话,用 x,y乘以 宽或高,才能得到实际的像素位置。 tex2D函数: 接收一个 sampler2D 和一个 float2 的坐标 。 返回sampler2D这个位图的制定位置的颜色。
- 知道了以上这些语句,我们就可以动手写着色器了。 我们的目的,就是要进行文字描边。假定我们只会对TextBlock应用这个着色器,那么有如下事实:TextBlock是一个矩形。文字所在像素的 alpha分量必定大于0,否则必定是透明像素。 我们要做这样一件事,就是如果当前像素的上,下,左,右任意一个像素不透明,那么说明本像素需要被描边,否则就输出文字颜色。 由于需要知道相邻像素,所以还需要传入TextBlock的ActualWidth和ActualHeight。 这样, 当前位置的 x 1/width 就是相邻像素的坐标,我们可以用tex2D函数来提取它的颜色值。 还需要输入描边的颜色,还有文字的颜色。 因此,废话不多说了,直接上着色器代码了:
1 sampler2D input : register(s0); //输入的textblock
2
3 float w:register(C0); //宽度
4
5 float h:register(C1); //高度
6
7 float4 fontcolor:register(C2); //字体颜色
8
9 float4 bordercolor:register(C3); //描边颜色
10
11 float4 main(float2 uv : TEXCOORD) : COLOR
12 {
13 //float3 rgb= bordercolor.rgb ;
14
15 float4 Color;
16 Color= tex2D( input , uv.xy); //提取当前像素颜色
17
18 int i;
19
20
21 for( i=1;i<2;i ) //修改此循环,可以改动描边的宽度,不过,这里就1次就够了
22 {
23 if( Color.a==0 )
24 {
25 float4 c2;
26 c2= tex2D( input, uv.xy float2 (0,i/h) ); //提取下方像素
27
28 if( c2.a>0 )
29 {
30 Color=bordercolor; //描边
31 }
32 else
33 {
34 c2= tex2D( input, uv.xy float2 (0,-i/h) ); //提取上方像素 。。。如此这般,共提取4次。
35 if( c2.a>0 )
36 {
37 Color=bordercolor;
38 }
39 else
40 {
41 c2= tex2D( input, uv.xy float2 (i/w,0) );
42 if( c2.a>0 )
43 {
44 Color=bordercolor;
45 }
46 else
47 {
48 c2= tex2D( input, uv.xy float2 (-i/w,0) );
49 if( c2.a>0 )
50 {
51 Color=bordercolor;
52 }
53 }
54 }
55 }
56 }
57 else
58 {
59 float4 tempcolor = fontcolor;
60
61
62 if( Color.a<0.1) //由于SL对字体的反锯齿处理,这里有一个非常讨厌的现象:有半透明像素存在,经过测试,如果透明度在0.1一下,则判断为边,否则为字。
63 {
64 tempcolor=bordercolor;
65 }
66
67 Color=tempcolor;
68 }
69 }
70
71
72
73 return Color;
74 }
75
然后,按照深蓝的教程,将着色器代码转为Effect,即可使用。效果如下,哈哈,和flash是一模一样的吧
代码下载