OpenGL绘制Bezier曲线

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(1t)ni=i!(ni)!n!ti(1t)ni(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曲线的性质

  1. 起点和终点:贝塞尔曲线的起点和终点分别是控制点的第一个和最后一个
  2. 端点切线:曲线在起点和终点的切线方向由第一个和第二个控制点,以及倒数第二个和最后一个控制点决定。
  3. 凸包性质:贝塞尔曲线完全位于其控制点的凸包内。
  4. 参数连续性:贝塞尔曲线在整个参数范围内是连续且平滑的。

4. Bezier曲线的递归定义

贝塞尔曲线还可以用德卡斯特里奥算法(De Casteljau’s algorithm)递归地计算。对于给定的控制点集
{ P 0 , P 1 , . . . P n } \{P_0,P_1,...P_n\} {P0,P1,...Pn}和参数 t t t,递归算法定义如下:

  1. 初始控制点: P i ( 0 ) P_i^{(0)} Pi(0)= P i P_i Pi
  2. 递归计算:
    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)=(1t)Pi(r1)+tPi+1(r1)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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ht巷子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值