OpenGL绘制B样条曲线

B样条

B样条曲线(B-spline curves)是一种基于样条函数(spline function)的分段多项式函数,广泛用于计算机图形学、数据拟合和数值分析中。B样条曲线有许多优点,如局部控制、平滑性和灵活性。

1. 基本概念

1.1 节点向量( Knot Vector )

节点向量是一个非递减序列 { t i } \{t_i\} {ti},其中 i i i 0 0 0 n + k + 1 n+k+1 n+k+1 n n n是控制点的数量, k k k是B样条曲线的阶数(degree)。节点向量决定了B样条基函数的定义域。

1.2 B样条基函数(Basis Functions)

B样条基函数 B i , k ( t ) B_{i,k}(t) Bi,k(t) 是分段多项式函数,它是定义B样条曲线的重要组成部分。基函数的定义是递归的:

  • 零阶基函数(piecewise constant): B i , 0 ( t ) = { 1 t i ≤ t < t i + 1 0 otherwise B_{i,0}(t) = \begin{cases} 1 & t_i \leq t < t_{i+1} \\ 0 & \text{otherwise} \end{cases} Bi,0(t)={10tit<ti+1otherwise
  • 递归定义高阶基函数: B i , k ( t ) = t − t i t i + k − 1 − t i B i , k − 1 ( t ) + t i + k − t t i + k − t i + 1 B i + 1 , k − 1 ( t ) B_{i,k}(t) = \frac{t - t_i}{t_{i+k-1} - t_i} B_{i,k-1}(t) + \frac{t_{i+k} - t}{t_{i+k} - t_{i+1}} B_{i+1,k-1}(t) Bi,k(t)=ti+k1tittiBi,k1(t)+ti+kti+1ti+ktBi+1,k1(t)
float B(int i, int k, float t, const std::vector<float>& knots) {
    if (k == 1) {
        return (knots[i] <= t && t < knots[i+1]) ? 1.0f : 0.0f;
    } else {
        float coeff1 = (t - knots[i]) / (knots[i + k - 1] - knots[i]);
        float coeff2 = (knots[i + k] - t) / (knots[i + k] - knots[i + 1]);
        return coeff1 * B(i, k - 1, t, knots) + coeff2 * B(i + 1, k - 1, t, knots);
    }
}

2. B样条曲线的数学表达式

给定一个控制点序列 { P i } \{P_i\} {Pi} 和一个节点向量 { t i } \{t_i\} {ti},B样条曲线 P ( t ) P(t) P(t) 的表达式为:
P ( t ) = ∑ i = 0 n P i B i , k ( t )      t ∈ [ t k − 1 , t n + 1 ] P(t)=\sum_{i=0}^{n}P_iB_{i,k}(t) \ \ \ \ t\in[t_{k-1},t_{n+1}] P(t)=i=0nPiBi,k(t)    t[tk1,tn+1]
其中:
P i P_i Pi是第 i i i个控制点
B i , k ( t ) B_{i,k}(t) Bi,k(t)是阶数为 k k k的第 i i i个B样条基函数

3. B样条的类型

  1. 均匀B样条曲线:当节点沿参数轴均匀等距分布,即 u u ui+1- u u ui=常数>0时,表示均匀B样条函数。
  2. 准均匀B样条曲线:与均匀B样条曲线的差别在于两端节点具有重复度k,这样的节点矢量定义了准均匀的B样条基。
  3. 分段Bezier曲线:节点矢量中两端节点具有重复度k,所有内节点重复度尾k-1,这样的节点矢量定义了分段的Bernestein基。B样条曲线用分段Bezier曲线表示后,各曲线段就具有了相对独立性。
  4. 非均匀B样条曲线:当节点沿参数轴的分布不等距,即 u u ui+1- u u ui ≠ \neq =常数时,表示非均匀B样条函数

4. 绘制代码

void BSplineCurve::calculateKnots()
{
    // 均匀B样条
    // 控制点个数 n+1
    // 阶数k
    int n = controlPoints.size() - 1;
    int k = degree;
    int m = n + k + 1;

    knots.resize(m + 1);

    // 有m+1个矢量结点
    for (int i = 0; i <= m; ++i)
    {
        knots[i] = float(i) / (m - degree + 1);
    }
}

float BSplineCurve::B(int i, int k, float t)
{
    if (k == 1)
        return (knots[i] <= t && t < knots[i + 1]) ? 1.0f : 0.0f;
    else
    {
        float coeff1 = (t - knots[i]) / (knots[i + k - 1] - knots[i]);
        float coeff2 = (knots[i + k] - t) / (knots[i + k] - knots[i + 1]);
        return coeff1 * B(i, k - 1, t) + coeff2 * B(i + 1, k - 1, t);
    }
}

void BSplineCurve::calculateBSplinePoints()
{
    int n = controlPoints.size() - 1;
    vertices.clear();

    for (int j = 0; j <= 64; ++j)
    {
        float t = (float)j / 64.0f * (knots[n + 1] - knots[degree]) + knots[degree];
        Vertex v;
        for (int i = 0; i <= n; ++i)
        {
            float basis = B(i, degree + 1, t);
            v.position += basis * controlPoints[i];
        }
        vertices.push_back(v);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ht巷子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值