一个连续函数都可以用一个多项式无限逼近
贝塞尔曲线于 1962 年,由法国工程师皮埃尔·贝济埃(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。
B 1 ( t ) = ( 1 − t ) P 0 + t P 1 , t ∈ [ 0 , 1 ] \mathbf{B_1(t)= (1-t)P_0 + tP_1, t\in[0, 1]} B1(t)=(1−t)P0+tP1,t∈[0,1]
P 0 ′ = ( 1 − t ) P 0 + t P 1 P 1 ′ = ( 1 − t ) P 1 + t P 2 B 2 ( t ) = ( 1 − t ) P 0 ′ + t P 1 ′ = ( 1 − t ) 2 P 0 + 2 t ( 1 − t ) P 1 + t 2 P 2 \mathbf{P_0'= (1-t)P_0 + tP_1} \\ \ \\ \mathbf{P_1'= (1-t)P_1 + tP_2} \\ \ \\ \mathbf{B_2(t)= (1-t)P_0' + tP_1'=(1-t)^2P_0+2t(1-t)P_1+t^2P_2} \\ P0′=(1−t)P0+tP1 P1′=(1−t)P1+tP2 B2(t)=(1−t)P0′+tP1′=(1−t)2P0+2t(1−t)P1+t2P2
B 3 ( t ) = ( 1 − t ) 3 P 0 + 3 t ( 1 − t ) 2 P 1 + 3 t 2 ( 1 − t ) 2 P 1 + t 3 P 3 \mathbf{B_3(t)= (1-t)^3P_0 + 3t(1-t)^2P_1 + 3t^2(1-t)^2P_1 + t^3P_3} B3(t)=(1−t)3P0+3t(1−t)2P1+3t2(1−t)2P1+t3P3
Bernstein polynomial(伯恩斯坦多项式)
B n ( t ) = ∑ i = 0 n C n i P i ( 1 − t ) n − i t i = ∑ i = 0 n B i , n P i C n i = n ! ( n − i ) ! ⋅ i ! B i , n = n ! ( n − i ) ! ⋅ i ! ( 1 − t ) n − i t i \mathbf{B_n(t)= \sum_{i=0}^{n} C_{n}^{i} P_i (1-t)^{n-i}t^i = \sum_{i=0}^{n} B_{i,n} P_i } \\ \ \\ \mathbf{C_{n}^{i} = \frac{n!}{(n-i)!\cdot i!}}\\ \ \\ \mathbf{B_{i,n} = \frac{n!}{(n-i)!\cdot i!}(1 - t)^{n-i}t^i}\\ Bn(t)=i=0∑nCniPi(1−t)n−iti=i=0∑nBi,nPi Cni=(n−i)!⋅i!n! Bi,n=(n−i)!⋅i!n!(1−t)n−iti
P i , j \bm{P_{i,j}} Pi,j表示第i列的第j个点
P i , j = ( 1 − t ) P i − 1 , j + t P i − 1 , j + 1 i ∈ [ 1 , n ] , j ∈ [ 1 , n − i + 1 ] \mathbf{P_{i, j} = (1 - t)P_{i - 1, j} + tP_{i - 1, j + 1}}\\ \ \\ \mathbf{i \in [1, n], j \in [1, n - i + 1]}\\ Pi,j=(1−t)Pi−1,j+tPi−1,j+1 i∈[1,n],j∈[1,n−i+1]
de Casteljau算法在Bézier曲线的光栅化阶段特别有用。
Vector2 Render::CalculateBezierPoint(const std::vector<Vector2>& ctl_vecs, float t){
std::vector<Vector2> points(ctl_vecs);
for (int i = 0; i < points.size() - 1; ++i) {
for (int j = 0; j < points.size() - i - 1; ++j) {
points[j] = interp(points[j], points[j + 1], t);
}
}
return points[0];
}
增加控制点却不改变原先曲线的表现
Degree raising means that adding control points to raise the degree of the Bézier curves, but the shape and direction of the curve remain unchanged.
已知:
B n ( t ) = ∑ i = 0 n B i , n P i \mathbf{B_n(t)= \sum_{i=0}^{n} B_{i,n} P_i } Bn(t)=i=0∑nBi,nPi
则:
B n ( t ) = ∑ i = 0 n B i , n P i = ∑ i = 0 n ( 1 − t + t ) B i , n P i \mathbf{B_n(t)= \sum_{i=0}^{n} B_{i,n} P_i = \sum_{i=0}^{n} (1-t + t) B_{i,n} P_i} Bn(t)=i=0∑nBi,nPi=i=0∑n(1−t+t)Bi,nPi
接下来看下相乘的两个式子
式子1:
( 1 − t ) B i , n P i = n ! ( n − i ) ! ⋅ i ! ( 1 − t ) n + 1 − i t i P i = n + 1 − i n + 1 ( n + 1 ) ! ( n + 1 − i ) ! ⋅ i ! ( 1 − t ) n + 1 − i t i P i = n + 1 − i n + 1 B i , n + 1 P i ( 1 − t ) B i , n P i = n + 1 − i n + 1 B i , n + 1 P i \mathbf{(1 - t)B_{i, n}P_i = \frac{n!}{(n-i)!\cdot i!}(1 - t)^{n + 1 - i}t^i P_i }\\ \ \\ \mathbf{= \frac{n + 1 - i}{n + 1}\frac{(n+1)!}{(n + 1 - i)!\cdot i!}(1 - t)^{n + 1 - i}t^i P_i = \frac{n + 1 - i}{n + 1}B_{i, n + 1}P_i}\\ \ \\ \mathbf{(1 - t)B_{i, n} P_i = \frac{n + 1 - i}{n + 1}B_{i, n + 1}P_i }\\ (1−t)Bi,nPi=(n−i)!⋅i!n!(1−t)n+1−itiPi =n+1n+1−i(n+1−i)!⋅i!(n+1)!(1−t)n+1−itiPi=n+1n+1−iBi,n+1Pi (1−t)Bi,nPi=n+1n+1−iBi,n+1Pi
式子2:
t B i , n P i = n ! ( n − i ) ! ⋅ i ! ( 1 − t ) n − i t i + 1 P i = i + 1 n + 1 ( n + 1 ) ! ( n + 1 − i − 1 ) ! ⋅ ( i + 1 ) ! ( 1 − t ) n + 1 − i − 1 t i + 1 P i = i + 1 n + 1 B i + 1 , n + 1 P i t B i , n P i = i + 1 n + 1 B i + 1 , n + 1 P i \mathbf{tB_{i, n}P_i = \frac{n!}{(n-i)!\cdot i!}(1 - t)^{n - i}t^{i + 1} P_i }\\ \ \\ \mathbf{= \frac{i + 1}{n + 1}\frac{(n+1)!}{(n + 1 - i - 1)!\cdot (i + 1)!}(1 - t)^{n + 1 - i - 1}t^{i + 1} P_i = \frac{i + 1}{n + 1}B_{i + 1, n + 1}P_i}\\ \ \\ \mathbf{tB_{i, n}P_i = \frac{i + 1}{n + 1}B_{i + 1, n + 1}P_i}\\ tBi,nPi=(n−i)!⋅i!n!(1−t)n−iti+1Pi =n+1i+1(n+1−i−1)!⋅(i+1)!(n+1)!(1−t)n+1−i−1ti+1Pi=n+1i+1Bi+1,n+1Pi tBi,nPi=n+1i+1Bi+1,n+1Pi
对于等式1,由于i = n + 1的时候等式为0,所以可以将等式变为
∑ i = 0 n n + 1 − i n + 1 B i , n + 1 P i = ∑ i = 0 n + 1 n + 1 − i n + 1 B i , n + 1 P i \mathbf{\sum_{i = 0}^{n}\frac{n + 1 - i}{n + 1}B_{i, n + 1}P_i = \sum_{i = 0}^{n + 1}\frac{n + 1 - i}{n + 1}B_{i, n + 1}P_i} i=0∑nn+1n+1−iBi,n+1Pi=i=0∑n+1n+1n+1−iBi,n+1Pi
对于等式2我们可以将i用i-1替代,因为i=-1时等式为0,所以成立,所以
∑ i = 0 n i + 1 n + 1 B i + 1 , n + 1 P i = ∑ i = 0 n + 1 i n + 1 B i , n + 1 P i − 1 \mathbf{\sum_{i = 0}^{n}\frac{i + 1}{n + 1}B_{i + 1, n + 1}P_i = \sum_{i = 0}^{n + 1}\frac{i}{n + 1}B_{i, n + 1}P_{i - 1}} i=0∑nn+1i+1Bi+1,n+1Pi=i=0∑n+1n+1iBi,n+1Pi−1
将两式子合并最终结果为:
B n ( t ) = ∑ i = 0 n B i , n P i = ∑ i = 0 n + 1 ( n + 1 − i n + 1 P i + i n + 1 P i − 1 ) B i , n + 1 \mathbf{B_n(t)= \sum_{i=0}^{n} B_{i,n} P_i = \sum_{i = 0}^{n + 1}(\frac{n + 1 - i}{n + 1}P_i + \frac{i}{n + 1}P_{i - 1}) B_{i, n + 1}} Bn(t)=i=0∑nBi,nPi=i=0∑n+1(n+1n+1−iPi+n+1iPi−1)Bi,n+1
其中 P − 1 , P n + 1 \bm{P_{-1}},\bm{P_{n + 1}} P−1,Pn+1值随意,毕竟最后乘以系数后均为0
新的节点推导公式为:
P i ∗ = ( n + 1 − i n + 1 P i + i n + 1 P i − 1 ) \mathbf{P_i^* = (\frac{n + 1 - i}{n + 1}P_i + \frac{i}{n + 1}P_{i - 1})} Pi∗=(n+1n+1−iPi+n+1iPi−1)
CODE:
std::vector<Vector2> Render::RaiseDegree(const std::vector<Vector2>& ctl_vecs){
unsigned long new_degree = ctl_vecs.size();
std::vector<Vector2> new_ctl_vecs(new_degree + 1);
new_ctl_vecs[0] = ctl_vecs[0];
new_ctl_vecs[new_degree] = ctl_vecs[new_degree - 1];
for (int i = 1; i < new_degree; ++i) {
float t = 1.0 - static_cast<float>(i) / new_degree;
new_ctl_vecs[i] = interp(ctl_vecs[i - 1], ctl_vecs[i], t);
}
return new_ctl_vecs;
}
减少控制点去逼近原先曲线的表现
Degree reduction is the opposite of degree raising, is to find a curve defined by new control points with minimum error
根据升阶公式
P i ∗ = ( n − i n P i + i n P i − 1 ) \mathbf{P_i^* = (\frac{n - i}{n}P_i + \frac{i}{n}P_{i - 1})} Pi∗=(nn−iPi+niPi−1)
可以得到如下两个递推式
P i ′ = n P i ∗ − i P i − 1 ′ n − i i = 0 , 1 , 2... n − 1 P i − 1 ′ ′ = n P i ∗ − ( n − i ) P i ′ ′ i i = n , n − 1 , . . . 1 \mathbf{P_i' = \frac{nP_i^* - iP_{i - 1}'}{n - i} \qquad i = 0, 1, 2...n-1}\\ \ \\ \mathbf{P_{i - 1}'' = \frac{nP_i^* - (n - i)P_{i}''}{i} \qquad i=n, n-1,...1}\\ Pi′=n−inPi∗−iPi−1′i=0,1,2...n−1 Pi−1′′=inPi∗−(n−i)Pi′′i=n,n−1,...1
一个与起点重合,然后逐渐偏离;一个与终点重合,然后逐渐偏离
Then we have two kinds of degree reduction schemes
P i ^ = { P i ′ , i = 0 , 1 , . . . , [ n − 1 2 ] P i ′ ′ , i = [ n − 1 2 ] + 1 , . . . n − 1 \mathbf{\hat{P_i} = \left\{\begin{matrix} P_i' , & i = 0,1,..., \left [ \frac{n - 1}{2} \right ] \\ P_i'', & i = \left[\frac{n - 1}{2} \right ] + 1,...n - 1 \end{matrix}\right.} Pi^={Pi′,Pi′′,i=0,1,...,[2n−1]i=[2n−1]+1,...n−1
P i ^ = ( 1 − i n − 1 ) P i ′ + i n − 1 P i ′ ′ \mathbf{\hat{P_i} = (1 - \frac{i}{n - 1})P_i' + \frac{i}{n - 1}P_i''} Pi^=(1−n−1i)Pi′+n−1iPi′′
P n ( t ) = ∑ i = 0 n P i N i , k ( t ) t ∈ [ 0 , 1 ] P_n(t) = \sum_{i=0}^{n}P_iN_{i, k}(t)\\ \ \\ t \in [0, 1]\\ Pn(t)=i=0∑nPiNi,k(t) t∈[0,1]
样条(Spline)其实是一种在造船和工程制图时用来画出光滑形状的工具。样条是一根柔软但有弹性的长条物,有些像尺子。将两端和几个点用钉子固定之后,便可以产生顺滑的曲线。
N i , 1 = { 1 t i < x < t i + 1 0 o t h e r w i s e N i , k = t − t i t i + k − 1 − t i N i , k − 1 ( t ) + t i + k − t t i + k − t i + 1 N i + 1 , k − 1 ( t ) \mathbf{N_{i,1} = \left\{\begin{matrix} 1 & t_i < x < t_{i + 1}\\ 0 & otherwise \end{matrix}\right.}\\ \ \\ \mathbf{N_{i,k} = \frac{t - t_i}{t_{i+k-1} - t_i}N_{i, k-1}(t) + \frac{t_{i+k} - t}{t_{i+k} - t_{i+1}}N_{i+1, k-1}(t)}\\ Ni,1={10ti<x<ti+1otherwise Ni,k=ti+k−1−tit−tiNi,k−1(t)+ti+k−ti+1ti+k−tNi+1,k−1(t)
我们让 t 0 = 0 , t 1 = 1 , t 2 = 2 , t 3 = 3 t_0 = 0,t_1 = 1,t_2 = 2, t_3 = 3 t0=0,t1=1,t2=2,t3=3,则 N 0 , 1 , N 1 , 1 , N 2 , 1 N_{0, 1}, N_{1, 1}, N_{2, 1} N0,1,N1,1,N2,1如下图所示:
递推关系如下(此图为wiki上的,用的是次-degree,而上面的公式用的是阶-order,degree=order-1):
[ t k − 1 , t n + 1 ] [t_{k-1},t_{n+1}] [tk−1,tn+1]区间内有相应数量的基函数,该区间才有意义:
节点成等差数列均匀分布
让起点和终点都具有K的重复度,使得曲线从起点开始,到终点结束