NURBS,Non-Uniform Rational B-Splines(非均匀有理B样条),以数学的方式精确的描叙所有的造型,从简单的2D线、圆、圆弧与线条到复杂的3D有机自由曲面与实体。由于它的灵活性与精确性可以应用到从草图到动画到加工的任何步骤中。
什么是 NURBS 几何图形?
Rhino 以 NURBS 呈现曲线及曲面,NURBS 曲线和曲面有非常类似的特性,并且共用许多专有名词。Rhino 的曲面结构和以下会提到的曲线结构非常类似,因为曲线比较容易描述,我们将会详细解说曲线结构 。NURBS 曲线是由以下四项所定义:阶数、控制点、节点及估计法则。
阶数
阶数 ( Degree ) 是正整数。
这个数字通常是 1、2、3 或 5,Rhino 的直线和多重直线的阶数是 1,圆的阶数是 2,大部分自由造型曲线的阶数是 3 或 5。Rhino 可以让您以阶数 1 至 11 的 NURBS 作业。有时候,Rhino 会用到线性、二次方、三次方或五次方等术语,线性表示阶数为 1、二次方表示阶数为 2、三次方表示阶数为 3、五次方表示阶数为 5。
您也可能会看到某些地方提及 NURBS 曲线的次数 ( Order ),一条 NURBS 曲线的次数等于 ( 阶数 + 1 ) 的正整数,所以阶数也等于次数 - 1 。
有可能提高一个曲线的阶数但其形状却没有发生改变。 但减少曲线的阶数一定会影响其形状。
控制点
控制点是一个点的列表,控制点的最小数目是 阶数 + 1 。
改变 NURBS 曲线形状最简单的方法之一是移动控制点。
每个控制点都带有一个数字 ( 权值 ),除了少数的特例以外,权值大多是正数。当一条曲线所有的控制点有相同的权值时 ( 通常是 1 ),称为"非有理" ( Non-Rational ) 曲线,否则称为"有理" ( Rational ) 曲线。NURBS 的 R 代表有理,意味着一条 NURBS 曲线有可能是有理的。在实际情况中,大部分的 NURBS 曲线是非有理的, 但有些 NURBS 曲线永远是有理的,圆和椭圆是最明显的例子。Rhino 也有检查和改变控制点权值的工具。
节点( Knot )
节点 ( 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 曲线比较不平滑,最不平滑的情形是节点列表中段出现全复节点,代表曲线有锐角。因此,有些设计师喜欢在曲线插入或移除节点,然后调整控制点,使曲线的造型变得平滑或尖锐,Rhino 也有移除或插入节点的工具。因为节点数等于 ( N + 阶数 - 1 ),N 代表控制点的数量,所以插入一个节点会增加一个控制点,移除一个节点也会减少一个控制点。插入节点时可以不改变 NURBS 曲线的形状,但通常移除节点必定会改变 NURBS 曲线的形状。Rhino 也允许您直接删除控制点,删除控制点时也会删除一个节点。
节点(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 估计演算法,该演算法需要额外的两个节点值,总数为 ( 阶数 + N + 1 ) 个节点。当 Rhino 导出或导入这种类型的 NURBS 几何图形时会自动加入或移除两个节点。
==================================================================================================
NURBS的两种表示
通常,一个NURBS曲面F(s,t)包含三个要素:
控制顶点(x,y,z),节点序列(s,t)和权因子(w),
或者说是
带权控制顶点(x*w,y*w,z*w,w)和节点序列(s,t)。
OpenGL中提供了对两类曲面的绘制:
type=4:GL_MAP2_VERTEX_4 控制顶点带权,每个点记为(x*w,y*w,z*w,w)
type=3:GL_MAP2_VERTEX_3 控制顶点权都为1,每个点记为(x,y,z)
第二种情况就是通常的B样条曲面,本文代码考虑的是第一种情况。
1.2 NURBS的基本关系式
假设曲面F(s,t)在s,t方向阶数分别为sorder,torder,控制网格和节点序列分别为
GLfloat ctrlpoints[s_count][t_count][type], //控制网格
GLfloat sknot[sknot_count], //s节点序列
GLfloat tknot[tknot_count], //t节点序列
那么,OpenGL中,控制网格和节点序列的大小必须满足
sknot_count = s_count + sorder
tknot_count = t_count + torder
2. NURBS曲面的绘制
OpenGL中的gluNurbsSurface()函数是绘制NURBS曲线曲面的关键函数,其函数原型定义如下
void gluNurbsSurface(
GLUnurbsObj * nobj, //NURBS曲面对象
GLint sknot_count, //s方向节点数目
GLfloat * sknot, //s方向节点数组指针
GLint tknot_count, //t方向节点数目
GLfloat * tknot, //t方向节点数组指针
GLint s_stride, //s方向控制点数据跨度
GLint t_stride, //t方向控制点数据跨度
GLfloat * ctlarray, //控制点数组指针
GLint sorder, //s方向上多项式阶数
GLint torder, //t方向上多项式阶数
GLenum type //确定求值器类型
);
该函数的参数包括:
2.1 NURBS对象: GLUnurbsObj *nobj
绘制NURBS曲面之前,必须创建一个NURBS对象并设定其属性,例如
nobj = gluNewNurbsRenderer();
gluNurbsProperty(nobj , GLU_SAMPLING_TOLERANCE, 20);
gluNurbsProperty(nobj , GLU_DISPLAY_MODE, GLU_FILL);[Page]
2.2 曲线阶数
GLint sorder,
GLint torder,
对应两个方向的阶数
2.3 节点序列
GLint sknot_count,
GLfloat * sknot,
GLint tknot_count,
GLfloat * tknot,
需要指定指针,数组大小。
2.4 控制网格
GLfloat * ctlarray,
GLint s_stride,
GLint t_stride,
a)这里,控制网格被保存到一个一维数组里面,所以,如果我们定义控制网格为
GLfloat ***ctrlpoints;
在非配空间时,我们必须保证整个网格数据保存与一个连续区域里面。
b)
假设控制网格为ctrlpoints[s_count][t_count][4]
如果控制网格以行优先保存进入ctlarray,那么
s_stride = 4 * t_count//注意 此处的t_count是控制点第二坐标值
t_stride = 4
反之,如果控制网格以列优先保存进入ctlarray,那么
s_stride = 4
t_stride = 4 * s_count
2.5 求值器类型
GL_MAP2_VERTEX_4
GL_MAP2_VERTEX_3
举例:
gluNurbsSurface(theNurb3,11,knot1,11,knot2,3*7,3,&ctrl[0][0][0],4,4,GL_MAP2_VERTEX_3);