美文网首页
【Siggraph 2007】Improved Alpha-Te

【Siggraph 2007】Improved Alpha-Te

作者: 离原春草 | 来源:发表于2022-01-16 11:30 被阅读0次

    本文是Valve公司在Siggraph 2007上给出的高清低消耗字体渲染方案。

    上面三图给出的是在同样字体贴图分辨率(64x64)下,不同的技术方案所能达到的效果,可以很明显的看到使用Signed Distance Field方案可以得到更为清晰的字体,下面我们一起来看下这个方案的具体实现以及这个方案的优缺点。

    1. 实施原理

    在深入技术细节之前,先对整个方案做一个概述:对于字体中需要绘制的每个符号(glyph,象形文字),会从一张高分辨率的贴图出发,创建一个Signed Distance Field,之后存入到一张低分辨率的贴图中,这张低分辨率贴图可以直接用在固定管线中,通过alpha blend/test的方式来绘制字体,效果如前面给出的贴图所展示的一般,这使得这种方案可以兼顾低端机型的需要,同时,在一些支持可编程shader的高端机型中,还可以通过算法对贴图进行更为复杂的计算以获得更好的效果,包括soft edge,描边,投影以及锐利的边缘等。

    使用SDF完成字体渲染需要重点关注两个方面,分别是Signed Distance Field的生成以及渲染

    1.1 Distance Field生成

    SDF可以从矢量图生成,也可以从高分辨率的字体贴图中生成,从结果来看,两种生成方式并没有太大区别,而且贴图创建SDF更为简单,因此这里采用的是后一种方案。

    以输入贴图分辨率4096x4096(SrcTex),输出的SDF贴图分辨率64x64为例,从高分辨率贴图生成SDF最简单粗暴的方案就是对SDF贴图中的每个texel而言,找到SrcTex上对应的src texel,以src texel为圆心不断拓展半径进行搜索,直到检测到有数值(字体有渲染的像素数据)与无数值的边界像素位置,取半径作为SDF的数值,不过因为这里是使用8bits进行数据存储的,这里需要转换一下,将数值转换为0~1,其中0表示的是字体内部最大距离边界,1表示的字体外部的最大距离边界,而0.5表示字体边缘(这里不太确定,如果需要0.5为边界,那么0跟1就只能对应于相同绝对值的正负值)。

    在具体实现的时候可以参考Anti-aliased Euclidean distance transform

    上面给的是一个粗暴的解法,而SDF的生成与计算有一些更为复杂精细高效的方案,不过由于本身字体SDF分辨率较小,且这个过程是离线完成,因此使用更为精细算法也没有太大收益,因此这里就不再展开优化。

    1.2 Signed Distance Field的渲染

    1.2.1 Alpha Test

    Signed Distance Field贴图的一个优点是,可以兼容低端硬件,当硬件性能有限的时候,可以直接将SDF贴图当成普通的贴图通过alpha test(使用0.5作为alpha threshold)完成绘制,这里需要注意的是,前面说过0表示的是内部,1表示的是外部(另外,如参考文献[2]中所说,由于外部区域更大更广,因此内外区域使用同样的范围是不合适的,可以根据需要缩小内部区域的表示范围并增加外部区域的表示范围),如果使用alpha test就需要做翻转处理(或者存储的时候就做了翻转处理),上图给出的是使用alpha test绘制得到的结果,对SDF使用alpha test绘制的好处是当所需绘制的图案在放大的时候不会出现明显的失真或者锯齿(普通贴图通过alpha test绘制在放大时会有锯齿吗?)。

    1.2.2 Alpha Blending

    如果不满意Alpha Test的边缘硬切效果,也可以通过在PS中添加一些计算逻辑加Alpha Blend得到柔和边缘过渡,如上图所示。

    具体而言,这里会通过smoothstep函数实现在Dist_{min}Dist_{max}中间的过渡,而smoothstep的过渡范围(即软化半径)则可以通过屏幕空间的梯度derivative(ddx/ddy)来求得,梯度大的像素,其过渡范围就小,反之亦然。另外,需要注意的是,当我们渲染的图案相对于SDF贴图尺寸更小(缩小)的话,那么这时候需要相应增加软化半径以减少锯齿感。

    这里另外提一句,通常在渲染树叶等alpha test物件时,alpha的threshold会随着物件到相机的距离的增加而增加,从而使得渲染的树叶逐渐消失,以避免缩小采样时的popup瑕疵。

    1.2.3其他效果

    SDF表示的贴图除了上述两种传统的用法之外,还可以用来实现一些特殊的效果,比如描边,外发光以及阴影效果等,不过这些效果都需要在PS中添加一些额外的计算,其效果给出如下,实现过程较为简单,后面有源码示意,这里就不做介绍了。

    float distAlphaMask = baseColor.a ; 
    if ( OUTLINE && (distAlphaMask >= OUTLINE MIN VALUE0) && (distAlphaMask <= OUTLINE MAX VALUE1) )
    {
     float oFactor =1.0;
     if ( distAlphaMask <= OUTLINE MIN VALUE1 )
     {
     oFactor=smoothstep ( OUTLINE MIN VALUE0, OUTLINE MIN VALUE1, distAlphaMask );
     }
     else
     {
     oFactor=smoothstep ( OUTLINE MAX VALUE1, OUTLINE MAX VALUE0, distAlphaMask );
     }
     baseColor = lerp ( baseColor , OUTLINE COLOR, oFactor );
    }
    
    if ( SOFT EDGES ) 
    {
     baseColor.a ∗= smoothstep ( SOFT EDGE MIN, SOFT EDGE MAX, distAlphaMask );
    } 
    else
    {
     baseColor.a = distAlphaMask >= 0.5;
    } 
    
    if ( OUTER GLOW ) 
    {
     float4 glowTexel = tex2D ( BaseTextureSampler, i.baseTexCoord.xy+GLOW UV OFFSET );
     float4 glowc = OUTER GLOW COLOR ∗ smoothstep (OUTER GLOW MIN DVALUE, OUTER GLOW MAX DVALUE, glowTexel.a );
     baseColor = lerp ( glowc , baseColor , mskUsed );
    }
    

    1.2.4 圆角消除方案

    我们注意到,上面实现的字体效果在一些锐利的转折处都会出现一定的圆角现象,且这个现象随着SDF贴图的分辨率的降低会变得更为严重,通常情况下,这个不会是什么问题,但是对于一些要求锐角的情景就不太合适了,这里给出了一种解决方案,就是通过增加一个额外的通道,如下图所示,左边小图是原有单通道SDF的渲染效果,在alpha test的渲染模式下,尖角处由于双线性采样导致alpha数值下降从而被裁掉,而右边则是通过两个通道来表示,其中重叠部分为真正需要绘制的区域,通过这样的方式就不会存在前面由于旁边区域没有像素从而经过双线性混合后alpha数值下降的问题,因此可以得到较为尖锐的夹角,不过这种方案的问题也很明显,那就是要想得到正确的双通道SDF可能会需要经过复杂的计算,尤其是当需要绘制的图案较为复杂的时候。

    2. 方案特点

    这种方案具有如下的一些优点:

    • 可以兼容所有的GPU,包括那些不支持可编程管线的GPU
    • 运行效率跟传统的texture mapping方案基本一致
    • 能够借用现代GPU自带的双线性插值能力
    • 对vector贴图采样增加的指令数较少,不会因此导致指令数超过上限
    • 输入贴图除了vector贴图之外,还可以使用其他形式存储
    • 贴图格式不需要过于复杂,只用普通的8位贴图即可支持

    其不足之处在于:

    • 字体的锐角特征得不到较好的保留

    参考

    [1] Improved Alpha-Tested Magnification for Vector Textures and Special Effects
    [2] Drawing Text with Signed Distance Fields in Mapbox GL

    相关文章

      网友评论

          本文标题:【Siggraph 2007】Improved Alpha-Te

          本文链接:https://www.haomeiwen.com/subject/xjdfhrtx.html