简介有向间隔场(signed distance field,SDF)是一张标注了每个像素到最近几何体边缘间隔的位图。因为在几何轮廓之外提供了额定的信息,SDF能为二维渲染减少很多灵活性,比方渲染文字时任意调整笔画粗细、放大形态时没有显著锯齿、通过穿插淡化两张SDF来穿插淡化几何轮廓等等。
这里介绍一种通用的有向间隔场生成办法:Dead Reckoning(原始文献),它可能从二维位图形容的几何轮廓计算SDF。不同于之前的一些近似算法会失去有“棱角”的后果,Dead Reckoning得出的后果简直是精确的,靠近于全局遍历失去的参考后果。
Dead Reckoning这个名字来源于船舶导航办法,名字中的“dead”可能来源于“deduced”简称之后的语音流变。这种导航办法通过观察一个指标之外的固定标志物来推算与导航指标之间的方位和间隔,这与算法中通过观察街坊像素来寻找最近边界有些类似,故而得名。
算法Dead Reckoning与生物信息学畛域的Needleman-Wunsch序列比对算法相似,都是一种动静布局算法。在处理过程中,它岂但记录每个像素的边界间隔,而且记录对应边界的像素地位,或者说以后的间隔是“从哪来的”。
整个计算过程会遍历两遍矩阵,第一次从上向下、从左向右逐行遍历所有像素,第二次反过来从下向上、从右向左。
数据初始化假如输出几何形态存在位图I上,外部像素值为1,内部像素值为0;位图d用来存储间隔,位图p用来存储每个像素的最近边界地位的坐标。
位图d:将位于边际的地位设为零,所有其它地位设为无穷大。
位图p:将位于边际的地位设为指向本身,所有其它地位设为某个非法值。
这里有个细节问题:如何定义边际地位。原论文认为该当既蕴含那些位于几何形态外部的像素,也蕴含位于内部的像素。也就是说,边界的宽度为2像素。这样的起因是为了数学对称性:将内外部的定义颠倒,对应的SDF也是原SDF求正数。但实际上咱们通常并不关怀这个对称性,只计入外部的那条边界就能够了。所以边际的条件是:
I[x,y] == 1 and ( I[x,y]!= I[x-1,y] or I[x,y]!= I[x+1,y] or I[x,y]!= I[x,y-1] or I[x,y]!= I[x,y+1] )两次遍历遍历每个像素的时候,将它的以后距边界间隔与曾经解决过的四个街坊比拟,如果街坊的以后距边界间隔+像素间隔比它本人的要小,阐明街坊对应的边界更近,就应用街坊的边界信息,并依此更新本人的间隔。
假如左上角是(0,0),那么正向遍历的四个街坊就是它的左、左上、上、右上像素,反向遍历的四个街坊是它的右、右下、下、左下像素。
图中正在进行正向遍历的过程。绿色像素是曾经解决过的。蓝色像素是以后要解决的像素,它会从四个相邻的已解决像素(标为深绿色)中获取最近的那个边界地位。伪代码如下:
// 正向遍历for y in 1 to height { for x in 1 to width { curr_d = d[x,y]; curr_p = p[x,y]; // 查看左上像素 if d[x-1,y-1] + 1.414 < curr_d { curr_p = p[x-1,y-1]; curr_d = distance((x,y), curr_p); } // 查看正上方像素 if d[x,y-1] + 1 < curr_d { curr_p = p[x,y-1]; curr_d = distance((x,y), curr_p); } // 查看右上像素 if d[x+1,y-1] + 1.414 < curr_d { curr_p = p[x+1,y-1]; curr_d = distance((x,y), curr_p); } // 查看正左方像素 if d[x-1,y] + 1 < curr_d { curr_p = p[x-1,y]; curr_d = distance((x,y), curr_p); } d[x,y] = curr_d; p[x,y] = curr_p; }}// 反向遍历for y in height to 1 { for x in width to 1 { curr_d = d[x,y]; curr_p = p[x,y]; // 查看左下像素 if d[x-1,y+1] + 1.414 < curr_d { curr_p = p[x-1,y+1]; curr_d = distance((x,y), curr_p); } // 查看正下方像素 if d[x,y+1] + 1 < curr_d { curr_p = p[x,y+1]; curr_d = distance((x,y), curr_p); } // 查看右下像素 if d[x+1,y+1] + 1.414 < curr_d { curr_p = p[x+1,y+1]; curr_d = distance((x,y), curr_p); } // 查看正右方像素 if d[x+1,y] + 1 < curr_d { curr_p = p[x+1,y]; curr_d = distance((x,y), curr_p); } d[x,y] = curr_d; p[x,y] = curr_p; }}在正反两次遍历之后,间隔d曾经填充实现,然而没有辨别几何体内外。如果须要这个辨别,只须要再遍历依此图像,将位于外部的像素的间隔变成负的。
...