找回密码
 立即注册
查看: 282|回复: 0

计算机图形学 几何表示与Bezier 曲线

[复制链接]
发表于 2022-4-29 14:50 | 显示全部楼层 |阅读模式
常见的坐标系 :

直角坐标系(x,y,z),球坐标系(r, ),柱坐标系 ( )



直角坐标系



球面坐标系



直角坐标和柱坐标

直角坐标系分类 :



右手坐标系和左手坐标系

几何场景 :定义在一个世界坐标系中,包含坐标系中的所有物体
世界坐标系:

  • 描述物体在场景中的相对位置和各自的形状,需要建立一个统一的整体坐标系
局部坐标系:

  • 有利于表达各个物体的内在几何特征,物体的几何表示可以表示为较为简单的形式
  • 容易实现物体绕自身轴线的旋转和各种调节形状的缩放变换
单位球面 (局部坐标系和世界坐标系)



球面坐标系

相互转换:线性变换,平移,旋转,剪切,放缩



局部坐标系和世界坐标系



局部坐标系中的物体进行变换更简单

多边型表示

对于数学公式难以描述的物体,需要用大量的平面片来逼近其外形
平面片 :三角型和四边型 (可以表示任意复杂的物体)



三角形表示的海豚



野鸭模型的多边形表示

构造方式 :三维数字化仪(原始数据为三维空间中的点集)(采用三维重建算法得到的多边形表示)
或者三维激光测距装置
采用解析公式精确表示的表面 :参数曲面,细分曲面,隐式曲面



参数曲面的多边形逼近



细分曲面的多边形逼近



隐式曲面的多边形逼近

通过控制逼近的精度,对表面进行采样,可以建立起满足给定精度的曲面物体的多边形逼近表示
多变形表示OBJ格式:

顶点表( )(每一个顶点的三维空间坐标)



顶点坐标表

纹理表(
(存储多边形表面的每一个顶点在相应纹理图像平面上的坐标(x,y))(确定顶点的颜色等材质属性)



纹理坐标表

法向表 (
每个顶点的法向都取该面片的法向,则绘制生成的物体棱角分明
如果每个顶点处的法向取周围面片法向的平均,则生成光滑的表面
面片表 (
存储了物体表面上的所有面片,每一面片记录有指向该 面片的各顶点,顶点的纹理坐标和法向的指针



法向表和面表

半边结构 :

Half-Edge Structure : 可定向的二维流形及其子集



半边结构



半边结构



半边结构举例



半边结构举例

优势 : 查询时间 O (1) ,操作时间 通常为O(1)
缺点 :只能表示可定向流形,信息冗余
多边形表示的优势




优势

多变形表示的缺点




缺点

参数曲线曲面

直线段的参数表示 :
直线段 ,直线段
则点R(t)可以表示为: (t在0-1范围内),并且



直线段的分量表示

三维参数形式为:R(t) = (x(t), y(t), z(t))



参数曲线

即:参数空间的每一个t表示直线段上的每一个点R(t)。



直线段

双线性四边面片




曲面参数



参数范围

四边面片的四个顶点P0、P1、P2和P3对应于参数曲面的四个角点R(0, 0)、R(1, 0)、R(1, 0)和R(0, 1)



双线性四边面片

空间参数曲面:R(u,v) = (x(u,v), y(u,v), z(u,v))
即:参数空间中每一点(u,v)对应于曲面上一点R(u,v)
三维参数曲线形式 :R(t)=(x(t),y(t),z(t))



参数曲面

Bezier 曲线




三次 Bezier 曲线

n次Bézier曲线 :



(这里的R(t)是一个点函数,是关于 t 的函数!!!)
(R_i 是控制点,是点的坐标)
其中 称为Bernstein基函数




性质 :

端点插值
端点切向




对称性 :
曲线的控制顶点的几何地位是对称的  
凸包性:曲线位于绘制多边形的凸包内



Bezier 曲线的凸包性

几何不变性:其形状仅仅与控制多边形有关,与坐标系无关
Bezier曲线采用递归剖分控制多边形的方法生成,这一剖分算法被称作de Cateljau算法。
每一次都遍历一遍相邻的控制点,根据给定参数在两相邻控制点形成的线段上取一个新的控制点。
每遍历一遍当前的控制点后生成的新控制点会更加逼近Bezier曲线,且控制点的数量会减1,当最后只剩一个控制点时,可以将这个控制点视为Bezier曲线上的点。
如果我们取不同的t,就能够进行多次剖分,最终生成足够多的点去绘制一个曲线,这个曲线就近似为Bezier,单次剖分算法过程描述如下:



Bezier 曲线剖分算法描述



t=0.5 时的Bezier 曲线剖分

等边三角形的形成图 :



t0 为0.5 的条件下Bzier曲线的剖分图

剖分生成的曲线 (每次剖分,曲线会被分成两段新的Bezier曲线):





Bezier

Bezier的不足 :



Bezier的缺点

Bezier 曲线的习题 :
输入 :3 次Bezier曲线C(t)  的四个控制点坐标 ,i=0,1,2,3,
输出 :C(t) 在t0 处分裂为两短C1(t)和 C2(t),求对应两端曲线对应Bezier 形式的控制点,并显示三条对应的Bezier曲线和对应的控制多边形



输入示例



输出示例

参考代码 :
#include <GL/glut.h>
#include <stdio.h>
#include <math.h>
#define n 4 // 4 个顶点
#define N 10000.0  // 绘制曲线的线段数
int width=600;
int height=600;
double xx[n];
double yy[n];// 原始的点坐标
double Rx[n][n];
double Ry[n][n];
double t0;
double frac(double x)
{
    return x>1?x*frac(x-1):1;
}// 阶乘
double Com(double x,double m)
{
    return frac(x)/frac(m)/frac(x-m);
}// 组合数
double R(double t,double* x)
{
    double ret=0;
    for(int i=0;i<n;i++)
    {
        ret+=x*(Com(n-1,i)*pow(1-t,n-i-1)*pow(t,i));
    }
    return ret;
}// R(t)
void input()
{
    for(int i=0;i<n;i++)
    {
        scanf("(%lf,%lf)\n",&Rx[0],&Ry[0]);
        xx=Rx[0];
        yy=Ry[0];
    }
    scanf("%lf",&t0);
}
void bezier()
{
    for(int i=1;i<n;i++)
    {
        for(int j=0;j<n-i;j++)
        {
            Rx[j]=(1-t0)*Rx[i-1][j]+t0*Rx[i-1][j+1];
            Ry[j]=(1-t0)*Ry[i-1][j]+t0*Ry[i-1][j+1];
            //printf("%lf,%lf\n",Rx[j],Ry[j]);
        }
    }
}
void drawControlPoint()
{
    glColor3f(1.0f,0.0f, 0.0f);// 红色
    GLfloat curSizeLine=8;
    glLineWidth(curSizeLine);
    glBegin(GL_LINE_STRIP);
    for(int i=0;i<n;i++)
    {
        glVertex3f(Rx[0],Ry[0],0.0f);
        //printf("(%lf,%lf)\n",Rx[0],Ry[0]);
    }
    //printf("\n");
    glEnd();
    glBegin(GL_LINE_STRIP);
    for(int i=0;i<n;i++)
    {
        glVertex3f(Rx[n-1-i],Ry[n-i-1],0.0f);
        //printf("(%lf,%lf)\n",Rx[n-1-i],Ry[n-i-1]);
    }
    glEnd();
}
void reshape(GLsizei w,GLsizei h)
{
    if (h==0) {
        h=1;
    }
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
   
    if (w<=h) {
       glOrtho (0.0f,250.0f, 0.0f,250.0f*h/w, 1.0f, -1.0f);
    }else
    {
       glOrtho (0.0f,250.0f*w/h, 0.0f,250.0f, 1.0f, -1.0f);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    // 画控制点
    drawControlPoint();
    //设置当前绘图使用的颜色
    glColor3f(0.0f,0.0f, 0.0f);// 黑色的曲线
    // 开始画连续的直线
    GLfloat curSizeLine=5;
    glLineWidth(curSizeLine);
    glBegin(GL_LINE_STRIP);
   
    for(int i=0;i<=N;i++)
    {
        glVertex3f(R(i/N,xx),R(i/N,yy),0.0f); // 取遍所有的t (t在0到1之间)
    }
    //glVertex3f(Rx[3][0]+i*dx,Ry[3][0]+i*dy,0.0f);
    glEnd();
    glFlush();
}

int main(int argc,char**argv)
{
    input();
    bezier();

    glutInit(&argc,argv);
    glutInitDisplayMode (GLUT_SINGLE |GLUT_RGB |GLUT_DEPTH);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(width,height);
    glutCreateWindow("hdbone_three_bezier_curve");

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glClearColor(1.0f,1.0f, 1.0f,1.0f);
    glutMainLoop();

}编译,链接,并运行指令 (不要忘记链接math.h):
gcc -o demo1 demo1.c -lGL -lGLU -lglut -lm && ./demo1测试样例 :



测试样例



测试结果

这里给出改进后的代码(用鼠标和键盘控制输入):(Bezier.c)

#include <GL/glut.h>
#include <stdio.h>
#include <math.h>
#define N 10000.0  // 绘制曲线的线段数
#define Maxn 1000  // 最多1000个顶点

int width=1000;
int height=600;
int show_regression_point=0;
int show_controlpoint=1;
int show_curve=0;
int n=0; // 点的数目
int flag=0;
double xx[Maxn];
double yy[Maxn];// 原始的点坐标
double Rx[Maxn][Maxn];
double Ry[Maxn][Maxn];
double t0=0.5;
int button_kind = -1;
double frac(double x)
{
    return x>1?x*frac(x-1):1;
}// 阶乘
double Com(double x,double m)
{
    return frac(x)/frac(m)/frac(x-m);
}// 组合数
double R(double t,double* x)
{
    double ret=0;
    for(int i=0;i<n;i++)
    {
        ret+=x*(Com(n-1,i)*pow(1-t,n-i-1)*pow(t,i));
    }
    return ret;
}// R(t)
void bezier()
{
    for(int i=1;i<n;i++)
    {
        for(int j=0;j<n-i;j++)
        {
            Rx[j]=(1-t0)*Rx[i-1][j]+t0*Rx[i-1][j+1];
            Ry[j]=(1-t0)*Ry[i-1][j]+t0*Ry[i-1][j+1];
            //printf("%lf,%lf\n",Rx[j],Ry[j]);
        }
    }
}
void display()
{
        // 清除屏幕
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        //重新设置OpenGL窗口:原点位置为左上角,x轴从左到右,y轴从上到下,坐标值与像素坐标值相同
        glViewport(0, 0, (GLsizei)width, (GLsizei)height);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(0, width, height, 0);

    if(show_controlpoint==1){
        glPointSize(2);
        glBegin(GL_LINE_STRIP);
        glColor3f(1.0f, 1.0f, 1.0f);        
        for(int i=0;i<n;i++)
        {
            glVertex2i(xx,yy);
        }
        glEnd();
    }
    if (flag){
        bezier();
    }
    if (show_curve==1){
        glColor3f(1.0f,0.0f, 0.0f);// 红色的曲线
        // 开始画连续的直线
        GLfloat curSizeLine=2;
        glLineWidth(curSizeLine);
        glBegin(GL_LINE_STRIP);

        for(int i=0;i<=N;i++)
        {
            glVertex3f(R(i/N,xx),R(i/N,yy),0.0f);
        }
        glEnd();
    }
    if (show_regression_point==1)
    {
        glColor3f(0.0f,1.0f, 1.0f);// 蓝色
        GLfloat curSizeLine=2;
        glLineWidth(curSizeLine);

        glBegin(GL_LINE_STRIP);
        for(int i=0;i<n;i++)
        {
            glVertex3f(Rx[0],Ry[0],0.0f);
        }
        glEnd();

        glBegin(GL_LINE_STRIP);
        for(int i=0;i<n;i++)
        {
            glVertex3f(Rx[n-1-i],Ry[n-i-1],0.0f);
        }
        glEnd();
    }
        //双缓存交换缓存以显示图像
        glutSwapBuffers();
        //每次更新显示
        glutPostRedisplay();
}

void mouse_hit(int button, int state, int x, int y)
{
        //鼠标操作种类赋值
        button_kind = button;

        //鼠标操作基本结构
        switch (button)
        {
        case GLUT_LEFT_BUTTON:        //左键操作,也可为数字0
                if (state == GLUT_DOWN)        //左键按下时
                {
                        //记录按键位置
                        Rx[0][n]=x;
            Ry[0][n]=y;
            xx[n]=Rx[0][n];
            yy[n]=Ry[0][n];
            n++;
                }
                break;
        case GLUT_RIGHT_BUTTON:        //右键操作,也可为数字1
                if (state == GLUT_DOWN)        //右键按下时
                {
                        flag=1;
            show_curve=1;
            show_regression_point=1;
                }
                break;
        default:
                break;
        }
}
void myKeyBoard(unsigned char key,int x,int y)
{
    switch(key){
        case '1':
            show_controlpoint=-show_controlpoint;
            break;
        case '2':
            show_curve=-show_curve;
            break;
        case '3':
            show_regression_point=-show_regression_point;
            break;
        case '0':
            n=0;
            flag=0;
            show_curve=0;
            show_regression_point=0;
            break;
    }
}
int main(int argc,char**argv)
{
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
        glutInitWindowSize(width, height);
        glutInitWindowPosition(100, 100);
        glutCreateWindow("Hdbone_bezier");
        glutDisplayFunc(display);
        glutMouseFunc(mouse_hit);
        glutKeyboardFunc(myKeyBoard);
        glutMainLoop();
}

  • 按下按键  ' 1 ',是否显示控制点连成的折线
  • 按下按键  ' 2 '  , 是否显示Bezier 曲线
  • 按下按键  ' 3 '   ,是否显示迭代的点组成的折线
  • 按下按键  ' 0 '  , 表示删除上一次绘制的Bezier曲线
编译指令 :gcc -o Bezier Bezier.c -lGL -lGLU -lglut -lm && ./Bezier

B- 样条曲线  (B-Spline)

B- 样条曲线是分段连续的多项式曲线,定义和节点向量有关



三次(四阶)B-样条曲线

定义在节点向量 { } 的k次(k+1阶),具有(n+1)个控制顶点的B-样条曲线:


如上面的三次(四阶)B- 样条曲线 : 有四个顶点,三次函数,8个控制顶点

是控制顶点,{  }   顺次连接称为曲线的控制多边形



B- 样条基函数

B- 样条曲线具有凸包性和几何不变性




曲线的两个端节点的重复度是k+!



三次B- 样条曲线的局部性质

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2025-5-3 17:04 , Processed in 0.543870 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表