本文主要的参考文献为:
1. 游戏编程中的数学——调和函数和中值坐标
2. Hormann K, Floater M S. Mean value coordinates for arbitrary planar polygons[J]. Acm Transactions on Graphics, 2006, 25(4):1424-1441.
3. Floater M S. Mean value coordinates[J]. Computer Aided Geometric Design, 2003, 20(1):19-27.
一般来说,我们不通过一个模型含有的网格(通常为三角形网格或四边形网格)以及顶点去理解它,而是在其网格上建立一个函数,这个函数拥有特定的性质,有助于解决我们面临的难题。那么这次介绍一个在网格上建立函数的方法——中值坐标。中值坐标是90年代后期产生的,其背后的数学原理却得追述到18、19世纪了,然而直到今天它依然没有受到游戏开发界足够的重视……本文重点是谈谈中值坐标的构造与应用。
由 Ceva 逆定理可以很容易证明三角形 Δv1v2v3 的三条中线交于一点(重心) v ,即重心的存在性得以证明: ∃ω1,ω2,ω3∈R,s.t.
令 Ψ 是平面上的一个任意简单多边形(在几何形状中,简单多边形是由直线,非相交的线段或“边”组成的扁平形状,其成对连接以形成封闭路径。 如果两边相交,那么多边形并不简单。定义参考自百度百科简单多边形),其顶点 v1,⋯,vn(n⩾3) ,其各边(互不相交)
由上图可以看出,简单多边形可以为凹多边形,也支持嵌套操作,并不局限于单个凸多边形。
对于 i=1,⋯,n ,我们定义 v∈R2 到 vi 的距离:
易知 Ai(v),−Bi(v),Ai−1(v) 是关于 △ivi−1vivi+1 的 v 的齐次重心坐标,即
由上图可以看出, v0 就相当于 vn ,同理可得 A0 相当于 An , An+1 相当于 A0 。
现在的关键步骤是对于齐次重心坐标的归一化,必须保证 λi(v)=ωi(v)∑j=1nωj(v) 中的分母不为0。对于凸多边形这是容易实现的。
事实上,由 ωi(v) 的定义我们可以得到
对于任一简单多边形的集合 Ψ ,我们称函数 λi:R2→R,i=1,⋯,n 为中值坐标,其中
非负性: λi 在凸多边形的内核上是正的,其中,平面上简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形内部。譬如说,就是一个在一个房子里面放一个摄像 头,能将所有的地方监视到的放摄像头的地点的集合即为多边形的核。(参考自博客半平面交,求解多边形内核)
至此,整个中值坐标的构造算是大功告成了!!!✿✿ヽ(°▽°)ノ✿
中值坐标最主要的应用便是在给定简单多边形 Ψ 的顶点 vi 以后,对于整个多边形区域的插值。换句话说,如果 fi(v)∈Rd 是在 vi 上的插值基函数,则我们可以将函数 F:R2→Rd 定义如下:
中值坐标的一个应用是图像变形,因为中值坐标为这个问题提供了一个特别简单的解决方案,可简要地说明如下。
给定一个矩形区域 Ω ,一个源多边形的集合 Ψ ,其顶点 vi∈Ω ,以及一个拓扑同胚的目标多边形的集合 Ψ^ (显然 Ψ 与 Ψ^ 顶点数相同),其顶点 v^i∈Ω ,我们想构建一个平滑的变形函数 f:Ω→Ω ,它将每一个 vi 映射到相应的 v^i 。这样一个变形函数 f 可以通过令 I^=I∘f−1 将一个源图像 I:Ω→C ( C 是一个颜色空间)转换为一个目标图像 I^:Ω→C 。我们通常会令 I^=I∘f−1 而非 I^=I∘f ,这样一种思想也称为逆向映射(两者的不同其实即为正映射与逆向映射的区别,具体可以参见博客图像变换——向前映射和向后映射),等等在后面会用一些实验结果图让大家感受一下两种映射方式的不同。
许多时候,图像变形函数可以用 Ψ^ 的中值坐标 λ^i 去表示:
λi:R2→R 是权函数,须满足条件 λi(v^i)=1,∑i=1nλi(v^i)=1 且 λi(v^)⩾0,i=1,2,⋯,n 。
Shepard 提出了以下简单的权函数:
对于上述问题,我们需要对空洞进行填补,我分别采用了均值滤波模板、中位数滤波模板与即时填充空洞进行处理:
容易看出,在对图像进行空洞区域的填补以后,出现了一些疑似空洞的点,故仍需要进行改进。先来简要分析一下原因~
由于存在取平均值的操作,故当一个像素点周围存在不同颜色的像素点时,填补以后容易出现颜色差异较大的点,譬如黑色方块和白色方块相邻边界上的空洞点,填补以后就会变成灰色点。
找到原因以后便可以有针对性地进行改进,自己采用的解决方案是替换均值滤波模板改用中位数滤波模板,需要注意的是,对一个空洞点填补时并不考虑周围的空洞点,即不应该将空洞点的像素用来求中位数;其次,若一个空洞点周围的非空洞点像素点数量为偶数时,其中位数是需要取平均的,此时容易出现类似使用均值滤波模板的填补错误,于是需要进行如下判断,假设该空洞点周围的非空洞点像素点数组为 surrounding_pixels,surrounding_pixels.size()%2==0 :
if surrounding_pixels[i] = = surrounding_pixels[i - 1],i=surrounding_pixels.size()/2
将surrounding_pixels[i]赋给空洞点;
else
为了避免出现取均值的操作,依然将surrounding_pixels[i]赋给空洞点;
在作了上述改进以后,发现仍存在一些问题,如:
可以看出,一些方块边缘会出现“毛边”现象,即方块颜色会“渗入”相邻的方块。究其原因,发现在填补时空洞点周围的空洞点太多,影响了其中位数的取值,因为对空洞点进行中位数滤波时是要舍去周围的空洞像素的,如下图所示:
为了解决上述现象,需要对空洞点进行即时填补,因为在之前的程序当中,找到空洞点对其进行中位数滤波以后,出于当前填补结果若是错误会对后面的填补效果产生影响的考虑,并非直接进行填补,而是先把像素存储起来,等到最后再进行填补。
而现在则需要对此做出改变,只要保证当前空洞点的填补结果是正确的就可以了。自己的做法如下所示:假设该空洞点周围的非空洞点像素点数组为 surrounding_pixels,surrounding_pixels.size()%2==0 :
if surrounding_pixels[i] = = surrounding_pixels[i - 1],i=surrounding_pixels.size()/2
将surrounding_pixels[i]赋给空洞点;
else
先不进行操作,将这个点记录下来,等对全部点进行中值滤波以后再对这个点进行中值滤波,此时因为之前的空洞点已被填补,故它能获得更多有效的像素点;
可以看出,在改进以后实验结果有了一定的改善。那么,我们再来看一下采用 I^=I∘g 的实验效果:
可以看出,实验的效果十分理想。无论是在算法运行时间及算法实现难度上都远胜之前的正向映射+空洞填补算法。当然对于图像变形还有其它优秀的算法,比如径向基函数算法( RBF 算法),限于篇幅此处不再赘述,不过可以弱弱地放张效果图让你们看看哈:
中值坐标的另一个应用是纹理参数化,简要地说明如下。
首先需要进行局部参数化:将局部结构映射到平面上,映射过程满足下列条件:
我们已经表明,中值坐标将三角形重心坐标的概念推广到任意的简单多边形,甚至是这些简单多边形的集合。中值坐标具有许多很漂亮的性质(如 Lagrange 性),对于在简单多边形的插值别有用。
我们注意到,中值坐标在任意简单多边形的情况下并不是处处都是正的。另一方面,可能是恰恰由于这些性质,中值坐标的插值性能才能如此棒,而且在实际应用中发挥着重要作用。(๑•̀ㅂ•́)و✧文中实验的源码会上传到我的 Github 上,有兴趣的同学可以自行下载玩玩~