Bezier曲线
1. Bezier曲线的定义
贝塞尔曲线是通过一组控制点
{
P
0
,
P
1
,
.
.
.
,
P
n
}
\{P_0,P_1,...,P_n\}
{P0,P1,...,Pn}来定义的,其中
n
n
n是曲线的阶数。Bezier曲线的数学表达式:
P
(
t
)
=
∑
i
=
0
n
P
i
B
i
,
n
(
t
)
t
∈
[
0
,
1
]
P\left(t\right)=\sum_{i=0}^{n}P_{i}B_{i,n}\left(t\right)\quad t\in\left[\begin{matrix}0,1\end{matrix}\right]
P(t)=∑i=0nPiBi,n(t)t∈[0,1]
其中
B
i
,
n
(
t
)
B_{i,n}(t)
Bi,n(t)是伯恩斯坦多项式
2. 伯恩斯坦多项式
B i , n ( t ) = C n i t i ( 1 − t ) n − i = n ! i ! ( n − i ) ! t i ( 1 − t ) n − i ( i = 0 , 1 , . . . . n ) B_{i,n}\left(t\right)=C_{n}^{i}t^{i}\left(1-t\right)^{n-i}=\frac{n!}{i!\left(n-i\right)!}t^{i}\left(1-t\right)^{n-i}\quad\left(i=0,1,....n\right) Bi,n(t)=Cniti(1−t)n−i=i!(n−i)!n!ti(1−t)n−i(i=0,1,....n)
float binomialCoefficient(int n, int k)
{
// n! / (i! * (n - i)!)
if (k > n)
return 0;
if (k == 0 || k == n)
return 1;
float coeff = 1.0f;
for (int i = 1; i <= k; ++i)
{
coeff *= (n - (k - i));
coeff /= i;
}
return coeff;
}
float bezierBasis(int n, int i, float t)
{
// binomialCoefficient(n, i) * pow(t, i) * pow(1 - t, n - i)
return binomialCoefficient(n, i) * pow(t, i) * pow(1 - t, n - i);
}
3. Bezier曲线的性质
- 起点和终点:贝塞尔曲线的起点和终点分别是控制点的第一个和最后一个
- 端点切线:曲线在起点和终点的切线方向由第一个和第二个控制点,以及倒数第二个和最后一个控制点决定。
- 凸包性质:贝塞尔曲线完全位于其控制点的凸包内。
- 参数连续性:贝塞尔曲线在整个参数范围内是连续且平滑的。
4. Bezier曲线的递归定义
贝塞尔曲线还可以用德卡斯特里奥算法(De Casteljau’s algorithm)递归地计算。对于给定的控制点集
{
P
0
,
P
1
,
.
.
.
P
n
}
\{P_0,P_1,...P_n\}
{P0,P1,...Pn}和参数
t
t
t,递归算法定义如下:
- 初始控制点: P i ( 0 ) P_i^{(0)} Pi(0)= P i P_i Pi
- 递归计算:
P i ( r ) = ( 1 − t ) P i ( r − 1 ) + t P i + 1 ( r − 1 ) , 其 中 r = 1 , 2 , . . . , n P_i^{(r)}=(1-t)P_i^{(r-1)} +tP_{i+1}^{(r-1)},其中r=1,2,...,n Pi(r)=(1−t)Pi(r−1)+tPi+1(r−1),其中r=1,2,...,n
最终得到Bezier曲线上的点为 P 0 ( n ) ( t ) P_0^{(n)}(t) P0(n)(t)
glm::vec3 Helper(std::vector<glm::vec3> cp, float t)
{
if (cp.size() == 1)
return cp[0];
std::vector<glm::vec3> newCp;
// 新的控制点
for (int i = 1; i < cp.size(); ++i)
{
newCp.push_back(cp[i - 1] * t + cp[i] * (1 - t));
}
return Helper(newCp, t);
}
5. OpenGL代码
void BezierCurve::CalculateVertices()
{
vertices.clear();
for (int i = 0; i <= 64; ++i)
{
float t = (float)i / 64.0f;
Vertex vertex;
vertex.position = Helper(controlPoints, t);
vertices.push_back(vertex);
}
// 设置数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
}
glm::vec3 BezierCurve::Helper(std::vector<glm::vec3> cp, float t)
{
if (cp.size() == 1)
return cp[0];
std::vector<glm::vec3> newCp;
// 新的控制点
for (int i = 1; i < cp.size(); ++i)
{
newCp.push_back(cp[i - 1] * t + cp[i] * (1 - t));
}
return Helper(newCp, t);
}