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. matlab写txt文件

    fd=fopen('C:\Users\Qin\Desktop\1112.txt','w');for i=16:19 for j=1:5 fprintf(fd,'%f,%f\r\n',[cluster( ...

  2. ZOJ 3329 One Person Game:期望dp【关于一个点成环——分离系数】

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3329 题意: 给你面数分别为k1,k2,k3的三个骰子. 给定a ...

  3. 在ubuntu环境安装youcompleteme

    sudo apt-get update #更新软件源 sudo apt-get clang #安装clang sudo apt-get cmake #安装cmake sudo apt-get inst ...

  4. MongoDB 使用经验笔记

    bin下的mongod就是MongoDB的服务端进程,mongo就是其客户端,其它的命令用于MongoDB的其它用途如MongoDB文件导出等 启动方式: 1.直接启动,指定各项参数: /usr/lo ...

  5. 「NOIP2017」「LuoguP3959」 宝藏(爆搜

    题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nn 个深埋在地下的宝藏屋, 也给出了这 nn 个宝藏屋之间可供开发的mm 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏. ...

  6. HTML Document 头

    1. 比较宽松的HTML格式,样式 <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"> 如下 ...

  7. mysql5.1的编译安装 ----针对第一次安装mysql的

    由于是第一次安装,不能确定你是否有安装编译和mysql所要依赖的插件,使用我是当做你最原始的安装环境.  1.安装mysql5.1的依赖包 yum install -y gcc gcc-c++ aut ...

  8. DataTable批量插入数据库

    最近在将excel中的文件导入到数据库中,用程序进行编写,由于数据量较大所以速度很慢,后来采用了SqlBulkCopy类,解决了速度的问题,我就insert语句,sqldataadapter.upda ...

  9. QT时钟绘制

    Demo的效果 资源占用还能接受 运行久一点内存就下去了 下面是Demo的代码 #include "mainwindow.h" #include "ui_mainwind ...

  10. [51nod1065]最小正子段和

    题意:求一个序列中大于0的最小子段和. 解题关键: 先求出前缀和和,对于每个位置求某个位置到当前位置和大于1的和的最小值.然而这是复杂度是O(n^2)的.其实可以通过排序优化到O(nlogn).对前缀 ...