贝塞尔曲线和B样条

样条线(Spline)是一个广泛的概念,指的是由多段连续且平滑的函数组成的曲线。样条线一般用来通过一组点(称为节点)来插值,并保证曲线在节点之间光滑过渡。

贝塞尔曲线

请添加图片描述请添加图片描述

t 是一个从 0 到 1 的参数。每个 t t t对应一个向量。随着 t t t从 0 到 1 的变化,曲线上的点也随之变化。从而确定曲线。

仅通过首尾,复杂度高

任何一控制点的变化会引起曲线上所有点的改变,牵一发而动全身。所以,贝塞尔曲线是无法针对局部进行编辑的。

请添加图片描述

// 点=======================================================
QVector<QVector3D> x{ {1,1,0.1},{1,-1,0.2},{-1,-1,0.3},{-1,1,0.4},{1,1,0.1},{1,-1,0.5},{-1,-1,0.6},{-1,1,0.7} };
pushObject(new Spline(x));

// 计算贝塞尔曲线=======================================================
	// 计算组合数(n 选 i)
    int binomialCoefficient(int n, int i)
    {
        int res = 1;
        for (int j = 0; j < i; ++j) {
            res *= (n - j);
            res /= (j + 1);
        }
        return res;
    }
    // 计算n次贝塞尔曲线的点(三维)
    QVector3D bezierN(const QVector<QVector3D>& controlPoints, double t)
    {
        int n = controlPoints.size() - 1;
        QVector3D point(0.0, 0.0, 0.0); // 用来存储计算结果
        // 贝塞尔曲线的求和公式
        for (int i = 0; i <= n; ++i) {
            int binom = binomialCoefficient(n, i);
            double weight = binom * pow(1 - t, n - i) * pow(t, i);
            point += weight * controlPoints[i];
        }
        return point;
    }
    // 计算n次贝塞尔曲线上的多个点(三维)
    QVector<QVector3D> calculateBezierCurve(const QVector<QVector3D>& controlPoints, int numPoints)
    {
        QVector<QVector3D> curvePoints;
        // 计算曲线上的多个点
        for (int i = 0; i <= numPoints; ++i) {
            double t = i / (double)numPoints; // t 值的变化
            QVector3D point = bezierN(controlPoints, t); // 计算当前t值对应的贝塞尔点
            curvePoints.append(point);
        }
        return curvePoints;
    }

// 绘制=======================================================
    void Spline::initialize()
    {
        QOpenGLFunctions* functions = QOpenGLContext::currentContext()->functions();
        vao.create();
        vbo.create();
        vao.bind();
        vbo.bind();
        vertexData = calculateBezierCurve(points, 100);
        vbo.allocate(vertexData.data(), 3*vertexData.size() * sizeof(float));
        functions->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        functions->glEnableVertexAttribArray(0);
        vbo.release();
        vao.release();
    }

    void Spline::draw()
    {
        QOpenGLFunctions* functions = QOpenGLContext::currentContext()->functions();
        shaderProgram->bind();
        vao.bind();
        vbo.bind();
        functions->glDrawArrays(GL_LINE_STRIP, 0, vertexData.size());
        vbo.release();
        vao.release();
        shaderProgram->release();
    }

B-spline

B样条是样条曲线的一种特殊表达形式,是B-样条基函数的线性组合,是贝塞尔曲线的一般化。

这是个分段函数,这些函数在他们的衔接处有连续性。

请添加图片描述请添加图片描述

    // Cox-de Boor算法,递归计算B样条基函数值
    double coxDeBoor(const QVector<double>& knots, int i, int p, double t)
    {
        // 基本情况
        if (p == 0) {
            if (knots[i] <= t && t < knots[i + 1]) {
                return 1.0;
            }
            else {
                return 0.0;
            }
        }

        double left = 0.0, right = 0.0;
        if (i + p < knots.size() && knots[i + p] != knots[i]) {
            left = (t - knots[i]) / (knots[i + p] - knots[i]) * coxDeBoor(knots, i, p - 1, t);
        }

        if (i + p + 1 < knots.size() && knots[i + p + 1] != knots[i + 1]) {
            right = (knots[i + p + 1] - t) / (knots[i + p + 1] - knots[i + 1]) * coxDeBoor(knots, i + 1, p - 1, t);
        }

        return left + right;
    }

    // 计算B样条曲线上的点, p是B样条的阶数
    QVector<QVector3D> calculateBSpline(const QVector<QVector3D>& controlPoints, int p, int numPoints)
    {
        QVector<QVector3D> curvePoints;
        int n = controlPoints.size() - 1;  // 控制点的数量
        int m = n + p + 1;  // 节点矢量的长度

        // 计算节点矢量 (Uniform Knot Vector, 简单的均匀节点矢量)
        QVector<double> knots(m);
        for (int i = 0; i < m; ++i) {
            if (i <= p) {
                knots[i] = 0.0;
            }
            else if (i >= m - p) {
                knots[i] = 1.0;
            }
            else {
                knots[i] = (i - p) / (double)(n - p + 1);
            }
        }

        // 计算曲线上的多个点
        for (int i = 0; i < numPoints; ++i) {
            double t = i / (double)numPoints;
            QVector3D point(0.0, 0.0, 0.0);

            // 对于每个控制点,使用Cox-de Boor公式计算B样条值并加权
            for (int j = 0; j <= n; ++j) {
              double weight = coxDeBoor(knots, j, p, t);
              point += weight * controlPoints[j];
            }

            curvePoints.append(point);
        }

        return curvePoints;
    }

参考:

https://frontzhm.github.io/2017/07/07/bezier/

https://zhuanlan.zhihu.com/p/539808761

https://zhuanlan.zhihu.com/p/672199076

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值