OpenGL几种绘制方式

OpenGL几种绘制方式

OpenGL

绘制

本文介绍了OpenGL的几种绘制方式及各自特点。绘制方式如下:

  • 立即模式
  • 显示列表
  • 顶点数组
  • VBO

1、立即模式

最直接的方式,传统的使用glBegin...glEnd绘制的方式,如下所示:

glBegin( GL_TRIANGLES );

glVertex3f(-1.0f, -0.5f, -4.0f);
glVertex3f( 1.0f, -0.5f, -4.0f);
glVertex3f( 0.0f, 0.5f, -4.0f);

glEnd();

这种方式效率较低。原因是

  • glVertex函数每次调用只把一个顶点从客户端(CPU或内存)传输到服务端(GPU),而这个传输的过程相对于GPU处理数据的过程是很慢的。
  • glVertex函数的调用次数过多

2、显示列表

将图形绘制进行预编译,把绘制好的图形放到GPU,使用的时候直接调用。

初始化函数

displayList = glGenLists( 1 );  //请求显示列表名称

glNewList( displayList, GL_COMPILE );   //创建显示列表

glBegin( GL_TRIANGLES );
glVertex3f(-1.0f, -0.5f, -4.0f);
glVertex3f( 1.0f, -0.5f, -4.0f);
glVertex3f( 0.0f, 0.5f, -4.0f);
glEnd();

glEndList();

渲染函数

glCallList( displayList );

显示列表的优化策略是将图形绘制命令集(数据块)存储在GPU中,无须CPU到GPU间的数据传递,节省了时间。

然而显示列表虽然提升了绘制速度,但是它一旦创建了就是不可修改的,如果要修改,只能销毁并重新创建显示列表,所以它适用于那些静态的图形。

3、顶点数组

将数据保存在数组中,当执行绘制(glDrawArrays或glDrawElements)的时候一次性将数据从CPU传递到GPU中。

//初始化顶点数组
GLfloat vertices[ 3 ][3] = {0};
vertices[0][0] = -1.0f;
vertices[0][1] = -0.5f;
vertices[0][2] = -4.0f;

vertices[1][0] = 1.0f;
vertices[1][1] = -0.5f;
vertices[1][2] = -4.0f;

vertices[2][0] = 0.0f;
vertices[2][1] = 0.5f;
vertices[2][2] = -4.0f;

glEnableClientState( GL_VERTEX_ARRAY );         //启用顶点数组
glVertexPointer( 3, GL_FLOAT, 0, vertices );    //指定数据
glDrawArrays( GL_TRIANGLES, 0, 3);              //进行绘图
glDisableClientState( GL_VERTEX_ARRAY );

顶点数据的优化策略是减少数据从CPU到GPU的传递次数,从而节省了时间。但是它仍然有数据的传递,所以绘制效率不如显示列表高。相比于显示列表它的优点是数据可以动态修改。

4、VBO

VBO将顶点数据存储在GPU缓存中,无须CPU到GPU的数据传递,并且可以动态修改。

初始化函数

//初始化顶点数组
GLfloat vertices[ 3 ][3] = {0};
vertices[0][0] = -1.0f;
vertices[0][1] = -0.5f;
vertices[0][2] = -4.0f;

vertices[1][0] = 1.0f;
vertices[1][1] = -0.5f;
vertices[1][2] = -4.0f;

vertices[2][0] = 0.0f;
vertices[2][1] = 0.5f;
vertices[2][2] = -4.0f;

glewInit();
glGenBuffers( 1, buffer );  //创建缓冲区

//指定缓冲区数据
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 3, vertices, GL_DYNAMIC_DRAW );

渲染函数

glBindBuffer( GL_ARRAY_BUFFER, buffer );    //绑定缓冲区
glEnableClientState( GL_VERTEX_ARRAY ); //使用VBO必须开启顶点数组
glVertexPointer( 3, GL_FLOAT, 0, 0 );
glDrawArrays( GL_TRIANGLES, 0, 3 ); //绘制
glDisableClientState( GL_VERTEX_ARRAY );
glBindBuffer( GL_ARRAY_BUFFER, 0 );     //取消绑定缓冲区

关于VBO的动态修改,可以将数据映射到客户端(内存)中,然后再进行修改。以下是一种刷新所有顶点数据的方法。

//初始化新数组
GLfloat newvertices[3][3] = {0};
newvertices[0][0] = 0.0f;
newvertices[0][1] = 0.0f;
newvertices[0][2] = 50.0f;
...

//映射缓冲区
glBindBuffer( GL_ARRAY_BUFFER, buffer] );
glBufferData( GL_ARRAY_BUFFER, sizeof( GLfloat ) * 3* 3, NULL, GL_STREAM_DRAW );
GLvoid* PositionBuffer = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); 

//刷新数据
memcpy( PositionBuffer, newvertices, sizeof( GLfloat ) * 1002 * 3 );

//刷新到VBO
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glUnmapBuffer( GL_ARRAY_BUFFER );
glVertexPointer( 3, GL_FLOAT, 0, 0 );

VBO结合了显示列表与顶点数组的优点,既在GPU保存数据避免数据传输,提高了绘制效率,又可以动态修改。

[参考]

OpenGL超级宝典(第四版)

http://www.songho.ca/opengl/gl_vbo.html

http://www.zwqxin.com/archives/opengl/learn-vbo.html

时间: 2024-12-25 04:20:39

OpenGL几种绘制方式的相关文章

opengl两种投影类型

openGL两种投影方式 from http://hi.baidu.com/fcqian/blog/item/cc5794ec76807a3f27979131.html 投影变换是一种很关键的图形变换,OpenGL中只提供了两种投影方式,一种是正射投影,另一种是透视投影.不管是调用哪种投影函数,为了避免不必要的变换,其前面必须加上以下两句: glMAtrixMode(GL_PROJECTION); glLoadIdentity(); 事实上,投影变换的目的就是定义一个视景体,使得视景体外多余的部

OpenGL学习脚印: 绘制一个三角形

写在前面 接着上一节内容,开发环境搭建好后,我们当然想立即编写3D应用程序了.不过我们还需要些耐心,因为OpenGL是一套底层的API,因而我们要掌握的基本知识稍微多一点,在开始绘制3D图形之前,本节我们将通过绘制一个三角形的程序来熟悉现代OpenGL的概念和流程. 通过本节可以了解到: 缓存对象VAO和VBO GLSL着色器程序的编译.链接和使用方法 OpenGL绘图的基本流程 绘图流水线简要了解 与使用高级绘图API(例如java里swing绘图,MFC里的绘图)不同,使用OpenGL绘制图

Android OpenGL入门示例:绘制三角形和正方形 (附完整源码)

Android上对OpenGl的支持是无缝的,所以才有众多3D效果如此逼真的游戏,在Camera的一些流程中也有用到GLSurfaceView的情况.本文记录OpenGL在Android上的入门级示例,绘制一个三角形和正方形.尽管功能简单,可是我捣腾了好几个晚上,大量网上文章上的代码都有点问题,不是绘制不出来就是挂了. 第一个文件:MainActivity.java package com.example.learnopengl1; import android.opengl.GLSurface

Qt 2D绘图 渐变填充(三种渐变方式)

在qt中提供了三种渐变方式,分别是线性渐变,圆形渐变和圆锥渐变.如果能熟练应用它们,就能设计出炫目的填充效果. 线性渐变: 1.更改函数如下: void Dialog::paintEvent(QPaintEvent *){    QPainter painter(this);    QLinearGradient linearGradient(100,150,300,150);    //从点(100,150)开始到点(300,150)结束,确定一条直线    linearGradient.se

Android自定义控件:进度条的四种实现方式

前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源码在最后) 最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习.发现读起来都不太好懂,就想写这么一篇东西作为学习笔记吧. 一.控件介绍: 进度条在App中非常常见,例如下载进度.加载图片.打开文章.打开网页等等--都需要这么一个效果让用户知道我们的App正在读取,以构造良好的

canvas入门-1三种填充方式、渐变、模式

1.定义canvas的尺寸的时候最好用html的方式定义,用width和height的方式,用css会导致画布按照css设定的方式进行缩放,cavas内部是一个2d的渲染环境 2.一个canvas对应一个2d的渲染环境,绘制图形的操作都是在2d渲染环境中进行的 <canvas id="canvas-1" style="border:solid 1px gray;" width = "400" height="400"&g

Android自己定义控件:进度条的四种实现方式

前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源代码下载)http://download.csdn.net/detail/chaoyu168/9616035 近期一直在学习自己定义控件,搜了很多大牛们Blog里分享的小教程.也上GitHub找了一些类似的控件进行学习.发现读起来都不太好懂,就想写这么一篇东西作为学习笔记吧. 一.控件介绍: 进度条在App中非经常见,比例如以下载

iOS的四种传值方式

传值有四种方法 : 1.属性传值 2.单例传值 3.代理传值 4.block传值 一.属性传值   (前-->后) 1. 后面的界面定义一个属性  存放前一个界面传过来的值 注意:属性定义成字符串还是别的类型 取决于你的需求 2. 后面的界面创建完毕后,为属性赋值(记录需要传递的值) 3. 在需要使用值的地方  使用属性记录的值 4. 通过 定义属性 达到传值目的的方法 称为属性传值 属性传值一般用于 从前一个界面向后一个界面传值 二.单例传值  (万能的传值方式  可以跨多个页面之间进行传值)

Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)(源码 + Demo)

Progress Wheel为GitHub热门项目,作者是:Todd-Davies,项目地址: https://github.com/Todd-Davies/ProgressWheel 前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源码在最后) 最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习.发现读起来都不太好懂,就想