非均匀有理样条NURBS

非均匀有理样条NURBS( Non-Uniform Rational B-Splines ) 是近年来发展迅速,应用广泛的一种表示曲线曲面造型技术。它能够精确地表示二次规则曲线曲面,从而能用统一的数学形式表示规则曲面与自由曲面,具有可影响曲线曲面形状的权因子,使形状更宜于控制和实现。1991 年国际标准化组织颁布了关于工业产品数据交换的STEP 国际标准,将NURBS 方法作为定义工业产品几何形状的唯一数学描述方法,从而使NURBS 方法成为曲面造型技术发展趋势中最重要的基础。 

OpenGL 对NURBS 的支持是通过实用库实现的,相应的函数有

gluNewNurbsRenderer

gluNurbsProperty

gluNurbsSurface

gluBeginSurface

gluEndSurface

gluDeleteNurbsRenderer

 

创建NURBS 曲面时,首先使用gluNewNurbsRenderer 创建一个NURBS 对象,然后使用gluNurbsProperty 设置NURBS 对象的属性。gluBeginSurface~gluEndSurface 的作用类似glBegin~glEnd ,在两个函数间使用gluNurbsSurface 来定义NURBS 曲面的具体形状。gluEndSurface 完成了NURBS 曲面的定义后就可以显示了。需要注意的是,不再使用NURBS 对象时,要记得使用 gluDeleteNurbsRenderer 释放掉其对象所占用的内存。

gluNewNurbsRenderer 函数不带任何参数,调用成功后返回一个GLUnurbsObj 类型的指针,指向创建成功的NURBS 对象。其原型如下

 
GLUnurbsObj* gluNewNurbsRenderer( void );
 

创建NURBS 对象的代码类似如下

 

GLUnurbsObj *theNurb;
theNurb = gluNewNurbsRenderer();
 

接下来,要使用 gluNurbsProperty 来设置theNurb 的属性。gluNurbsProperty 的原型如下

 
void gluNurbsProperty(
  GLUnurbsObj *nobj,
  GLenum property,
  GLfloat value
);

nobj 参数就是使用gluNewNurbsRenderer 创建的NURBS 对象。property 是需要设置的属性,可以取的值如下表10-2 所示。

 

表 10-2   gluNurbsProperty 参数含义

Property 取值

含义

GLU_SAMPLING_TOLERANCE

当抽样方法设置为GLU_PATH_LENGTH 时指定抽样误差,单位是像素。缺省值为50.0 。

GLU_DISPLAY_MODE

使用value 参数来定义NURBS 曲面的渲染方式,此时value可以取值GLU_FILL ,GLU_OUTLINE_POLYGON和GLU_OUTLINE_PATCH 。

GLU_FILL 表示曲面填充多边形方式渲染,曲面是一个整体的平滑曲面,这也是缺省的方式。

GLU_OUTLINE_POLYGON 表示曲面仅仅画出由小方格组成的外轮廓,表现出来的就是网格曲面。

GLU_OUTLINE_PATCH 则纯粹是一个外轮廓边沿。

GLU_CULLING

value 参数是一个BOOL 型的数值,确定NURBS 曲线是否丢弃视点范围之外的控制点,缺省为GL_FALSE 。

GLU_AUTO_LOAD_MATRIX

value 参数是一个BOOL 型的数值。当为GL_TRUE 时,表示NURBS 要从OpenGL 服务器下载投影矩阵,模型观察矩阵和视点来计算对每一个NURBS 曲线计算抽样和消隐矩阵。当value 为GL_FALSE 时,表示必须由本地应用提供投影矩阵,模型观察矩阵和视点。对于非网络应用来说,该项可以忽略。

GLU_PARAMETRIC_TOLERANCE

当抽样方法为GLU_PARAMETRIC_ERROR 时指定最大距离,单位是像素,缺省值为0.5 。

GLU_SAMPLING_METHOD

指定怎样利用方格拼成NURBS 曲面,即拟合NURBS 的方法。对应的value 可以取GLU_PATH_LENGTH,GLU_PARAMETRIC_ERROR 和GLU_DOMAIN_DISTANCE 。

当value 取值为GLU_PATH_LENGTH (缺省值)时,用于组成曲面的小方格的边长最大不超过以GLU_SAMPLING_TOLERANCE 设置的值。

当value 取值为GLU_PARAMETRIC_ERROR 时,以GLU_PARAMETRIC_TOLERANCE 指定的最大距离值在小方格和整个表面之间。

当value 取值为 GLU_DOMAIN_DISTANCE 时,指明在u 、v方向上每单位长度有多少个抽样点。

GLU_U_STEP

指定沿u 方向每单位长度的采样点数,当LU_SAMPLING_METHOD 设置成GLU_DOMAIN_DISTANCE时GLU_U_STEP 有效。缺省为100 。

GLU_V_STEP

指定沿v 方向每单位长度的采样点数,当LU_SAMPLING_METHOD 设置成GLU_DOMAIN_DISTANCE时GLU_U_STEP 有效。缺省为100 。

 

value 表示property 的值,它可以是一个数字,也可以是GLU_PATH_LENGTH 、 GLU_PARAMETRIC_ERROR和GLU_DOMAIN_DISTANCE 三者之一。

 

下面是一个NURBS 曲面的生成代码。

 

//NURBS 对象

GLUnurbsObj *theNurb;

 

// 三种显示模式

GLfloat DisplayMode[3] = {GLU_FILL, GLU_OUTLINE_POLYGON, GLU_OUTLINE_PATCH};

int Mode = 0;                   // 缺省的显示模式为GLU_FILL

BOOL bModePressed = FALSE;  // 改变显示模式选择按键

 

// 改变显示模式的按键处理在WndProc 中

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

    switch (message)

    {

    case WM_ACTIVATE:

    ……   

    case WM_SIZE:

        ……

    case WM_KEYDOWN:

        switch(wParam)

        {

        case VK_ESCAPE:

            PostQuitMessage(0);

            return 0;

        case VK_SPACE:              // 空格键进行显示模式切换

            bModePressed = TRUE;

            Mode ++;

            if(Mode >2)

                Mode = 0;

            break;

        }

        break;

        case WM_DESTROY:

            PostQuitMessage(0);

            break;

        default:

            return DefWindowProc(hWnd, message, wParam, lParam);

    }

    return 0;

}

 

在glInit() 中,我们来建立一个光源,并且创建一种材质,将其应用到NURBS 曲面上。

 

int glInit()

{

   

    // 启用阴影平滑(Smooth Shading) 。

    glShadeModel(GL_SMOOTH);

    // 设置深度缓冲

    glClearDepth(1.0f);

    // 启动深度测试

    glEnable(GL_DEPTH_TEST);

    // 深度测试的类型

    glDepthFunc(GL_LEQUAL);

    // 进行透视修正

    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    // 翡翠、祖母绿

    GLfloat mat_ambient[] = { 0.021500, 0.174500, 0.021500, 0.550000 };

    GLfloat mat_diffuse[] = { 0.075680, 0.614240, 0.075680, 0.550000 };

    GLfloat mat_specular[] = {0.633000, 0.727811, 0.633000, 0.550000};

    GLfloat mat_shininess[] = {76.800003 };

    glColor3f(1.0f, 1.0f, 1.0f);

    // 材质属性 

    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);

    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);

    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);

    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

 

    // 启动光照,使用缺省的光照参数 

    glEnable(GL_LIGHTING);

    glEnable(GL_LIGHT0);

 

    // 自动计算法向量

    glEnable(GL_AUTO_NORMAL);

 

    // 创建一个新的NURBS 对象

    theNurb = gluNewNurbsRenderer();

 

    // 设置NURBS 对象的参数

    gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, 25.0);

    gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);

 

    return TRUE;

}

 

在glShutdown 中必须添加将NURBS 对象释放的代码,否则,即便应用程序关闭,对象占用的内存也可能会一直无法释放。

 

void glShutdown()

{

    ……

    if(theNurb)

        gluDeleteNurbsRenderer(theNurb);

}

 

// 定义NURBS 的控制点,共25 个

GLfloat CtlPoints[5][5][3]=

{

    {{-3.0, -3.0, -3.0}, {-3.0, -1.0, -3.0}, {-3.0, 0.0, -3.0},

{-3.0, 1.0, -3.0}, {-3.0, 3.0, -3.0}},

 

    {{-1.0, -3.0, -3.0}, {-1.0, -1.0, -9.0}, {-1.0 ,0.0, 9.0},

{-1.0, 1.0, -9.0}, {-1.0, 3.0, -3.0}},

 

    {{1.0, -3.0, -3.0}, {1.0, -1.0, 3.0}, {1.0, 0.0, 3.0,},

{1.0, 1.0, 3.0}, {1.0, 3.0, -3.0}},

 

    {{3.0, -3.0, -3.0}, {3.0, -1.0, -3.0},{3.0, 0.0, -3.0},

  {3.0, 1.0, -3.0},{3.0, 3.0, -3.0}},

 

    {{5.0, -3.0, -3.0}, {5.0, -1.0, -9.0},{5.0, 0.0, 9.0},

{5.0, 1.0, -9.0},{5.0, 3.0, -3.0}}

};

GLfloat knots[10] = {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0};

void glMain()

{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();   // 加载单位矩阵

    glTranslatef(0.0f, -1.0f, -10.0f);

    glRotatef(90, 0.0f, 0.0f, 1.0f);

 

    // 根据按键确定切换显示模式     

    if(bModePressed)

    {

        bModePressed = FALSE;

        gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, DisplayMode[Mode]);

    }

    // 开始定义NURBS 曲面   

    gluBeginSurface(theNurb);

    gluNurbsSurface(theNurb,

        10,

        knots,

        10,

        knots,

        15,

        3,

        &CtlPoints[0][0][0],

        5, 5,

        GL_MAP2_VERTEX_3);

    gluEndSurface(theNurb);     // 结束曲面定义并显示

   

    SwapBuffers(g_hDC);// 交换前后缓冲区

程序运行后,可以看到一个脸部模型的大概轮廓已经显示出来。当然,真正的实现一个完整的面具模型还需要更多的控制点。按空格键后还可以看到网格状的曲面和仅有外边沿轮廓的曲面,仅有外边沿轮廓的曲面基本上已经看不出是一个曲面了,更象一个平面多边形。效果如图10-11 所示。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值