http://codercdy.com/openglxue-xi-bi-ji-qiu-zhi-qi-he-nurbs/

在最底层,图形硬件所绘制的是点、直线和多边形(通常是三角形和四边形)。平滑的曲线或表面是通过使用大量的微小线段或多边形模拟的。但是,从数学角度而言,许多非常实用的曲线和表面可以用少许几个参数(例如控制点)来描述。保存一个表面的16个控制点要比保存1000个三角形以及这些三角形每个顶点的法线向量信息所需要的空间要少的多。另外,这1000个三角形只能对真正的表面进行近似的模拟,而这些控制点却能够准确地描述真正的表面。

求值器提供了一种方式,只有少数的控制点来指定曲线或表面上的点。然后,就可以按照任意的精度来渲染曲线或表面。此外,它还可以自动计算表面的法线向量。可以按照多种方式使用求值器返回的点,例如绘制点状表面、绘制线框表面,也可以绘制进行了完全的光照、着色甚至纹理处理的表面。

可以使用求值器描述任何角度的多项式或有理多项式样条或表面,它们几乎包括了如今所有常见的样条或样条表面,包括B-样条、NURBS(非均匀有理B-样条)表面、Bezier曲线和表面,以及Hermite样条。由于求值器只提供了对曲线或表面的底层描述,因此它们一般存在于底层的工具函数库中。程序员使用的一般是更高层次的接口,GLU的NURBS工具就是这样一种高层接口。NURBS函数封装了大量的复杂代码。NURMBS所完成的最终渲染大部分是由求值器完成的。但是有些情况下(例如,修剪曲线),NURBS函数使用平面多边形进行渲染。

前提条件

如果想使用求值器来渲染Bezier曲线和表面的一部分,需要对它们的分割粒度做出决定,在做出决定时,需要在图像质量和渲染速度之间进行权衡。

求值器

Bezier曲线是单变量的向量值函数,Bezier曲面是双变量的向量值函数。对于每个u(如果是曲面则是u和v),C()(或S())公式计算曲线(或曲面)上的一个点。为了使用求值器,首先需要定义函数C()或S(),然后启用它,并使用glEvalCoord1()或glEvalCoord2()函数代替glVertex()函数。按照这种方式,我们可以像使用其他顶点一样使用曲线或曲面上的顶点。另外,有一些函数会自动生成一系列的顶点,组成一个沿u(或u和v)方向均匀排列的网格。

  • 一维求值器

    GLfloat ctrlpoints[4][3] = {
    { -4.0, -4.0, 0.0}, { -2.0, 4.0, 0.0},
    {2.0, -4.0, 0.0}, {4.0, 4.0, 0.0}};
    void init(void)
    {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlpoints[0][0]);
    glEnable(GL_MAP1_VERTEX_3);
    }
    void display(void)
    {
    int i;
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glBegin(GL_LINE_STRIP);
    for (i = 0; i <= 30; i++) {
    glEvalCoord1d((GLfloat)i/30.0);
    }
    glEnd();
    glPointSize(5.0);
    glColor3f(1.0, 1.0, 0.0);
    glBegin(GL_POINTS);
    for (i = 0; i < 4; i++) {
    glVertex3fv(&ctrlpoints[i][0]);
    }
    glEnd();
    glFlush();
    }

    样例的曲线是由位于glBegin()和glEnd()之间的display()函数绘制的。由于启用了求值器,调用glEvalCoord1f()函数就像调用glVertex()函数一样,曲线上的一个顶点的坐标对应于输入参数u。

    可以一次使用多个求值器进行计算。例如,如果已经定义并启用了一个GL_MAP1_VERTEX_3和一个GL MAP1 COLOR_4求值器,就可以调用glEvalCoord1()函数同时生成一个位置和一种颜色。对于两个顶点求值器,一次只能启用一个,尽管可能同时对它们进行了定义。类似地,只有一个纹理求值器可以处于活动状态。

    可以在glEvalCoord1()函数中使用任何u值,但目前最常用的是均匀分布的值。

  • 二维求值器

    GLfloat ctrlpoints[4][4][3] = {
    {{-1.5, -1.5, 4.0}, {-0.5, -1.5, 2.0},
    {0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},
    {{-1.5, -0.5, 1.0}, {-0.5, -0.5, 3.0},
    {0.5, -0.5, 0.0}, {1.5, -0.5, -1.0}},
    {{-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0},
    {0.5, 0.5, 3.0}, {1.5, 0.5, 4.0}},
    {{-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},
    {0.5, 1.5, 0.0}, {1.5, 1.5, -1.0}}
    };
    void display(void)
    {
    int i, j;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glPushMatrix ();
    glRotatef(85.0, 1.0, 1.0, 1.0);
    for (j = 0; j <= 8; j++) {
    glBegin(GL_LINE_STRIP);
    for (i = 0; i <= 30; i++)
    glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0);
    glEnd();
    glBegin(GL_LINE_STRIP);
    for (i = 0; i <= 30; i++)
    glEvalCoord2f((GLfloat)j/8.0, (GLfloat)i/30.0);
    glEnd();
    }
    glPopMatrix();
    glFlush();
    }
    void init(void)
    {
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4,
    0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glEnable(GL_MAP2_VERTEX_3);
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
    }
    GLfloat ctrlpoints[4][4][3] = {
    { {-1.5, -1.5, 4.0},
    {-0.5, -1.5, 2.0},
    {0.5, -1.5, -1.0},
    {1.5, -1.5, 2.0}},
    { {-1.5, -0.5, 1.0},
    {-0.5, -0.5, 3.0},
    {0.5, -0.5, 0.0},
    {1.5, -0.5, -1.0}},
    { {-1.5, 0.5, 4.0},
    {-0.5, 0.5, 0.0},
    {0.5, 0.5, 3.0},
    {1.5, 0.5, 4.0}},
    { {-1.5, 1.5, -2.0},
    {-0.5, 1.5, -2.0},
    {0.5, 1.5, 0.0},
    {1.5, 1.5, -1.0}}
    };
    void initlights(void)
    {
    GLfloat ambient[] = {0.2, 0.2, 0.2, 1.0};
    GLfloat position[] = {0.0, 0.0, 2.0, 1.0};
    GLfloat mat_diffuse[] = {0.6, 0.6, 0.6, 1.0};
    GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat mat_shininess[] = {50.0};
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    }
    void display(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glRotatef(85.0, 1.0, 1.0, 1.0);
    glEvalMesh2(GL_FILL, 0, 20, 0, 20);
    glPopMatrix();
    glFlush();
    }
    void init(void)
    {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4,
    0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glEnable(GL_MAP2_VERTEX_3);
    glEnable(GL_AUTO_NORMAL);
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    initlights();
    }
  • 使用求值器进行纹理处理

    GLfloat ctrlpoints[4][4][3] = {
    {{ -1.5, -1.5, 4.0}, { -0.5, -1.5, 2.0},
    {0.5, -1.5, -1.0}, {1.5, -1.5, 2.0}},
    {{ -1.5, -0.5, 1.0}, { -0.5, -0.5, 3.0},
    {0.5, -0.5, 0.0}, {1.5, -0.5, -1.0}},
    {{ -1.5, 0.5, 4.0}, { -0.5, 0.5, 0.0},
    {0.5, 0.5, 3.0}, {1.5, 0.5, 4.0}},
    {{ -1.5, 1.5, -2.0}, { -0.5, 1.5, -2.0},
    {0.5, 1.5, 0.0}, {1.5, 1.5, -1.0}}
    };
    GLfloat texpts[2][2][2] = {{{0.0, 0.0}, {0.0, 1.0}},{{1.0, 0.0}, {1.0, 1.0}}};
    void display(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glEvalMesh2(GL_FILL, 0, 20, 0, 20);
    glFlush();
    }
    #define imageWidth 64
    #define imageHeight 64
    GLubyte image[3*imageWidth*imageHeight];
    void makeImage(void)
    {
    int i, j;
    float ti, tj;
    for (i = 0; i < imageWidth; i++) {
    ti = 2.0*3.14159265*i/imageWidth;
    for (j = 0; j < imageHeight; j++) {
    tj = 2.0*3.14159265*j/imageHeight;
            image[3*(imageHeight*i+j)] = (GLubyte) 127*(1.0+sin(ti));
    image[3*(imageHeight*i+j)+1] = (GLubyte) 127*(1.0+cos(2*tj));
    image[3*(imageHeight*i+j)+2] = (GLubyte) 127*(1.0+cos(ti+tj));
    }
    }

    } void init(void) { glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]); glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, &texpts[0][0][0]); glEnable(GL_MAP2_TEXTURE_COORD_2);
    glEnable(GL_MAP2_VERTEX_3); glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0); makeImage(); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imageWidth, imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, image); glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST); glShadeModel (GL_FLAT); }

GLU的NURBS接口

求值器是能够直接绘制曲线和表面的唯一OpenGL图元。

  • 一个简单的NURBS例子

    • 如果相对NURBS表面应用光照,可以用GL_AUTO_NORMAL为参数调用glEnable()函数,自动生成法线向量。
    • 使用gluNewNurbsRenderer()函数创建一个指向NURBS对象的指针。在创建自己的NURBS曲线或表面时,将会用到这个指针。
    • 如果需要,可以调用gluNurbsProperty()函数选择渲染值。例如,用于渲染NURBS对象的直线或多边形的最大数量。gluNurbsProperty()函数还可以启用一种模式,使用户可以在这种模式下通过回调接口提取分格化的几何数据。
    • 如果想在遇到错误时得到通知,可以调用gluNurbsCallback()函数。gluNurbsCallback()函数还可以注册用于提取分格化后的几何图形数据的函数。
    • 调用gluBeginCurve()或gluBeginSurface()函数,开始绘制曲线或表面。
    • 生成和渲染曲线或表面。至少需要调用1次gluNurbsCurve()或gluNurbsSurface()函数,以NURBS对象的控制点、节点序列以及多项式基函数的阶数为参数。可以多次调用这些函数,指定法线向量和纹理坐标。
    • 调用gluEndCurve()或gluEndSurface()函数完成曲线或表面的绘制。
  • 管理NURBS对象

    与NURBS对象相关联的一组属性可以影响物体的渲染方式。这些属性包括表面如何被光栅化、显示还是返回分格化顶点以及分格化的精度。

  • 创建NURBS曲线或表面

    为了渲染NURBS表面,可以在一对gluBeginSurface()和gluEndSurface()函数之间调用gluNurbsSurface()函数。绘制NURBS曲线,需要在一对gluBeginCurve()和gluEndCurve()函数之间调用绘制曲线的函数。

    在默认情况下,NURBS分格化对象把NURBS对象分解为几何直线和多边形,然后再对它们进行渲染。GLU1.3增加了额外的回调函数,可以不再渲染后分格化的值,而是把它们返回给应用程序。

  • 修剪NURBS表面

    为了用OpenGL创建经过修剪的表面,一开始执行的步骤与创建未修剪的表面相同。在调用glugluBeginSurface()和gluNurbsSurface()之后,但在调用gluEgluEndSurface()之前,可以调用gluBeginTrim()函数对表面进行修剪。

    可以创建两种类型的修剪曲线:用gluPwlCurve()函数创建一条分段的线性曲线或者用gluNurbsCurve()函数创建一条NURBS曲线。

    修剪曲线必须闭合并且互不相交。可以任意组合修剪曲线,只要修剪曲线能够形成环路。

OpenGL学习笔记——求值器和NURBS的更多相关文章

  1. OpenGL学习笔记:拾取与选择

    转自:OpenGL学习笔记:拾取与选择 在开发OpenGL程序时,一个重要的问题就是互动,假设一个场景里面有很多元素,当用鼠标点击不同元素时,期待作出不同的反应,那么在OpenGL里面,是怎么知道我当 ...

  2. java学习笔记13--比较器(Comparable、Comparator)

    java学习笔记13--比较器(Comparable.Comparator) 分类: JAVA 2013-05-20 23:20 3296人阅读 评论(0) 收藏 举报 Comparable接口的作用 ...

  3. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

  4. matlab学习笔记7-定时器

    一起来学matlab-matlab学习笔记7-定时器 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用>张德丰等著 感谢张老师的书籍,让 ...

  5. OpenGL学习笔记3——缓冲区对象

    在GL中特别提出了缓冲区对象这一概念,是针对提高绘图效率的一个手段.由于GL的架构是基于客户——服务器模型建立的,因此默认所有的绘图数据均是存储在本地客户端,通过GL内核渲染处理以后再将数据发往GPU ...

  6. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  7. C#学习笔记之值类型与引用类型

    [TOC] C#学习笔记之值类型与引用类型 1.值类型与引用类型 1.1 深层区别 值类型与引用类型有不同的内存分布,这导致了不同的内存管理机制: 值类型由OS负责内存管理 引用类型由垃圾回收器(GC ...

  8. Python学习笔记012——装饰器

    1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...

  9. Python学习笔记:装饰器

    Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...

随机推荐

  1. python manage.py shell 的增删改查

    python manage.py shell 的增删改查 guoguo-MacBook-Pro:myblog guoguo$ python manage.py shell Python 3.5.1 ( ...

  2. ABAP HTTP POST

    1.HTTP DATA: lo_http_client TYPE REF TO if_http_client, lv_service TYPE string, lv_result TYPE strin ...

  3. Windows Server 2008 MS Office 操作 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败,原因是出现以下错误: 80070005 拒绝访问。 (异常来自 HRESULT:0x80070005 (E_ACCESSDENIED))。

    Make sure that you have Office runtime installed on the server. If you are using Windows Server 2008 ...

  4. Hadoop- NameNode和Secondary NameNode元数据管理机制

    元数据的存储机制 A.内存中有一份完整的元数据(内存meta data) B.磁盘有一个“准完整”的元数据镜像(fsimage)文件(在namenode的工作目录中) C.用于衔接内存metadata ...

  5. kvm初体验之二:安装

    Host: CentOS release 6.4 (Final) 1. 开启处理器的虚拟化功能 进入BIOS,使能虚拟化功能: 进入linux, grep -E "vmx|svm" ...

  6. oracle中导出sql的几个常见词语的意思

    set feedback off不显示反馈信息  “1行已插入”,大量数据装入时,显示这个也是很浪费资源和时间的. set define off 如果你某个字段里面有&字符,插入数据会出错,设 ...

  7. NO0:重新拾起C语言

    因工作所需,重新捡起C语言,之前在学校里有接触过,但现在已经忘的一干二净了,现决定重新开始学习,为工作,为生活. 以<标准 C程序设计 第5版>的课程进行基础学习,同时以另外两本书为辅助, ...

  8. BZOJ_3935_Rbtree

    https://lydsy.com/JudgeOnline/problem.php?id=3935 分析: 如果知道更改后的状态,那么代价和是否合法都能求出来 对不合法的情况也设一个估价函数. 随机这 ...

  9. C++之MutexLock和MutexLockGuard封装

    noncopyable.h #ifndef __WD_NONCOPYABLE_H__ #define __WD_NONCOPYABLE_H__ namespace wd { class Noncopy ...

  10. 创建自己的YUM仓库

    1. 首先,假定我们应用的名字叫helloworld(可以参考简单 RPM 包制作来创建两个版本helloworld安装RPM包,helloworld-1.0.0-1.el6.x86_64.rpm和h ...