`
aijuans
  • 浏览: 1566007 次
社区版块
存档分类
最新评论

Qt下的OpenGL 编程(7)顶点数组和显示列表

阅读更多
 
 

一、提要
        OpenGL作为一个高性能的图形接口,性能能肯定是放在第一位的了,
现在的移动平台也是OpenGL ES,这对性能的要求就更高了,
今天我们要接触到的这两个东西—顶点数组和显示列表都是用于实现高性能绘图的手段。
         下一篇教程我打算去做一个一个3D漫游的例子,会用到今天的技术。

二、顶点数组
        在之前的几篇教程中,有时候绘制一个图形需要很多次函数调用才能完成。
首先调用一次glBegin(),然后为每个点调用一次函数,最后还要调用glEnd().中间
如果还要设置颜色或是法线什么的,函数的调用次数还要增加,这给系统带来了很大的
开销,影响了程序的性能。

        OpenGL提供了一些顶点数组函数,允许只用少数几个数组指定大量的与顶点相关的数据,
把数据放在顶点数组中可以提高应用程序的性能还可一减少函数调用的次数,从而提高性能。
另外,使用顶点数组还可以避免共享顶点的冗余处理。
        使用顶点数组对几何图元进行渲染的3个步骤:
       1)激活数组,存储顶点坐标、RGBA颜色等等;
       2)装载数组
       3)利用数组绘制图形

       首先用一个简单的例子来演示一下,用顶点数组绘制带颜色的三角形。
       首先在initializeGL()中设置参数:

       void NeHeWidget::initializeGL()
       {
 
 
           // 启用阴影平滑
           glShadeModel( GL_SMOOTH );
           // 黑色背景
           glClearColor( 0.0, 0.0, 0.0, 0.0 );
           // 设置深度缓存
           glClearDepth( 1.0 );
           // 启用深度测试
           glEnable( GL_DEPTH_TEST );
           // 所作深度测试的类型
           glDepthFunc( GL_LEQUAL );
           // 告诉系统对透视进行修正
           glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
 
 
           glClearColor(0.0, 0.0, 0.0, 0.0);
           // 启用顶点数组
           glEnableClientState(GL_VERTEX_ARRAY);
           // 颜色数组也需要启用
           glEnableClientState(GL_COLOR_ARRAY);
           // 默认就是此参数,可忽略,为了明确说明特意指定
           glShadeModel(GL_SHADE_MODEL);
           // 顶点数组数据
           static GLfloat fVertices[] = {    -0.5, -0.5,
                                             0.5, -0.5,
                                             0.5,  0.5,
                                            };
           // 颜色数组
           static GLfloat fColor[] = { 1.0, 0.0, 0.0,
                                       0.0, 1.0, 0.0,
                                       0.0, 0.0, 1.0,
                                       };
           // 指定顶点数组数据
           glVertexPointer(2, GL_FLOAT, 0, fVertices);
           //指定颜色数组
           glColorPointer(3, GL_FLOAT, 0, fColor);
       }

 
 
       接着在paintGL()中绘制图形:

 
 
void NeHeWidget::paintGL()
{
 
 
    // 清除屏幕和深度缓存
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glLoadIdentity();
    //移到屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景
    glTranslatef(0.0f,0.0f,zoom);
 
 
 
 
    glBegin(GL_TRIANGLES);
    glArrayElement(0);
    glArrayElement(1);
    glArrayElement(2);
    glEnd();
 
 
}
        可以看到,我们得到的效果和一个个定义顶点,一个个定义颜色是一样的。


           下面解释其中的几个函数:

           void glEnableClientState(GLenum array)

           用于制动需要启用的数组,例子中GL_VERTEX_ARRAY表示顶点数组,GL_COLOR_ARRAY表示颜色数组。开启一种顶点数组都必须调用glEnableClientState来激活.

          glDisableClientState(GLenum array)

        用于关闭相应的数组。
  
 
 
       void glVertexPointer(	GLint  	size,GLenum  	type,GLsizei  	stride,const GLvoid *  	pointer);
 
 
        指定需要访问的空间的坐标数据,作用是把一数组与图形的顶点关联在一起。
  glColorPointer意思相类似。
         glArrayElement则根据顶点数组来调用相应的函数,每次只调用1个顶点。
 
 
跨距

        如果将两个数组合并在一起,那么坐标和颜色的数据就在一个数组了,现在代码修改如下:
        static GLfloat data[]={
            -0.5, -0.5,1.0, 0.0, 0.0,
            0.5, -0.5, 0.0, 1.0, 0.0,
            0.5,  0.5, 0.0, 0.0, 1.0,
        };
 
 
        glVertexPointer(2,GL_FLOAT,5*sizeof(GLfloat),&data[0]);
        glColorPointer(3,GL_FLOAT,5*sizeof(GLfloat),&data[2]);

 
 
        这里用到了跨距的概念,第3个参数指定跨距,如颜色,从数组的每组数据的第3个开始取数据,然后跨5个,坐标顶点则从数据每组数据的第1个开始取数据并跨5个.

        注意:由于跨距需要计算数据类型,所以数组的数据类型需要相同.
        最后得到的效果与之前的一样。


 
 
解引用数组
        下面要用到的是glDrawElements,学名叫解引用数组,作用类似于循环调用glArrayElement,但需要定义一个索引的数组。有了它,我们只需要一句函数调用就可以把图形绘制出来了,继续修改代码。

        只需修改paintGL()中的代码,将glBegin()和glEnd()还有它们之间的代码换成:
        //定义索引数组
        GLubyte index[]= {0,1,2,3} ;
        //解引用数组
            glDrawElements(GL_TRIANGLES,3,GL_UNSIGNED_BYTE,index);

        接下来,我们来利用解引用数组绘制一个立方体。
 
 
    //正方体绘制
    static GLfloat data[]={
        -1.0, -1.0, -1.0 , 0.0, 0.0, 0.0 ,
        1.0, -1.0, -1.0 ,0.0, 0.0, 1.0,
        1.0, 1.0, -1.0 ,0.0, 1.0, 0.0 ,
        -1.0, 1.0,-1.0 ,0.0,1.0, 1.0,
 
 
        -1.0, -1.0, 1.0 ,1.0, 0.0, 0.0,
        1.0, -1.0, 1.0 ,  1.0, 0.0, 1.0,
        1.0, 1.0, 1.0 ,1.0,1.0, 0.0,
        -1.0, 1.0, 1.0 ,1.0, 1.0, 1.0 ,
    };
    //指定顶点数据
    glVertexPointer(3,GL_FLOAT,6*sizeof(GLfloat),&data[0]);
    //指定颜色数据
    glColorPointer(3,GL_FLOAT,6*sizeof(GLfloat),&data[2]);
     //指定索引序列
static const GLint face_lists[][4]=
             {
             0, 3, 2, 1,
             6, 5, 1, 2,
             3, 0, 4, 7,
             3, 7, 6, 2,
             0, 4, 5, 1,
             7, 4, 5, 6,
             };//注意每个面绘制的顺序,背面采用顺时针方向。
 
 
              //绘制图形
              glDrawElements( GL_QUADS, 24, GL_UNSIGNED_INT, face_lists );

 
 
最后结果如下:


        我们在定义出数组之后,只用了一条语句便画出了一个立方体,而不是像之前那样定义24次顶点,同时定义24次颜色。

 
 
        glArrayElement击败了glVertex*,但是glDrawElements又比glArrayElement更高。glDrawElements使用的时候将一连串glArrayElement需要使用的数据放在一个数组中(此例中是byRectIndices),然后通过一个函数调用一次指定。注意啊,glDrawElements自带glBegin及glEnd效果,不再需要它们。(事实上,在OpenGL中命名中带Draw的一般都不需要glBegin和glEnd)程序运行的效果与glVertexArrayWithColor例子运行效果相同。

          需要提一下的还有glMultiDrawElements,它是glDrawElements的升级版,一条语句相当于多条glArrayElements,接口有点麻烦,使用方法可以参考文档。

 
 
混合数组
        这个有点类似上面说到的跨距的使用了,也是将不同的顶点数组放在同一个数组中,然后用相应的方法把
它解析出来,这里要用到的是glInterleavedArrays,函数将会根据参数,激活各种顶点数组,并存储顶点。
        修改代码:
 
 
static GLfloat data[]={
    -0.5, -0.5,1.0, 0.0, 0.0,
    0.5, -0.5, 0.0, 1.0, 0.0,
    0.5,  0.5, 0.0, 0.0, 1.0,
};
glInterleavedArrays(GL_C3F_V3F,0,data);
glDrawArrays(GL_LINES,0,4);

 
 
        对应于相应的参数,就可以实现相应的图形,感觉这个比跨距的使用更简单一些,代码也会更加清晰。

 
 
总结
        OpenGL提供了一系列更加高效的函数以完成对效率要求苛刻的任务,我们将逐一介绍,会发现,使用难度越来越高,适用范围越来越窄,但是函数调用越来越少,功能越来越强大,事实上,函数调用少并不是OpenGL这样设计的唯一理由,越是这样同时处理多个数据的接口,因为一个接口掌握的信息越多,那么也就越能更多的对其进行优化。

 
 
三、显示列表

            显示列表可以用来存储OpenGL函数,供以后执行。如果需要多次重绘同一个几何图形,或者如果有一些需要多次调用的用于更改状态的函数,把这些函数存储在显示列表中是一个很好的思路。
            为了实际操作一下,我们先在我们的项目中添加glut库。
            在.pro文件中添加
            LIBS += -lglut

 
 
         简单介绍一下glut库
GLUT(英文全写:OpenGL Utility Toolkit)是一个处理OpenGL程式的工具库,负责处理和底层操作系统的呼叫以及I/O,并包括了以下常见的功能:
定义以及控制视窗
侦测并处理键盘及鼠标的事件
以一个函数呼叫绘制某些常用的立体图形,例如长方体、球、以及犹他茶壶(实心或只有骨架,如glutWireTeapot())
提供了简单选单列的实现。
         *注意:在使用glut函数之前一定要对其进行和初始化,在man.cpp的main函数中加入glutInit(&argc, argv);

 
 
创建显示列表
  OpenGL提供类似于绘制图元的结构即glBegin()与glEnd()的形式创建显示列表,其相应的函数为:
 
 
void glNewList(GLuint list,GLenum mode);
  说明一个显示列表的开始,其后的OpenGL函数存入显示列表中,直至调用结束表的函数(见下面)。参数list是一个正整数,它标志唯一的显示列表。参数mode的可能值有GL_COMPILE和GL_COMPILE_AND_EXECUTE。若要使后面的函数语句只存入而不执行,则用GL_COMPILE;若要使后面的函数语句存入表中且按瞬时方式执行一次,则用GL_COMPILE_AND_EXECUTE。

 
 
void glEndList(void);
  标志显示列表的结束。

 
 
执行显示列表
    在建立显示列表以后就可以调用执行显示列表的函数来执行它,并且允许在程序中多次执行同一显示列表,同时也可以与其它函数的瞬时方式混合使用。显示列表执行的函数形式如下:
 
 
  void glCallList(GLuint list);
  执行显示列表。参数list指定被执行的显示列表。显示列表中的函数语句按它们被存放的顺序依次执行;若list没有定义,则不会产生任何事情。下面举出一个应用显示列表的简单例子:

 
 
首先是没有用显示列表的状态:
//绘制100个茶壶
 glColor3f( 0.0, 1.0,1.0 );
for(int i=0;i<10;i++)
{
    for(int j=0;j<10;j++)
    {
        glTranslatef(j*0.5f,0.0f,0.0f);
        glPushMatrix();
        glutWireTeapot(0.2);
        glPopMatrix();
    }
   glTranslatef(0.0f,-0.4f,0.0f);
}

结果会在屏幕上绘制100个茶壶。


 
 
下面用显示列表来实现:
在initializeGL()中添加:
glNewList (teapotList, GL_COMPILE);
glutWireTeapot(0.2);
glEndList ();

 
 
创建了一个teapotList的列表,接着我们在循环中替换相应的代码:
for(int i=0;i<10;i++)
{
    for(int j=0;j<10;j++)
    {
        glPushMatrix();
        glTranslatef(j*0.5f,0.0f,0.0f);
        //glutWireTeapot(0.2);
        glCallList (teapotList);
       glPopMatrix();
 
 
    }
   glTranslatef(0.0f,-0.4f,0.0f);
}
 
 
实际运行发现渲染的速度会快一点点(感觉是心理作用),可能是小的场景差别不是很大,有兴趣的同学可以测一下fps,结果就会明晰了。

四.参考资料

1.      《 OpenGL Reference Manual 》, OpenGL 参考手册

2.      《 OpenGL 编程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis 著,徐波译,机械工业出版社

3.         《win32 OpenGL编程 》   一个大牛的博客     http://blog.csdn.net/vagrxie/article/category/628716/3
4.             《OpenGL函数思考 》   里面有很多OpenGL函数的通俗解释     http://blog.csdn.net/shuaihj

 
 
 
 
更多详细信息请查看java教程网 http://www.itchm.com/forum-59-1.html
分享到:
评论

相关推荐

    QtOpenGL文字显示

    同时,通过QFont和QFontMetrics来处理文字的布局和尺寸,然后使用OpenGL的顶点数组和顶点着色器将字符转换为几何形状并绘制到屏幕上。 接下来,我们讨论“透视投影”。在3D图形中,透视投影能模拟人眼观察物体的...

    QT+OpenGL读obj模型

    QT+OpenGL读取obj模型是一项在图形编程领域中常见的任务,它涉及到计算机图形学、QT框架和OpenGL库的综合应用。下面将详细解释这个过程涉及的知识点。 首先,QT(Qt)是一个跨平台的C++应用程序开发框架,广泛用于...

    QT使用OpenGL显示YUV420和YUV444数据实例,内附用例,可直接运行

    在本文中,我们将深入探讨如何使用QT框架与OpenGL来显示YUV420和YUV444格式的视频数据。首先,让我们了解这些技术的基本概念。 **OpenGL** 是一个跨语言、跨平台的编程接口,用于渲染2D、3D图形。它允许程序员直接...

    Linux下用QT开发OpenGL教程

    在Linux环境下,使用Qt进行OpenGL开发是一项常见的任务,Qt是一个强大的C++图形用户界面工具包,而OpenGL则是一个跨语言、跨平台的编程接口,用于渲染2D和3D图形。本教程将深入探讨如何结合这两者,在Linux系统中...

    QT 导入STL文件显示在OpenGL窗口

    在本文中,我们将深入探讨如何在QT平台上使用OpenGL来导入并显示二进制STL( Stereolithography)文件。STL是一种常见的3D模型文件格式,广泛用于3D打印和计算机图形学。QT是一个跨平台的应用程序开发框架,而OpenGL...

    Qt与OpenGL实现的立体俄罗斯方块游戏

    开发者需要使用OpenGL的数组缓冲对象(Array Buffer Object, ABO)和元素数组缓冲对象(Element Array Buffer Object, EBO)来存储和传输顶点数据。同时,利用纹理映射技术,可以将不同颜色的纹理贴在每个色块上,...

    Qt+OpenGL框架代码

    【Qt+OpenGL框架代码】是基于C++编程语言和Visual Studio 2017 IDE实现的一个图形用户界面(GUI)应用程序框架。这个框架利用了Qt库的强大功能以及OpenGL的3D图形渲染能力,旨在帮助开发者快速构建具有高性能3D图形...

    Qt_OpenGL中文教程实例代码1-16课 配合NeHe的OpenGL例子用Qt实现,

    在NeHe的教程中,我们会遇到各种OpenGL技术,如顶点数组、纹理映射、光照模型、深度测试等。在Qt中实现这些,需要将NeHe的C风格代码转换为C++风格,使用Qt的OpenGL辅助类,如`QOpenGLVertexArrayObject`、`...

    qt+opengl实现的烟花粒子爆炸效果

    在本项目中,"qt+opengl实现的烟花粒子爆炸效果"是一个利用Qt框架和OpenGL图形库...通过学习和分析这个项目,开发者可以深入理解Qt的图形界面编程以及OpenGL的图形渲染原理,同时提升在实时图形应用开发方面的能力。

    Qt OpenGL三维例子

    现代OpenGL更倾向于使用顶点数组和顶点着色器,而不是这些固定功能管线的函数,但初学者可以从这些基本函数开始学习。 3. **resizeGL()**:当窗口大小改变时,这个函数会被调用,我们需要在这里更新视口大小和投影...

    QT + OPenGL 绘图

    QT是一个流行的开源C++框架,提供了丰富的GUI(图形用户界面)工具,而OpenGL则是一种跨语言、跨平台的编程接口,用于渲染2D和3D图形。在本项目中,开发者使用了QT 4.7.3版本和Visual Studio 2008来编写一个简单的...

    QT和GDAL和OpenGL影像显示,qt使用opengl显示图片,C,C++源码.zip

    4. **绘制图像**:使用OpenGL的函数(如glGenTextures、glBindTexture、glTexImage2D等)将图像数据加载到纹理对象中,然后通过顶点数组和着色器程序在屏幕上绘制图像。 5. **更新和显示**:在QT事件循环中,定期...

    qt qml opengl(qt封装接口) 画笔实例

    接着,我们可以使用`glBegin()`, `glVertex()`, `glEnd()`等函数来绘制线条或形状,或者使用现代OpenGL的顶点数组和着色器来实现更复杂的渲染。 ```cpp class OpenGLPaint : public QQuickItem { Q_OBJECT Q_...

    基于QT4的OpenGL代码

    在QT4中,更常见的是使用QGLShaderProgram和顶点数组对象(VAOs)结合纹理映射来实现更高效、更灵活的文本渲染。 7. **资源文件**:在QT4中,资源系统允许开发者将资源(如图像、音频、字体等)打包到应用程序中,...

    图形字体的QT opengl实现

    7. **清理资源**:在关闭窗口或应用退出时,记得释放分配的OpenGL资源,如删除纹理和清除顶点数组。 在压缩包的"lesson14"中,很可能包含了实现这一过程的源代码,包括Qt项目文件、头文件、源代码文件以及可能的...

    Qt5_Samuel_Rodal_on_Qt_GUI_OpenGL

    5. **顶点数组对象(VAO)、顶点缓冲对象(VBO)和索引缓冲对象(IBO)**:这些是用于高效存储和传输几何数据的核心OpenGL概念,学习如何在Qt环境中创建和使用它们。 6. **纹理映射**:学习如何加载图像资源并将其...

    qt5_qml_opengl_shader显示yuv

    在本例中,我们可以创建一个自定义的QML元件,该元件负责加载和显示YUV数据。 2. **OpenGL Shader**:OpenGL着色器是运行在GPU上的小程序,负责处理顶点、片段等渲染过程中的计算。在我们的场景中,我们编写两个...

    C++下的OpenGL文字显示的完美解决方案

    5. **渲染**:在OpenGL的渲染循环中,使用顶点数组和纹理进行渲染。 在`Text.cpp`中,可能会有创建、销毁字体对象,以及绘制文本的函数。`gmath.cpp`则可能包含了旋转、平移、缩放等函数,帮助在3D空间中正确地定位...

Global site tag (gtag.js) - Google Analytics