这是真实感图像渲染系列的第八篇文章。
概述
贝塞尔曲线是一个由多个控制点组合而成的曲线。在本文探讨的贝塞尔曲线,是由多条简单的4阶贝塞尔曲线拼接而成。绕z轴旋转贝塞尔曲线,我们可以得到一个旋转体曲面,该曲面就是待渲染的参数曲面。本文将探讨如何使用牛顿迭代法与直线求交。求交过程中
- 关于迭代初值的求法
- 迭代中的一些优化技巧
- 关于浮点数精度误差eps的求法。
贝塞尔曲线的定义
贝塞尔曲线是一个由多个控制点组合而成的曲线,n阶贝塞尔曲线定义为
(1)
式(1)定义了该曲线的关于参数
的参数方程,其中
为n个曲线的控制点。当我看到这个公式的时候,整个人都已经懵掉了。实际上理解起来并不复杂。
函数由参数
定义了一个二维向量,其中
与
相互独立。只需要理解其中一个的产生式即可。考虑函数
,发现这
实际上是一个和为1的数列,数列在杨辉三角形中定义。换句话来说,
实际上可以得到一个权值的分配,使用
对于所有的控制点
加权平均,就是贝塞尔曲线尝试完成的事情。当
权值全部分配给了
,因此贝塞尔曲线的起点(
)和
重合。
贝塞尔曲线旋转体
在三维空间中,我们通过旋转贝塞尔曲线得到一个曲面。曲面强制对z轴旋转对称。(注:在代码中是对x轴旋转对称,不过z轴对称相对好理解一些)。
(2)
一个贝塞尔曲线旋转体要能够成功渲染,其核心就是需要支持与光线的求交。光线我们使用表示,
表示光线起点,
表示光线方向,是一个单位向量。则光线上一点可表示为
,满足:
(3)
倘若直线与
相交,则有
(4)
其中,为
的函数,要求
且
,原问题可转化为求函数零点问题。函数零点问题使用牛顿迭代解决。
(5)
其中为函数
的Jacobian矩阵,定义为:
(6)
要试图计算出式(\ref(eq:jacobian)),我们需要分别求出对于
偏导数的每一个分量的值(共九个)。结果为:
(7)
其中是对于式子(1)按照最原始的求导法则求导的结果。
贝塞尔曲面物体设计
如图
共6条4阶贝塞尔曲线,每一条由绿蓝红绿四个控制点定义。
注意事项
- 牛顿迭代需要保证答案中
,而牛顿迭代本身无法附加条件,因此需要在答案的邻域中查找合适的
,
,
,我用到的方法是,对于给定曲线,生成一个圆柱体包含框,随机该圆柱体里面高度
的一个圆形面片,将光线与该面片的交点的
,
,
作为迭代的初值。随机
约30次即可得解。
- 由于牛顿迭代不太精确,可能是的结果产生微小偏移,这种偏移会对判断点在平面哪一侧产生影响,我们可以通过调整eps的取值,不同部分赋予不同的eps,使得这些误差不至于相互影响。
- 贝塞尔曲线求交本身费时间,但是判断是否一定没有交点却相对简单。可以求出贝塞尔曲线旋转体的“圆柱体包围盒”,(更精确的话,可以是空心圆柱体包围盒)。然后判断直线是否与包围盒有交点。判断直线与圆柱交点
的位置,可以先讲所有东西映射到
屏幕,这是变成2维直线和圆求交,算出
,回带到三维情况验证。
- 实际上,像本文采用的多条低阶贝塞尔曲线求交并不明智,倘若将本文那个碗的6条贝塞尔曲线改为1条简单一些的曲线,然后做一个玻璃杯什么的,渲染效率将提高6倍!