贝塞尔曲线的内容很基础,只是国内的各种图形学教材都容易把这部分讲的特别“复杂”(大量数学公式推导证明),新手极其不友好。这里不把证明作为重点,而只关注是什么。 同时,也为了整理一下我以前看的一些东西,本文会从三次曲线开始。
从三次曲线开始# 曲线的参数化简单理解为用一个参数方程表示曲线,这样可以通过控制参数来控制曲线,也可以通过遍历参数获得曲线上每一个点的精确位置。我们知道可以用多项式去逼近函数,所以可以用多项式作为参数方程的表达式。 为了统一,参数范围一般是0~1. 三次多项式定义的曲线在操作灵活性和计算简单性方面比较平均.
Q ( t ) = a + b t + c t 2 + d t 3 \bold Q(t) = \bold a+\bold bt+\bold ct^2+ \bold dt^3 Q ( t ) = a + b t + c t 2 + d t 3 注意除了参数之外的字母都是向量,表示x,y,z分量。 用矩阵表示为:
Q ( t ) = [ a b c d ] [ 1 t t 2 t 3 ] Q ( t ) = C T ( t ) \begin{aligned} \bold Q(t) &= \begin{bmatrix} \bold a & \bold b & \bold c & \bold d \end{bmatrix} \begin{bmatrix} 1 \\ t \\ t^2 \\ t^3 \end{bmatrix} \\ \bold Q(t) &= \bold C \bold T(t) \end{aligned} Q ( t ) Q ( t ) = [ a b c d ] 1 t t 2 t 3 = CT ( t ) 对于三次曲线的求导也非常简单,因为 C \bold C C 是常数矩阵,所以直接对向量 T ( t ) \bold T(t) T ( t ) 求导即可.
d d t Q ( t ) = C [ 0 1 2 t 3 t 2 ] \begin{aligned} \frac{d}{dt}\bold Q(t)=\bold C \begin{bmatrix} 0 \\ 1 \\ 2t \\ 3t^2 \end{bmatrix} \end{aligned} d t d Q ( t ) = C 0 1 2 t 3 t 2 我们希望有办法控制系数矩阵 C \bold C C 中的值,因而我们引入若干控制点 ,对这些控制点定义不同的约束条件 ,比如,以某两个点为起点、终点,以某两个点之间的斜率为曲线起点的导等等,这样能通过控制这些控制点来比较直观地修改曲线。 为了计算上能够更“通用”,往往用如下方式表示一组约束条件,直接对“约束条件”做加权和”:
Q ( t ) = ∑ i = 1 4 ( a i + b i t + c i t 2 + d i t 3 ) g i \bold Q(t) = \sum_{i=1}^4 (a_i+b_it+c_it^2+d_it^3) \bold g_i Q ( t ) = i = 1 ∑ 4 ( a i + b i t + c i t 2 + d i t 3 ) g i 其中 g 是约束条件,往往直接是控制点本身,或者是由控制点能够直接得到的,每个 g 都是三维向量。 上式用矩阵表示,为:
Q ( t ) = [ g 1 g 2 g 3 g 4 ] [ a 1 a 2 a 3 a 4 b 1 b 2 b 3 b 4 c 1 c 2 c 3 c 4 d 1 d 2 d 3 d 4 ] [ 1 t t 2 t 3 ] Q ( t ) = G M T ( t ) \begin{aligned} \bold Q(t)&=\begin{bmatrix} \bold g_1 & \bold g_2 & \bold g_3 & \bold g_4 \end{bmatrix} \begin{bmatrix} a_1 & a_2 & a_3 &a_4 \\ b_1 & b_2 & b_3 &b_4 \\ c_1 & c_2 & c_3 &c_4 \\ d_1 & d_2 & d_3 &d_4 \end{bmatrix} \begin{bmatrix} 1 \\ t \\ t^2 \\ t^3 \end{bmatrix} \\ \bold Q(t) &= \bold G \bold M \bold T(t) \end{aligned} Q ( t ) Q ( t ) = [ g 1 g 2 g 3 g 4 ] a 1 b 1 c 1 d 1 a 2 b 2 c 2 d 2 a 3 b 3 c 3 d 3 a 4 b 4 c 4 d 4 1 t t 2 t 3 = GMT ( t ) 其中 G M \bold G \bold M GM 相当于前面的系数矩阵 C \bold C C ,对于特定的控制点,G 是确定的,需要求解 M 使得 Q(t) 满足规定的约束条件,从而得到由这些控制点和约束条件决定的三次曲线 Q。
Hermite曲线# Hermite 曲线是对上述三次曲线经过四个控制点进行约束后得到的结果。四个控制点 g 0 , g 1 , g 2 , g 3 g_0, g_1, g_2, g_3 g 0 , g 1 , g 2 , g 3 的约束规则是:
g 0 , g 3 g_0, g_3 g 0 , g 3 分别是曲线的起点和终点。g 1 , g 0 g_1, g_0 g 1 , g 0 形成的直线的斜率是起点的导数。g 2 , g 3 g_2, g_3 g 2 , g 3 形成的直线的斜率是终点的导数。可见,g 1 , g 2 g1,g2 g 1 , g 2 的存在只是提供了一种控制起点终点导数的直观方式。后面将直接用P 1 , P 2 , T 1 , T 2 \bold P_1, \bold P_2, \bold T_1, \bold T_2 P 1 , P 2 , T 1 , T 2 分别表示起点终点、起点导数和终点导数。 直接用 P 1 , P 2 , T 1 , T 2 \bold P_1, \bold P_2, \bold T_1, \bold T_2 P 1 , P 2 , T 1 , T 2 作为控制条件,作为上面的 G,代入上面的式子有:
Q ( t ) = [ P 1 P 2 T 1 T 2 ] M T ( t ) \bold Q(t)=\begin{bmatrix} P_1 & P_2 & T_1 & T_2 \end{bmatrix} \bold M \bold T(t) Q ( t ) = [ P 1 P 2 T 1 T 2 ] MT ( t ) 需要满足上面前面所述的约束条件,即起点终点和导数。
Q ( 0 ) = P 1 Q ( 1 ) = P 2 d d t Q ( 0 ) = T 1 d d t Q ( 1 ) = T 2 \begin{aligned} \bold Q(0) &= P_1 \\ \bold Q(1) &= P_2 \\ \frac{d}{dt}\bold Q(0) &= T_1 \\ \frac{d}{dt}\bold Q(1) &= T_2 \\ \end{aligned} Q ( 0 ) Q ( 1 ) d t d Q ( 0 ) d t d Q ( 1 ) = P 1 = P 2 = T 1 = T 2 上面四个方程写成矩阵的形式:
[ P 1 P 2 T 1 T 2 ] = [ P 1 P 2 T 1 T 2 ] M [ 1 1 0 0 0 1 1 1 0 1 0 2 0 1 0 3 ] \begin{bmatrix} P_1 & P_2 & T_1 & T_2 \end{bmatrix} = \begin{bmatrix} P_1 & P_2 & T_1 & T_2 \end{bmatrix} \bold M \begin{bmatrix} 1 & 1 & 0 & 0 \\ 0 & 1 & 1 & 1 \\ 0 & 1 & 0 & 2 \\ 0 & 1 & 0 & 3 \end{bmatrix} [ P 1 P 2 T 1 T 2 ] = [ P 1 P 2 T 1 T 2 ] M 1 0 0 0 1 1 1 1 0 1 0 0 0 1 2 3 容易解得 Hermite 曲线的 M H \bold M_H M H :
M H = [ 1 1 0 0 0 1 1 1 0 1 0 2 0 1 0 3 ] − 1 \begin{aligned} \bold M_H = \begin{bmatrix} 1 & 1 & 0 & 0 \\ 0 & 1 & 1 & 1 \\ 0 & 1 & 0 & 2 \\ 0 & 1 & 0 & 3 \end{bmatrix}^{-1} \end{aligned} M H = 1 0 0 0 1 1 1 1 0 1 0 0 0 1 2 3 − 1 Hermite 曲线 Q H ( t ) \bold Q_H(t) Q H ( t ) :
Q H ( t ) = [ P 1 P 2 T 1 T 2 ] M H T ( t ) Q H ( t ) = [ P 1 P 2 T 1 T 2 ] [ 1 1 0 0 0 1 1 1 0 1 0 2 0 1 0 3 ] − 1 T ( t ) \begin{aligned} \bold Q_H(t)&=\begin{bmatrix} P_1 & P_2 & T_1 & T_2 \end{bmatrix} \bold M_H \bold T(t) \\ \bold Q_H(t)&=\begin{bmatrix} P_1 & P_2 & T_1 & T_2 \end{bmatrix} \begin{bmatrix} 1 & 1 & 0 & 0 \\ 0 & 1 & 1 & 1 \\ 0 & 1 & 0 & 2 \\ 0 & 1 & 0 & 3 \end{bmatrix}^{-1} \bold T(t) \end{aligned} Q H ( t ) Q H ( t ) = [ P 1 P 2 T 1 T 2 ] M H T ( t ) = [ P 1 P 2 T 1 T 2 ] 1 0 0 0 1 1 1 1 0 1 0 0 0 1 2 3 − 1 T ( t ) 给定四个控制点,即可得到 P1,P2,T1,T2,然后应用上式即可得到 Hermite 曲线。
贝塞尔曲线# 贝塞尔一般的来说先从数学定义开始,然后证明一些性质,最后才介绍怎么算出贝塞尔曲线上各个点。GAMES101 从是什么的角度入手,先介绍贝塞尔曲线是怎么画出来的,最后稍微提及其数学表达。 这里仍旧从数学表达式入手,贝塞尔曲线的表达式本身不难,仅仅以知道是什么为目的的话,并没有很大难度。
贝塞尔曲线解决的问题是,给定一系列控制点 { b 0 , b 1 , … , b n } \left\{ b_0, b_1, \dots,b_n\right\} { b 0 , b 1 , … , b n } ,用这些控制点控制一条曲线。 贝塞尔曲线通过对 Bernstein基函数 加权求和的方式定义。
Q B ( t ) = ∑ i = 0 n B i , n ( t ) b i B i , n ( t ) = ( n i ) t i ( 1 − t ) i \begin{aligned} \bold Q_B(t) &= \sum_{i=0}^nB_{i,n}(t) b_i \\ B_{i,n}(t)&=\begin{pmatrix} n \\ i \end{pmatrix}t^i(1-t)^i \end{aligned} Q B ( t ) B i , n ( t ) = i = 0 ∑ n B i , n ( t ) b i = ( n i ) t i ( 1 − t ) i 可见,对 Bernstein基函数加权求和,权重即为控制点坐标本身,而Bernstein基函数为 ( t + ( 1 − t ) ) n (t+(1-t))^n ( t + ( 1 − t ) ) n 的二项式展开中的项。
贝塞尔曲线的性质。# 1、起点终点 Q B ( 0 ) = b 0 , Q B ( 1 ) = b 1 Q_B(0) = b_0, Q_B(1)=b_1 Q B ( 0 ) = b 0 , Q B ( 1 ) = b 1 起点的切线方向和 b 0 b 1 b_0b_1 b 0 b 1 方向一致,终点的切线方向和 b n − 1 b n b_{n-1}b_n b n − 1 b n 方向一致。
2、降阶(导数) Q B ′ ( t ) = n ∑ i = 0 n − 1 b i ( B i − 1 , n − 1 ( t ) − B i , n − 1 ( t ) ) Q_B'(t)=n\sum_{i=0}^{n-1}b_i(B_{i-1, n-1}(t)-B_{i,n-1}(t)) Q B ′ ( t ) = n ∑ i = 0 n − 1 b i ( B i − 1 , n − 1 ( t ) − B i , n − 1 ( t )) Q B ′ ′ ( t ) = n ( n − 1 ) ∑ i = 0 n − 2 ( b i + 2 − 2 b i + 1 + b i ) B i , n − 1 ( t ) Q_B''(t)=n(n-1)\sum_{i=0}^{n-2}(b_{i+2}-2b_{i+1}+b_i)B_{i,n-1}(t) Q B ′′ ( t ) = n ( n − 1 ) ∑ i = 0 n − 2 ( b i + 2 − 2 b i + 1 + b i ) B i , n − 1 ( t )
3、凸包性。 贝塞尔曲线一定处在其所有控制点形成的凸包的范围内。
4、(仿射变换)几何不变性 贝塞尔曲线只与控制点位置有关,与具体使用那个坐标轴无关。对所有控制点做同一个仿射变换,最后得到的贝塞尔曲线和变换之前是一样的。 只对仿射变换有效,对于其它如投影变换等,没有这个性质。
5、不具备局部可控性 修改贝塞尔曲线的任意控制点,整条线都会发生变化。 在控制点较多的时候,曲线会变得相当平,难以较大幅度改变。
de … 算法# 这个算法解决了贝塞尔曲线怎么画出来的问题,即根据参数 t 快速地找到这个 t 对应的贝塞尔曲线上的点坐标。 首先,把所有原本的控制点定义为 b i 0 b_i^0 b i 0 ,对于参数值 t,取 b i k = ( 1 − t ) b i k − 1 + t b i + 1 k − 1 b_i^k=(1-t)b_{i}^{k-1}+tb_{i+1}^{k-1} b i k = ( 1 − t ) b i k − 1 + t b i + 1 k − 1 ,即 b i k b_i^k b i k 是线段 b i k − 1 b i + 1 k − 1 b_{i}^{k-1}b_{i+1}^{k-1} b i k − 1 b i + 1 k − 1 上的一点,使得该点到左右端点长度的比例是 b i k − 1 b i k b i k b i + 1 k − 1 = t 1 − t \frac{b_{i}^{k-1}b_i^k}{b_i^kb_{i+1}^{k-1}}=\frac{t}{1-t} b i k b i + 1 k − 1 b i k − 1 b i k = 1 − t t ,即 1/t 分位点。 然后将 b i k , b i + 1 k b_i^k, b_{i+1}^k b i k , b i + 1 k 连起来形成一条新的线段,然后重复上述过程。 可见,上标 k 每+1,得到的点的个数就-1. 重复上述过程,最终得到唯一的 b 0 n b_0^n b 0 n 的坐标,就是 Q B ( t ) Q_B(t) Q B ( t ) 即参数值 t 对应的贝塞尔曲线上的坐标。
这是一个动态规划算法。
Q B ( t ) = b 0 n b i k = { ( 1 − t ) b i k − 1 + t b i + 1 k − 1 , k ≠ 0 b i , k = 0 \begin{aligned} \bold Q_B(t) &= b_0^n \\ b_i^k &= \begin{cases} \begin{aligned} (1-t)b_{i}^{k-1}+tb_{i+1}^{k-1} , \quad k &\neq 0 \\ b_i, \quad k &= 0 \end{aligned} \end{cases} \end{aligned} Q B ( t ) b i k = b 0 n = { ( 1 − t ) b i k − 1 + t b i + 1 k − 1 , k b i , k = 0 = 0 就是不停地取 1/t 分位点,然后把相邻的新点连起来,再取分位点。。。一直到最后只剩一个点。
贝塞尔曲面# 曲线用一个参数 t 就可以了,而曲面需要用参数 u, v。而控制点也构成一个 m*n 的 “网格”。参数 { b 00 … b m n } \left\{b_{00}\dots b_{mn} \right\} { b 00 … b mn }
Q B ( u , v ) = ∑ i + j ≤ n B i , j , n ( u , v ) b u v B i , j , n ( u , v ) = ( n i j ) u i v j ( 1 − u − v ) n − i − j \begin{aligned} \bold Q_B(u,v) &= \sum_{i+j \leq n}B_{i,j,n}(u,v) b_{uv} \\ B_{i,j,n}(u,v)&=\begin{pmatrix} n \\ ij \end{pmatrix}u^iv^j(1-u-v)^{n-i-j} \end{aligned} Q B ( u , v ) B i , j , n ( u , v ) = i + j ≤ n ∑ B i , j , n ( u , v ) b uv = ( n ij ) u i v j ( 1 − u − v ) n − i − j 其中高阶的 Bernstein基函数 就是 ( u + v + ( 1 − u − v ) ) n (u+v+(1-u-v))^n ( u + v + ( 1 − u − v ) ) n 的广义二项式展开的项。
贝塞尔曲面的算法就是进行了两趟 de…算法。 给定 (u, v),对于 u 方向,对每个 u 方向上的控制点(n列,每列m个)以u为 t 算出自己的贝塞尔曲线点,在以这些点为新的贝塞尔曲线控制点,再以 v 为 t 进行一次贝塞尔曲线求解得到最终的 (u,v) 对应的曲面上的点。 就是非常直观的行列分解 。
Picewise 贝塞尔曲线# 由于贝塞尔曲线没有局部性,所以往往用分段贝塞尔曲线来近似实现局部性。将控制点分组,每组若干个控制点(一般是三四个),上一组最后一个控制点和下一组第一个控制点重合。这样每组控制点的贝塞尔曲线能首尾相接地拼合在一起,并且不同组的控制点之间互不干扰,实现近似的局部性。