|
常见的坐标系 :
直角坐标系(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(&#34;(%lf,%lf)\n&#34;,&Rx[0],&Ry[0]);
xx=Rx[0];
yy=Ry[0];
}
scanf(&#34;%lf&#34;,&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(&#34;%lf,%lf\n&#34;,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(&#34;(%lf,%lf)\n&#34;,Rx[0],Ry[0]);
}
//printf(&#34;\n&#34;);
glEnd();
glBegin(GL_LINE_STRIP);
for(int i=0;i<n;i++)
{
glVertex3f(Rx[n-1-i],Ry[n-i-1],0.0f);
//printf(&#34;(%lf,%lf)\n&#34;,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(&#34;hdbone_three_bezier_curve&#34;);
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(&#34;%lf,%lf\n&#34;,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 &#39;1&#39;:
show_controlpoint=-show_controlpoint;
break;
case &#39;2&#39;:
show_curve=-show_curve;
break;
case &#39;3&#39;:
show_regression_point=-show_regression_point;
break;
case &#39;0&#39;:
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(&#34;Hdbone_bezier&#34;);
glutDisplayFunc(display);
glutMouseFunc(mouse_hit);
glutKeyboardFunc(myKeyBoard);
glutMainLoop();
}
- 按下按键 &#39; 1 &#39;,是否显示控制点连成的折线
- 按下按键 &#39; 2 &#39; , 是否显示Bezier 曲线
- 按下按键 &#39; 3 &#39; ,是否显示迭代的点组成的折线
- 按下按键 &#39; 0 &#39; , 表示删除上一次绘制的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- 样条曲线的局部性质 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|