OpenGl中的Nurbs B样条曲面绘制

NURBS

贝塞尔曲线的缺点是当我们增加很多控制点的时候,曲线变得不可控,其连续性会变差差。如果控制点很多(高阶曲线),当我们调整一个控制点的位置,对 整个曲线的影响是很大的。要获得更高级的控制,可以使用GLU库提供的NURBS(非均匀有理B样条)。通过这些函数我们可以在求值器中调整控制点的影响 力,在有大量控制点的情况下,依然可以产生平滑的曲线。

从贝塞尔到B样条

贝塞尔曲线由起点、终点和其他控制点来影响曲线的形状。在二次贝塞尔曲线和三次贝塞尔曲线中,可以通过调整控制点的位置而得到很好的平滑性(C2级 连续性 曲率级)的曲线。当增加更多的控制点的时候,这种平滑性就被破坏了。如下图所示,前两个曲线很平滑(曲率级的连续性),第三个曲线在增加了一个控制点之 后,曲线被拉伸了,其平滑性遭到了破坏。

  image

B样条的工作方式类似于贝塞尔曲线,但不同的是曲线被分成很多段。每段曲线的形状只受到最近的四个控制点的影响,这样曲线就像是4阶的贝塞尔曲线拼接起来的。这样很长的有很多控制点的曲线就会有固定的连续性,平滑性(每一段都是c2级的连续性)。

结点

NURBS(非均匀有理B样条)的真正威力在于,可以调整任意一段曲线中的四个控制点的影响力,来产生较好的平滑性。这是通过一系列结点来控制的。每个控制点都定义了两个结点的值。结点的取值范围是u或v的定义域,而且必须是非递减的。

结点的值决定了落在u、v参数定义域内的控制点的影响力。下图的曲线表示控制点对一条在u参数定义域内的具有四个单位的曲线的影响。下图表示中间点对曲线的影响更大,而且只有在[0,3]范围内的控制点才会对曲线产生影响。

image

在u、v参数定义域内的控制点对曲线的形状会有有影响,而且我们可以通过结点来控制控制点的影响力。非均匀性就是指一个控制点的影响力的范围是可以改变的。

以下内容及节选自 http://www.rhino3d.com/cn/nurbs

节点 ( Knot ) 是一个 ( 阶数 + N - 1 ) 的数字列表,N 代表控制点数目。有时候这个列表上的数字也称为节点矢量 ( Knot Vector ),这里的矢量并不是指 3D 方向。

节点列表上的数字必须符合几个条件,确定条件是否符合的标准方式是在列表上往下时,数字必需维持不变或变大,而且数字重复的次数不可以比阶数大。例 如,阶数 3 有 15 个控制点的 NURBS 曲线,列表数字为 0,0,0,1,2,2,2,3,7,7,9,9,9 是一个符合条件的节点列表。列表数字为 0,0,0,1,2,2,2,2,7,7,9,9,9 则不符合,因为此列表中有四个 2,而四比阶数大 ( 阶数为 3 )。

节点值重复的次数称为节点的重数 ( Multiplicity ),在上面例子中符合条件的节点列表中,节点值 0 的重数值为三;节点值 1 的重数值为一;节点值 2 的重数为三;节点值 7 的重数值为二;节点值 9 的重数值为三。如果节点值重复的次数和阶数一样,该节点值称为全复节点 ( Full-Multiplicity Knot )。在上面的例子中,节点值 0、2、9 有完整的重数,只出现一次的节点值称为单纯节点 ( Simple Knot ),节点值 1 和 3 为单纯节点。

如果在节点列表中是以全复节点开始,接下来是单纯节点,再以全复节点结束,而且节点值为等差,称为均匀 ( Uniform )。例如,如果阶数为 3 有 7 个控制点的 NURBS 曲线,其节点值为 0,0,0,1,2,3,4,4,4,那么该曲线有均匀的节点。如果节点值是 0,0,0,1,2,5,6,6,6 不是均匀的,称为非均匀 ( Non-Uniform )。在 NURBS 的 NU 代表“非均匀”,意味着在一条 NURBS 曲线中节点可以是非均匀的。

在节点值列表中段有重复节点值的 NURBS 曲线比较不平滑,最不平滑的情形是节点列表中段出现全复节点,代表曲线有锐角。因此,有些设计师喜欢在曲线插入或移除节点,然后调整控制点,使曲线的造型 变得平滑或尖锐。因为节点数等于 ( N + 阶数 - 1 ),N 代表控制点的数量,所以插入一个节点会增加一个控制点,移除一个节点也会减少一个控制点。插入节点时可以不改变 NURBS 曲线的形状,但通常移除节点必定会改变 NURBS 曲线的形状。

节点(Knot)与控制点

控制点和节点是一对一成对的是常见的错误概念,这种情形只发生在 1 阶的 NURBS ( 多重直线 )。较高阶数的 NURBS 的每 ( 2 x 阶数 ) 个节点是一个群组,每 ( 阶数 + 1 ) 个控制点是一个群组。例如,一条 3 阶 7 个控制点的 NURBS 曲线,节点是 0,0,0,1,2,5,8,8,8,前四个控制点是对应至前六个节点;第二至第五个控制点是对应至第二至第七个节点 0,0,1,2,5,8;第三至第六个控制点是对应至第三至第八个节点 0,1,2,5,8,8;最后四个控制点是对应至最后六个节点

创建NURBS表面

GLU库中提供了易用高级的绘制NURBS表面的函数。我们不需要显示地调用求值函数或建立网格。渲染一个NURBS表面的步骤如下:

  1. 创建一个NURBS渲染对象 gluNewNurbsRenderer()
  2. 调用相关的NURBS函数来修改曲线或曲面的外观 gluNurbsProperty
  3. 定义表面,渲染NURBS gluBeginSurface gluNurbsSurface gluEndSurface
  4. 销毁NURBS渲染对象 gluDeleteNurbsRenderer();

我们通过gluNewNurbsRenderer函数来为NURBS创建一个渲染器对象,在最后使用gluDeleteNurbsRenderer销毁它。代码如下:

// NURBS 对象指针 GLUnurbsObj * pNurb = NULL; ... ... // 创建NURBS对象 pNurb = gluNewNurbsRenderer(); ... if (pNurb) gluDeleteNurbsRenderer(pNurb);
NURBS属性

在创建了NURBS渲染器之后,我们需要设置NURBS的属性。

//设置采样容差

gluNurbsProperty(pNurb, GLU_SAMPLING_TOLERANCE, 25.0f);

//填充一个实体的表面

gluNurbsProperty(pNurb, GLU_DISPLAY_MODE, (GLfloat)GLU_FILL);

GLU_SAMPLING_TOLERANCE决定了网格的精细程度。GLU_FILL表示使用填充模式,相应的GLU_OUTLINE_POLYGON是线框模式。

定义表面

表面通过一组控制点和一个结点序列来定义。使用gluNurbsSurface函数来定义表面,这个函数要在gluBeginSurface和gluEndSurface中间:

// 渲染NURB // 开始NURB表面的定义 gluBeginSurface(pNurb); gluNurbsSurface(pNurb, // 指针指向NURBS渲染器 8 , Knots, // u定义域内的结点个数,以及结点序列 8 , Knots, // v定义域内的结点个数,以及结点序列 4 * 3 , // u方向上控制点的间隔 3 , // v方向上控制点的间隔 & ctrlPoints[ 0 ][ 0 ][ 0 ], // 控制点数组 4 , 4 , // u,v 的次数 GL_MAP2_VERTEX_3); // 表面的类型 // 完成定义 gluEndSurface(pNurb);
 
程序如下:
 1     float ctrlpoints[4][4][3]={   
 2         {{100.0,270.0,0.0},//p00   
 3         {105.0,180.0,0.0},//p01   
 4         {110.0,160.0,0.0},//p02   
 5         {155.0,100.0,0.0}},//p03   
 6         {{180.0,200.0,0.0},//p10   
 7         {190.0,130.0,0.0},//p11   
 8         {200.0,110.0,0.0},//p12   
 9         {240.0,70.0,0.0}},//p13   
10         {{310.0,200.0,0.0},//p20   
11         {320.0,130.0,0.0},//p21   
12         {330.0,110.0,0.0},//p22   
13         {370.0,70.0,0.0}},//p23   
14         {{420.0,270.0,0.0},//p30   
15         {430.0,180.0,0.0},//p31   
16         {440.0,160.0,0.0},//p32   
17         {490.0,120.0,1.0}}//p33   
18     };  
19 
20     glPushMatrix();
21     //绘制控制点与控制线
22     glScaled(0.2, 0.2, 0.2);
23 
24     glPointSize(4.0f); 
25     glColor3f(0.0, 0.0, 1.0);
26     glColor3f(0, 0, 1);
27     glBegin(GL_POINTS);
28     for (int i = 0; i < 4; i++)
29     {
30         for (int j = 0; j < 4; j++)
31             glVertex3fv(ctrlpoints[i][j]);
32 
33     }
34     glEnd();
35     //绘制控制线
36     glLineWidth(1.5f);
37     glColor3f(0.0,1.0,1.0); 
38     for (int i = 0; i < 4; i++)
39     {
40         glBegin(GL_LINE_STRIP);
41         for (int j = 0; j < 4; j++)
42             glVertex3fv(ctrlpoints[i][j]);
43         glEnd();
44 
45         glBegin(GL_LINE_STRIP);
46         for (int j = 0; j < 4; j++)
47             glVertex3fv(ctrlpoints[j][i]);
48         glEnd();
49     }
50     //绘制B样条控制曲面
51      GLfloat knots[8]={0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0}; //B样条控制向量
52     glLineWidth(1.0f);
53     glColor3f(0.0, 0.0, 0.0);
54     
55     gluNurbsProperty(pNurb,GLU_SAMPLING_TOLERANCE,25.0); //设置属性
56     gluNurbsProperty(pNurb,GLU_DISPLAY_MODE, GLU_OUTLINE_POLYGON); 
57     gluBeginSurface(pNurb);//开始绘制
58     gluNurbsSurface(pNurb, 
59         8,knots, 
60         8,knots, 
61         4*3, 
62         3, 
63         &ctrlpoints[0][0][0], 
64         4, 4, 
65         GL_MAP2_VERTEX_3);
66 
67     gluEndSurface(pNurb); //结束绘制
68 
69 for (int j = 0; j <= 8; j++) 70 { 71 glBegin(GL_LINE_STRIP); 72 for (int i = 0; i <= 30; i++) 73 glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0); 74 glEnd(); 75 glBegin(GL_LINE_STRIP); 76 for (int i = 0; i <= 30; i++) 77 glEvalCoord2f((GLfloat)j/8.0, (GLfloat)i/30.0); 78 glEnd(); 79 } 80 glPopMatrix();

  程序运行效果如下:

  

转载于:https://www.cnblogs.com/icmzn/p/5089185.html

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在OpenGL绘制B样条曲线,需要进行以下步骤: 1.定义控制点:B样条曲线是通过一组控制点定义的,因此需要定义控制点的坐标。 2.计算B样条基函数:B样条曲线是通过一组基函数计算得到的,因此需要先计算B样条基函数。 3.计算曲线上的点:根据控制点和B样条基函数,计算曲线上的点。 4.绘制曲线:通过OpenGL的绘图函数,将曲线上的点绘制出来。 下面是一个简单的OpenGL B样条曲线绘制的代码示例: ``` #include <GL/glut.h> #include <vector> std::vector<GLfloat> ctrlPoints = { -4.0, 0.0, 0.0, 1.0, -2.0, 4.0, 0.0, 1.0, 2.0,-4.0, 0.0, 1.0, 4.0, 0.0, 0.0, 1.0 }; GLfloat knots[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 }; GLUnurbsObj *theNurb; void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); theNurb = gluNewNurbsRenderer(); gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, 25.0); gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL); } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0); gluBeginCurve(theNurb); gluNurbsCurve(theNurb, 8, knots, 4, &ctrlPoints[0], 4, GL_MAP1_VERTEX_4); gluEndCurve(theNurb); glFlush(); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho(-5.0, 5.0, -5.0*(GLfloat)h / (GLfloat)w, 5.0*(GLfloat)h / (GLfloat)w, -5.0, 5.0); else glOrtho(-5.0*(GLfloat)w / (GLfloat)h, 5.0*(GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; } ``` 这个示例代码使用了OpenGLNURBS库来绘制B样条曲线。在`init`函数,我们使用`gluNewNurbsRenderer`函数创建了一个NURBS对象,并设置了一些属性。在`display`函数,我们使用`gluBeginCurve`函数开始绘制曲线,然后使用`gluNurbsCurve`函数计算曲线上的点,并使用`gluEndCurve`函数结束绘制。在`reshape`函数,我们设置了视口和投影矩阵。 注意,在实际应用,我们可能需要自己实现B样条基函数的计算,并使用OpenGL的绘图函数绘制曲线上的点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值