OpenGL绘制Bezier曲面

Bezier的定义

贝塞尔曲面是贝塞尔曲线在二维上的扩展。它由一组控制点定义,通过这些控制点生成光滑的曲面。贝塞尔曲面通常用两个参数 u u u v v v来表示,这两个参数的取值范围都在 [0, 1] 之间。
在这里插入图片描述

数学表示

P ( u , v ) = ∑ i = 0 n ∑ j = 0 m p i j ⋅ B i , n ( u ) ⋅ B j , m ( v ) P(u,v)=\sum_{i=0}^n\sum_{j=0}^m p_{ij}\cdot B_{i,n}(u)\cdot B_{j,m}(v) P(u,v)=i=0nj=0mpijBi,n(u)Bj,m(v)
其中:
P ( u , v ) P(u,v) P(u,v)是曲面上的一个点。
P i , j P_{i,j} Pi,j是控制点矩阵中的一个控制点。
B i , n ( u ) B_{i,n}(u) Bi,n(u) B j , m ( v ) B_{j,m}(v) Bj,m(v)是bezier基函数,它们的定义是:
B i , n ( u ) = C n i u i ( 1 − u ) n − i = n ! i ! ( n − i ) ! u i ( 1 − u ) n − i ( i = 0 , 1 , . . . . n ) B_{i,n}\left(u\right)=C_{n}^{i}u^{i}\left(1-u\right)^{n-i}=\frac{n!}{i!\left(n-i\right)!}u^{i}\left(1-u\right)^{n-i}\quad\left(i=0,1,....n\right) Bi,n(u)=Cniui(1u)ni=i!(ni)!n!ui(1u)ni(i=0,1,....n)
B j , m ( v ) = C v j v j ( 1 − v ) m − j = m ! j ! ( m − j ) ! v j ( 1 − v ) m − j ( j = 0 , 1 , . . . . n ) B_{j,m}\left(v\right)=C_{v}^{j}v^{j}\left(1-v\right)^{m-j}=\frac{m!}{j!\left(m-j\right)!}v^{j}\left(1-v\right)^{m-j}\quad\left(j=0,1,....n\right) Bj,m(v)=Cvjvj(1v)mj=j!(mj)!m!vj(1v)mj(j=0,1,....n)

控制点

控制点矩阵定义了贝塞尔曲面的形状。控制点的个数决定了曲面的阶数(degree)。例如,一个四阶的贝塞尔曲面需要 5 × 5 5×5 5×5个控制点。通过移动控制点,可以调整贝塞尔曲面的形状。

特性

● 局部控制:移动一个控制点只会影响该点附近的曲面区域,而不会影响整个曲面。
● 多项式性质:贝塞尔曲面的每个参数方向上都是贝塞尔多项式的线性组合。
● 仿射不变性:贝塞尔曲面在进行仿射变换(例如平移、旋转、缩放)时,其形状不变。

应用

贝塞尔曲面广泛应用于各种设计和动画中,包括:
● 汽车和飞机设计:贝塞尔曲面用于设计光滑的车身和机翼。
● 动画和特效:用于生成复杂的角色和场景。
● 3D建模:在3D建模软件中,贝塞尔曲面常用于创建复杂的曲面模型。

OpenGL代码

BezierSurface::BezierSurface(std::vector<std::vector<glm::vec3>> controlPoints) : Object()
{
    this->controlPoints = controlPoints;

    CalculateVertices();

    SetData();
}

float BezierSurface::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 BezierSurface::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);
}

Vertex BezierSurface::CalculatePoint(float u, float v)
{
    int m = controlPoints.size() - 1;
    int n = controlPoints[0].size() - 1;
    Vertex vertex;
    glm::vec3 point(0.0f);
    glm::vec2 tex(0.0f);
    for (int i = 0; i <= m; ++i)
    {
        for (int j = 0; j <= n; ++j)
        {
            float basisU = bezierBasis(m, i, u);
            float basisV = bezierBasis(n, j, v);
            point += controlPoints[i][j] * basisU * basisV;
        }
    }
    vertex.position = point;
    tex.x = u;
    tex.y = 1.0F - v;
    vertex.texCoord = tex;
    return vertex;
}

void BezierSurface::CalculateVertices()
{
    vertices.clear();
    std::vector<Vertex> tvs;
    // 计算顶点
    for (int i = 0; i <= 100; ++i)
    {
        for (int j = 0; j <= 100; ++j)
        {
            float u = (float)i / 100.0f;
            float v = (float)j / 100.0f;
            Vertex vertex = CalculatePoint(u, v);
            vertices.push_back(vertex);
        }
    }

    // 索引
    indices.resize(100 * 100 * 6 * sizeof(unsigned int));
    // 计算索引
    for (int i = 0; i < 100; ++i)
    {
        for (int j = 0; j < 100; ++j)
        {
            int index = i * 101 + j;
            indices.push_back(index);
            indices.push_back(index + 1);
            indices.push_back(index + 101);

            indices.push_back(index + 1);
            indices.push_back(index + 101);
            indices.push_back(index + 102);
        }
    }

    // 计算法线
    CalculateNormal();
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ht巷子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值