NGUI字体贴图压缩以及相关Shader解读

来源:互联网 发布:网络舆情监控资质 编辑:程序博客网 时间:2024/06/05 04:09

一般游戏中,字体贴图是游戏贴图压缩的一个重点,特别是对于中文的游戏。考虑的字体贴图的特殊性,一般我们输出的字体贴图是不含颜色信息的,所以正常情况下,我们输出的字体贴图RGBA每个通道都是一样的。这样一来,就存在很大的浪费。所以我们可以在RGBA四个通道中可以保存不同的字体,这样,我们的贴图的大小可以减少4倍。如果我们想使用ETC压缩的话,我们可还以直接不使用Alpha通道,只在RGB三个通道中存储不同的文字(这样,如果我们要进行ETC压缩的话,不需要对Alpha通道做分离处理了,但是得到的ETC会比正常的图片压缩成ETC后的要模糊,这跟ETC的压缩机制(采样)有关,这跟AAA比ABB或者AWW压缩成ETC之后清晰是同样的道理,有兴趣也可以研究下)。因为不同的通道中包含了不同的文字,所以我们需要使用NGUI一个特殊的Shader,Unlit/Transparent Colored (Packed)。要理解这个shader,我们首先来看一下一般我们使用多通道字体贴图的时候,fnt文件中包含的信息

在这里这个chnl就是表明当前这个文字是存储在哪一个通道,R、G、B、A分别对应1,2,4,8. 然后我们来看一下Shader的核心代码

half4 frag (v2f IN) : COLOR{half4 mask = tex2D(_MainTex, IN.texcoord);half4 mixed = saturate(ceil(IN.color - 0.5));half4 col = saturate((mixed * 0.51 - IN.color) / -0.49);mask *= mixed;col.a *= mask.r + mask.g + mask.b + mask.a;return col;}

乍一看,感觉很奇怪的样子,特别是这里有两个比较特别的数字5.1和0.49.其实这里跟NGUI的处理机制有关系,NGUI主要通过变换传入的顶点颜色(IN.color)来进行处理。那么我们IN.color的取值是怎么设定的呢?这里我们简单来推导一下这里的公式,首先摆在我们眼前的问题是,我们在shader中要知道当前的文字是要读取RGB的哪一个通道,那我们如何得知呢?一个简单的办法,我们可以变换IN.color的RGB,使得,当前通道的色值>0.5,而其他的通道小于0.5.那么我们在shader中就可以简单的通过

half4 mixed = saturate(ceil(IN.color - 0.5));//如果值在0-0.5区间,那么结果为0,如果值为0.5-1,值为1

来得到当前文字所在的通道了(为1的就是当前的通道)。那么我们怎么使得其他的通道的颜色值<0.5呢?我们知道RGB默认的取值范围为0-1,那么我们可以简单的乘以0.49,这样范围就在0-0.49之间了(即满足<0.5),我们假设NGUI设置的UIlabel的颜色为X,那么通过上面的处理,IN.color = X * 0.49。现在,所有通道的颜色范围都在0-0.49之间,而我们需要我们当前通道的颜色>0.5,所以我们可以简单的对当前通道的色值加上0.51,那么当前通道的颜色值取值范围就到了0.51-1,从而满足了条件。这时候IN.color = X *0.49 + M *0.51(其中M中当前通道的值为1,其他通道的值为0,如当前如R通道的话,M=(1,0,0),其实这里M取值就相当于上线的mixed)。这个时候,我们只要根据文字设置的颜色所在的通道设置In.color的值就可以了。那么,反过来,我们在Shader中如何还原字体的颜色值X呢?根据IN.color = X *0.49 + M *0.51,我们可以很简单的得到X = (M*0.51-In.color)/0.49.相信到这里,大家都应该明白了,这就是shader中代码

half4 col = saturate((mixed * 0.51 - IN.color) / -0.49);
的由来。

文字的颜色我们设置OK了,那么剩下的只需要得到文字的轮廓就好了。也就是设置对应的col的alpha值。比如说如果当前通道为R,那么我的col.a 设置成R通道的值就可以了,这样R通道中有文字的地方(a>0)就会显示出来,其他地方就不会显示(a=0)。也就是说我们得到的是R通道中文字的轮廓,代码如下

<pre name="code" class="csharp">half4 mask = tex2D(_MainTex, IN.texcoord);
mask *= mixed;            //处理后,mask中只保留了当前通道的颜色值,其他通道为0,如红色通道(R,0,0)col.a *= mask.r + mask.g + mask.b + mask.a; //col.a = 当前通道的颜色值


注:1、关于如何得到多通道贴图(这里我们使用BFont)


2、得到的贴图示例


3、相对于直接使用Alpha8的优点

a、如果使用Alpha8贴图的话,如果文字无法恰好填满一个标准大小时,会存在浪费(主要内存占用大小)。如一张1024*1024的贴图

如果放RGB三个通道就会好很多。

b、相对于Unity中使用Alpha,多通道贴图还可以进一步压缩,比如说压缩成RGB565(16Bit)。而Alpha8贴图是无法这样处理的。

0 0
原创粉丝点击