真实感图像渲染系列:贝塞尔曲线旋转体与直线求交

这是真实感图像渲染系列的第八篇文章。

概述

贝塞尔曲线是一个由多个控制点组合而成的曲线。在本文探讨的贝塞尔曲线,是由多条简单的4阶贝塞尔曲线拼接而成。绕z轴旋转贝塞尔曲线,我们可以得到一个旋转体曲面,该曲面就是待渲染的参数曲面。本文将探讨如何使用牛顿迭代法与直线求交。求交过程中

  1. 关于迭代初值的求法
  2. 迭代中的一些优化技巧
  3. 关于浮点数精度误差eps的求法。

贝塞尔曲线的定义

贝塞尔曲线是一个由多个控制点组合而成的曲线,n阶贝塞尔曲线定义为

(1)   \begin{equation*}  P(u) = \left[ \begin{matrix}P^{(x)}(u) \\ P^{(y)}(u) \end{matrix} \right] = \left[ \begin{matrix} \sum _{i=0}^n P_i^{(x)} C(n,i) (1-u)^{n-i}u^i \\ \sum _{i=0}^n P_i^{(y)}C(n,i) (1-u)^{n-i}u^i \end{matrix} \right] \end{equation*}

式(1)定义了该曲线P(u)的关于参数u \in [0,1]的参数方程,其中P_i为n个曲线的控制点。当我看到这个公式的时候,整个人都已经懵掉了。实际上理解起来并不复杂。

函数P(u)由参数u定义了一个二维向量,其中P^{(x)}P^{(y)}相互独立。只需要理解其中一个的产生式即可。考虑函数 f_i(u) =C(n,i) u^i (1-u)^{n-i-1},发现这f_0(u),f_1(i),\cdots,f_{n-1}(u)实际上是一个和为1的数列,数列在杨辉三角形中定义。换句话来说,f_i(u)实际上可以得到一个权值的分配,使用f_i(u)对于所有的控制点P_i加权平均,就是贝塞尔曲线尝试完成的事情。当f_0(u) = C(n,0) u^0 (1-u)^{n-i} = 1权值全部分配给了P_0,因此贝塞尔曲线的起点(u=0)和P_0重合。

贝塞尔曲线旋转体

在三维空间中,我们通过旋转贝塞尔曲线得到一个曲面。曲面强制对z轴旋转对称。(注:在代码中是对x轴旋转对称,不过z轴对称相对好理解一些)。

(2)   \begin{equation*}  S(u,\theta) = \left[ \begin{matrix} Q_x + sin\theta P^{(x)}(u) \\ Q_y + cos\theta P^{(x)}(u) \\ Q_z + P^{(y)}(u) \end{matrix} \right] \end{equation*}

一个贝塞尔曲线旋转体要能够成功渲染,其核心就是需要支持与光线的求交。光线我们使用(rayO,rayD)表示,rayO表示光线起点,rayD表示光线方向,是一个单位向量。则光线上一点可表示为C(t),满足:

(3)   \begin{equation*}  C(t) = rayO + t \times rayD \end{equation*}

倘若直线C(t)S(u,\theta)相交,则有

(4)   \begin{equation*}  F(t,u,\theta) = C(t) - S(u,\theta) = 0 \end{equation*}

其中,F(t,u,\theta)t,u,\theta的函数,要求t>00\leq u \leq 1,原问题可转化为求函数零点问题。函数零点问题使用牛顿迭代解决。

(5)   \begin{equation*}  x_{i+1} = x_i - [F'(x_i)]^{-1} \cdot F(x_i) \end{equation*}

其中F'(x_i)为函数F(x_i)的Jacobian矩阵,定义为:

(6)   \begin{equation*}  \frac{\partial F}{\partial t} = \left[ \begin{matrix} \frac{\partial F_1}{\partial t} & \frac{\partial F_1}{\partial u} & \frac{\partial F_1}{\partial \theta} \\ \frac{\partial F_2}{\partial t} & \frac{\partial F_2}{\partial u} & \frac{\partial F_2}{\partial \theta} \\ \frac{\partial F_3}{\partial t} & \frac{\partial F_3}{\partial u} & \frac{\partial F_3}{\partial \theta} \\ \end{matrix} \right] \end{equation*}

要试图计算出式(\ref(eq:jacobian)),我们需要分别求出F(t,u,\theta)对于t,u,\theta偏导数的每一个分量的值(共九个)。结果为:

(7)   \begin{equation*}  \frac{\partial F}{\partial args} = \left[ \begin{matrix} rayD_x & -sin\theta \frac{dP_x}{du} & -cos\theta P_x(u)\\ rayD_y & -cos\theta \frac{dP_x}{du} & +sin\theta P_x(u)\\ rayD_z & -\frac{dP_y}{du} & 0\\ \end{matrix} \right] \end{equation*}

其中\frac{dP}{du}是对于式子(1)按照最原始的求导法则求导的结果。

贝塞尔曲面物体设计

如图

共6条4阶贝塞尔曲线,每一条由绿蓝红绿四个控制点定义。

注意事项

  1. 牛顿迭代需要保证答案中u \in [0,1],而牛顿迭代本身无法附加条件,因此需要在答案的邻域中查找合适的u,t,\theta,我用到的方法是,对于给定曲线,生成一个圆柱体包含框,随机该圆柱体里面高度h的一个圆形面片,将光线与该面片的交点的u,t,\theta作为迭代的初值。随机h约30次即可得解。
  2. 由于牛顿迭代不太精确,可能是的结果产生微小偏移,这种偏移会对判断点在平面哪一侧产生影响,我们可以通过调整eps的取值,不同部分赋予不同的eps,使得这些误差不至于相互影响。
  3. 贝塞尔曲线求交本身费时间,但是判断是否一定没有交点却相对简单。可以求出贝塞尔曲线旋转体的“圆柱体包围盒”,(更精确的话,可以是空心圆柱体包围盒)。然后判断直线是否与包围盒有交点。判断直线与圆柱交点P = rayO + k \times rayD的位置,可以先讲所有东西映射到z=0屏幕,这是变成2维直线和圆求交,算出k,回带到三维情况验证。
  4. 实际上,像本文采用的多条低阶贝塞尔曲线求交并不明智,倘若将本文那个碗的6条贝塞尔曲线改为1条简单一些的曲线,然后做一个玻璃杯什么的,渲染效率将提高6倍!

 

 

原创文章地址:【真实感图像渲染系列:贝塞尔曲线旋转体与直线求交】,转载时请注明出处mhy12345.xyz

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据