OpenGL中的VBO是显存地址空间中的空间吗

1676人阅读
OpenGL(28)
图形渲染管线(Pipeline)
图形渲染管线指的是对一些原始数据经过一系列的处理变换并最终把这些数据输出到屏幕上的整个过程。
图形渲染管线的整个处理流程可以被划分为几个阶段,上一个阶段的输出数据作为下一个阶段的输入数据,是一个串行的,面向过程的执行过程。每一个阶段分别在GPU上运行各自的数据处理程序,这个程序就是着色器。
部分着色器允许我们使用着色语言(OpenGL Shading Language)编写自定义的着色器,这样就可以更为细致的控制图像渲染流程中的特定处理过程了,下图是一个图形渲染管线每一个阶段的抽象表示,蓝色部分代表允许自定义着色器。
顶点数据是一些顶点的集合,顶点一般是3维的点坐标组成。
基本图元(Primitives)包括点,线段,三角形等,是构成实体模型的基本单位,需要在传入顶点数据的同时通知OpenGL这些顶点数据要组成的基本图元类型。
顶点着色器(Vertex Shader)包含对一些顶点属性(数据)的基本处理。
基本图元装配(Primitive Assembly)把所有输入的顶点数据作为输入,输出制定的基本图元。
几何着色器(Geometry Shader)把基本图元形式的顶点的集合作为输入,可以通过产生新顶点构造出新的(或是其他的)基本图元来生成其他形状。
细分着色器(Tessellation Shaders)可以把基本图元细分为更多的基本图形,创建出更加平滑的视觉效果。
光栅化(Rasterization)即像素化,把细分着色器输出的基本图形映射为屏幕上网格的像素点,生成供片段着色器处理的片段(Fragment),光栅化包含一个剪裁操作,会舍弃超出定义的视窗之外的像素。
片段着色器(Fragment Shader)的主要作用是计算出每一个像素点最终的颜色,通常片段着色器会包含3D场景的一些额外的数据,如光线,阴影等。
测试与混合是对每个像素点进行深度测试,Alpha测试等测试并进行颜色混合的操作,这些测试与混合操作决定了屏幕视窗上每个像素点最终的颜色以及透明度。
在整个渲染管线中需要自定义处理的主要是顶点着色器和片段着色器。
顶点缓冲对象(Vertex Buffer Objects,VBO)
顶点缓冲对象VBO是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法向量,顶点颜色数据等。在渲染时,可以直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高。
所以可以理解为VBO就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个VBO,每个VBO在OpenGL中有它的唯一标识ID,这个ID对应着具体的VBO的显存地址,通过这个ID可以对特定的VBO内的数据进行存取操作。
VBO的创建以及配置
创建VBO的第一步需要开辟(声明/获得)显存空间并分配VBO的ID:
//创建vertex buffer object对象
GLuint vboId;//vertex buffer object句柄
glGenBuffers(1, &vboId);
创建的VBO可用来保存不同类型的顶点数据,创建之后需要通过分配的ID绑定(bind)一下制定的VBO,对于同一类型的顶点数据一次只能绑定一个VBO。绑定操作通过glBindBuffer来实现,第一个参数指定绑定的数据类型,可以是GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER或者GL_PIXEL_UNPACK_BUFFER中的一个。
glBindBuffer(GL_ARRAY_BUFFER, vboId);
接下来调用glBufferData把用户定义的数据传输到当前绑定的显存缓冲区中。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
顶点数据传入GPU之后,还需要通知OpenGL如何解释这些顶点数据,这个工作由函数glVertexAttribPointer完成:
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
第一个参数指定顶点属性位置,与顶点着色器中layout(location=0)对应。
第二个参数指定顶点属性大小。
第三个参数指定数据类型。
第四个参数定义是否希望数据被标准化。
第五个参数是步长(Stride),指定在连续的顶点属性之间的间隔。
第六个参数表示我们的位置数据在缓冲区起始位置的偏移量。
顶点属性glVertexAttribPointer默认是关闭的,使用时要以顶点属性位置值为参数调用glEnableVertexAttribArray开启。如glEnableVertexAttribArray(0);
顶点数组对象(Vertex Arrary Object,VAO)
VBO保存了一个模型的顶点属性信息,每次绘制模型之前需要绑定顶点的所有信息,当数据量很大时,重复这样的动作变得非常麻烦。VAO可以把这些所有的配置都存储在一个对象中,每次绘制模型时,只需要绑定这个VAO对象就可以了。
VAO是一个保存了所有顶点数据属性的状态结合,它存储了顶点数据的格式以及顶点数据所需的VBO对象的引用。
VAO本身并没有存储顶点的相关属性数据,这些信息是存储在VBO中的,VAO相当于是对很多个VBO的引用,把一些VBO组合在一起作为一个对象统一管理。
VAO的创建和配置
生成一个VAO对象并绑定:
//创建vertex array object对象
GLuint vaoId;//vertext array object句柄
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
执行VAO绑定之后其后的所有VBO配置都是这个VAO对象的一部分,可以说VBO是对顶点属性信息的绑定,VAO是对很多个VBO的绑定。
OpenGL中所有的图形都是通过分解成三角形的方式进行绘制,glDrawArrays函数负责把模型绘制出来,它使用当前激活的着色器,当前VAO对象中的VBO顶点数据和属性配置来绘制出来基本图形。
glDrawArrays (GLenum mode, GLint first, GLsizei count)
第一个参数表示绘制的类型,有三种取值:
1.GL_TRIANGLES:每三个顶之间绘制三角形,之间不连接;2.GL_TRIANGLE_FAN:以V0V1V2,V0V2V3,V0V3V4,……的形式绘制三角形;3.GL_TRIANGLE_STRIP:顺序在每三个顶点之间均绘制三角形。这个方法可以保证从相同的方向上所有三角形均被绘制。以V0V1V2,V1V2V3,V2V3V4……的形式绘制三角形;
第二个参数定义从缓存中的哪一位开始绘制,一般定义为0;
第三个参数定义绘制的顶点数量;
索引缓冲对象(Element Buffer Object,EBO)
索引缓冲对象EBO相当于OpenGL中的顶点数组的概念,是为了解决同一个顶点多洗重复调用的问题,可以减少内存空间浪费,提高执行效率。当需要使用重复的顶点时,通过顶点的位置索引来调用顶点,而不是对重复的顶点信息重复记录,重复调用。
EBO中存储的内容就是顶点位置的索引indices,EBO跟VBO类似,也是在显存中的一块内存缓冲器,只不过EBO保存的是顶点的索引。
创建EBO并绑定,用glBufferData(以GL_ELEMENT_ARRAY_BUFFER为参数)把索引存储到EBO中:
GLuint EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
当用EBO绑定顶点索引的方式绘制模型时,需要使用glDrawElements而不是glDrawArrays:
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
第一个参数指定了要绘制的模式;
第二个参数指定要绘制的顶点个数;
第三个参数是索引的数据类型;
第四个参数是可选的EBO中偏移量设定。
Talk is cheap,第一个例子是使用VBO,VAO绘制一个矩形图形:
//使用VAO VBO绘制矩形
#include &GL/glew.h&
#include &GL/freeglut.h&
void userInit();
//自定义初始化
void reshape(int w, int h);
void display(void);
void keyboardAction(unsigned char key, int x, int y);
//键盘退出事件
GLuint vboId;//vertex buffer object句柄
GLuint vaoId;//vertext array object句柄
GLuint programId;//shader program 句柄
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow(&Rectangle demo&);
//使用glew,需要执行glewInit,不然运行过程会报错
//glewInit要放在glut完成了基本的初始化之后执行
glewInit();
//自定义初始化,生成VAO,VBO对象
userInit();
//重绘函数
glutReshapeFunc(reshape);
glutDisplayFunc(display);
//注册键盘按键退出事件
glutKeyboardFunc(keyboardAction);
glutMainLoop();
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);
//创建顶点数据
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
//创建VAO对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建VBO对象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//传入VBO数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
//调整窗口大小回调函数
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableVertexAttribArray(0);
//解释顶点数据方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//绘制模型
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key)
// Escape key
exit(EXIT_SUCCESS);
编译并执行:
第二个例子使用EBO绘制两个三角形,组成同样的矩形图形:
//使用EBO绘制矩形(两个三角形)
#include &GL/glew.h&
#include &GL/freeglut.h&
void userInit();
//自定义初始化
void reshape(int w, int h);
void display(void);
void keyboardAction(unsigned char key, int x, int y);
//键盘退出事件
GLuint eboId;//element buffer object句柄
GLuint vboId;//vertext buffer object句柄
GLuint vaoId;//vertext array object句柄
int main(int argc, char **argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow(&Rectangle demo&);
//使用glew,需要执行glewInit,不然运行过程会报错
//glewInit要放在glut完成了基本的初始化之后执行
glewInit();
//自定义初始化,生成VAO,VBO,EBO
userInit();
//重绘函数
glutReshapeFunc(reshape);
glutDisplayFunc(display);
//注册键盘按键退出事件
glutKeyboardFunc(keyboardAction);
glutMainLoop();
//自定义初始化函数
void userInit()
glClearColor(0.0, 0.0, 0.0, 0.0);
//创建顶点数据
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
// 索引数据
GLshort indices[] = {
// 第一个三角形
// 第二个三角形
//创建VAO对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建VBO对象,把顶点数组复制到一个顶点缓冲中,供OpenGL使用
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//创建EBO对象
glGenBuffers(1, &eboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
//传入EBO数据
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//解释顶点数据方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//调整窗口大小回调函数
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//绘制回调函数
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vaoId);
//绘制模型
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
glutSwapBuffers();
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
switch (key)
// Escape key
exit(EXIT_SUCCESS);
效果一样:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:415219次
积分:5997
积分:5997
排名:第4164名
原创:189篇
评论:244条
阅读:14067
文章:51篇
阅读:198284OpenGL中的几何体实例化OpenGL Geometry Instancing_博客园
当前位置: >
>OpenGL中的几何体实例化OpenGL Geometry Instancing
OpenGL中的几何体实例化OpenGL Geometry Instancing
& 作者:江枫月 & 来源: 博客园-jamesclarke &
&&&&&&在GPU Gems 2中有一篇文章是专门介绍几何体实例化的,不过它是基于DirectX的。经过多年的发展,OpenGL在几何体实例化方面也做出了改进,于2008年在OpenGL3.0中正式引入实例化函数,该函数只被NVIDIA 8系列以上显卡支持。
&&&&&&几何体实例化,是通过对具有相同顶点数据的几何体,赋予不同的空间位置、颜色或纹理等特征,从而创造出不同实例对象的技术。尤其适合于草地、石块、数目、士兵群等对象的绘制。它的好处在于,只需传递一次单个模型的顶点数据(或者说调用一个批次的绘制指令),再传递不同对象的特征数据,就能实现多个对象的绘制。
&&&&&&最早,OpenGL是不支持几何体实例化的。它通过即时模式,调用glDrawElements等函数,能较高效的实现多个对象的绘制。然而,这还是会造成占用大量的VBO存储空间和数据传输带宽。
&&&&&&在GPU可编程后,通过设置全局统一参数glUniformMatrix4fvARB来传递对象的特征数据给Vertex Shader,来间接实现实例化。然而这还是不能够减少绘制指令的调用次数。
&&&&&&而后,NVIDIA提出了伪实例化(pseudo-instancing)的方法,利用顶点数据指令,如glColor3f、glTexCoord、glMultiTexCoord等,将对象的特征数据传递给Vertex Shader。虽然这同样不能减少绘制指令的调用次数,但它却比方法2提高了2~3倍的效率。原因有2:一此文来自: 马开东博客
转载请注明出处 网址:
是方法2中的全局统一参数函数设置的是GPU寄存器,或者说是改变Vertex shader当前的全局处理状态,很显然将等待当前GPU正在执行的Vertex process完成后才能返回,即需要CPU等待GPU完成该对象的绘制,存在严重的效率问题[1];二是全局统一参数函数设置的GPU寄存器是全局共享的,Vertex Shader拜访该寄存器所耗费的时间显然要多于拜访本地寄存器(由顶点数据指令设置的)所花费的时间。
&&&&&&最后,ARB终于通过了实例化的函数,有两个:DrawArraysInstancedARB、DrawElementsInstancedARB。在GLSL中,通过instance ID获取实例的ID,加以控制。这两个函数不能用在显示列表中。
&&&&&&JeGX对以上四种方法进行了,有兴趣的童鞋,可以点击链接去看看。测试的结论是[2]:
&&&&&&1、在OpenGL中,每个实例的顶点数越小,实例化带来的效率提升越明显。因此要尽量将实例模型的面数和顶点数控制到最小。
&&&&&&2、新的实例化函数,虽然减少了指令的个数,但当实例模型的面数和顶点数比较大的时候,好像没有带来太大的性能改变。
&&&&&&我曾经把特征数据编码进顶点纹理,通过Vertex Shader来获取,实现实例化,然而效果好像并不是非常明显(相较于第2种方法而言)。目前还没有试验第4种方法,希望以后有时间的时候来试验一下。
参考文献:
相关阅读:
来源:(微信/QQ:,微信公众号:makaidong-com) &&&&&& 欢迎分享本文,转载请保留出处!
&&&&&& 【原文阅读】:
上一篇:没有了
【相关文章】
每日最新文章
每日最热文章
本周最热文章
本月最热文章
本年最热文章
Powered by
Copyright &
, All Rights Reserved拒绝访问 | www.icax.org | 百度云加速
请打开cookies.
此网站 (www.icax.org) 的管理员禁止了您的访问。原因是您的访问包含了非浏览器特征(38b73bd7de864394-ua98).
重新安装浏览器,或使用别的浏览器OpenGL学习脚印: 基本图形绘制方式比较(glBegin,glCallList,glVertexPointer,VBO)
我的图书馆
OpenGL学习脚印: 基本图形绘制方式比较(glBegin,glCallList,glVertexPointer,VBO)
OpenGL学习脚印: 基本图形绘制方式比较
&&&&&&&&&&&&&&&本节主要讨论OpenGL下基本图形(points, lines, triangles, polygons, quads, fans and strips)的绘制方式,比较传统和现代绘制方式的区别。本文整理自网络,保留了部分原文,参考资料部分列出了主要参考内容。本节示例代码在vs2012下测试通过,如果发现了错误请纠正我。转载需经过作者同意。
通过本节,可以了解到:
传统立即模式Immediate Mode绘图传统显示列表Display List绘图顶点数组Vertex Arrays绘图
现代的VBO VAO绘图现代结合Shader的绘图
1.传统方式绘制
传统绘制方式在OpenGL新版本中已经废弃,不过部分情况下还能工作。这里列出他们仅供学习。
1.1&立即模式Immediate Mode
传统的使用glBegin...glEnd方式制定绘制方式,在这两个函数对之间给出绘制的数据,这种方式成为立即模式。
立即模式绘图,示例代码如下所示:
[cpp] //依赖库glew32.lib&freeglut.lib&&//使用glBegin...glEnd绘制三角形(已过时,仅为学习目的)&&#&&include&&GL/glew.h&&&#&&include&&GL/freeglut.h&&&&&void&userInit();&&void&reshape(int&w,int&h);&&void&display(&void&);&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&);&&&&int&main(&int&argc,&char&**argv&)&&{&&&&&&glutInit(&argc,&argv);&&&&&&&&glutInitDisplayMode(&GLUT_RGBA|GLUT_SINGLE);&&&&&&glutInitWindowPosition(100,100);&&&&&&glutInitWindowSize(&512,&512&);&&&&&&glutCreateWindow(&"Triangle&demo"&);&&&&&&&&&&&&&glewInit();&&&&&&userInit();&&&&&&glutReshapeFunc(reshape);&&&&&&glutDisplayFunc(&display&);&&&&&&glutKeyboardFunc(&keyboardAction&);&&&&&&glutMainLoop();&&&&&&return&0;&&}&&//自定义初始化函数&&void&userInit()&&{&&&&&&&glClearColor(&0.0,&0.0,&0.0,&0.0&);&&&&&&&glColor4f(1.0,1.0,0.0,0.0);&&}&&//调整窗口大小回调函数&&void&reshape(int&w,int&h)&&{&&&&&&glViewport(0,0,(GLsizei)w,(GLsizei)h);&&}&&//绘制回调函数&&void&display(&void&)&&{&&&&&&glClear(&GL_COLOR_BUFFER_BIT);&&&&&&//使用传统的glBegin...glEnd绘制三角形&&&&&&glBegin(GL_TRIANGLES);&&&&&&&&&&&&glVertex3f(-0.5,-0.5,0.0);&&&&&&&&&&&&&glVertex3f(0.5,0.0,0.0);&&&&&&&&&&&&&glVertex3f(0.0,0.5,0.0);&&&&&&&&glEnd();&&&&&&&&glFlush();&&}&&//键盘按键回调函数&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&)&&{&&&&&&switch(&key&)&&&&&&&{&&&&&&&&&&case&033:&&//&Escape&key&&&&&&&&&&&&&&exit(&EXIT_SUCCESS&);&&&&&&&&&&&&&&&&&&&&}&&}&&
本例及本文所有绘制效果都如下图所示:
1.2&显示列表Display List
显示列表是一组存储在一起的OpenGL函数,可以再以后执行。调用一个显示列表时,它所存储的函数就会按照顺序执行。显示列表通过存储OpenGL函数,可以提高性能。如果需要多次重复绘制同一个几何图形,或者如果有一些需要多次调用的用于更改状态的函数,就可以把他们存储在显示列表中。例如绘制三轮车的车轮的有效方法是,把绘制一个车轮的操作存储在显示列表中,并3次执行这个显示列表。每次在执行时,适当地做出坐标转换即可(参考自[2])。
显示列表,示例代码如下所示,执行效果同上图:
[cpp] //依赖库glew32.lib&freeglut.lib&&//使用顶点列表绘制三角形(已过时,仅为学习目的)&&&&#&&include&&GL/glew.h&&&#&&include&&GL/freeglut.h&&&&&void&userInit();&&void&reshape(int&w,int&h);&&void&display(&void&);&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&);&&//显示列表句柄&&GLuint&displayListId;&&int&main(&int&argc,&char&**argv&)&&{&&&&&&glutInit(&argc,&argv);&&&&&&&&glutInitDisplayMode(&GLUT_RGBA|GLUT_SINGLE);&&&&&&glutInitWindowPosition(100,100);&&&&&&glutInitWindowSize(&512,&512&);&&&&&&glutCreateWindow(&"Triangle&demo"&);&&&&&&&&&&&&&glewInit();&&&&&&userInit();&&&&&&glutReshapeFunc(reshape);&&&&&&glutDisplayFunc(&display&);&&&&&&glutKeyboardFunc(&keyboardAction&);&&&&&&glutMainLoop();&&&&&&return&0;&&}&&//自定义初始化函数&&void&userInit()&&{&&&&&&&glClearColor(&0.0,&0.0,&0.0,&0.0&);&&&&&&&glColor4f(1.0,1.0,0.0,0.0);&&&&&&&//创建显示列表&&&&&&&displayListId&=&glGenLists(1);&&&&&&&glNewList(displayListId,GL_COMPILE);&&&&&&&&&&&glBegin(GL_TRIANGLES);&&&&&&&&&&&&&&&glVertex3f(-0.5,-0.5,0.0);&&&&&&&&&&&&&&&&&glVertex3f(0.5,0.0,0.0);&&&&&&&&&&&&&&&&&glVertex3f(0.0,0.5,0.0);&&&&&&&&&&&glEnd();&&&&&&glEndList();&&}&&//调整窗口大小回调函数&&void&reshape(int&w,int&h)&&{&&&&&&glViewport(0,0,(GLsizei)w,(GLsizei)h);&&}&&//绘制回调函数&&void&display(&void&)&&{&&&&&&glClear(&GL_COLOR_BUFFER_BIT);&&&&&&//利用显示列表,绘制三角形&&&&&&glCallList(displayListId);&&&&&&glFlush();&&}&&//键盘按键回调函数&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&)&&{&&&&&&switch(&key&)&&&&&&&{&&&&&&&&&&case&033:&&//&Escape&key&&&&&&&&&&&&&&exit(&EXIT_SUCCESS&);&&&&&&&&&&&&&&&&&&&&}&&}&&
2.现代方式绘制
2.1&顶点数组绘图
使用顶点数组方式,需要利用glEnableClientState开启一些特性,这里开启顶点数组特性使用glEnableClientState(GL_VERTEX_ARRAY)。
使用顶点数组时,用户定义好存储顶点的数据,在调用glDrawArrays、glDrawElements之类的函数时,通过glVertexPointer设定的指针,传送数据到GPU。当调用完glDrawArrays后,GPU中已经有了绘图所需数据,用户可以释放数据空间。(参考自[3])
顶点数组方式绘图示例代码如下所示:
[cpp] //依赖库glew32.lib&freeglut.lib&&//使用Vertex&Arrays顶点数组绘制三角形(不推荐使用)&&#&&include&&GL/glew.h&&&#&&include&&GL/freeglut.h&&&&&void&userInit();&&void&reshape(int&w,int&h);&&void&display(&void&);&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&);&&//定义一个包含3个float的结构体&&//为了保持简单,暂时未引入c++类概念&&struct&vec3f&{&&&&&&&GLfloat&x,&y,&z;&&};&&int&main(&int&argc,&char&**argv&)&&{&&&&&&glutInit(&argc,&argv);&&&&&&&&glutInitDisplayMode(&GLUT_RGBA|GLUT_SINGLE);&&&&&&glutInitWindowPosition(100,100);&&&&&&glutInitWindowSize(&512,&512&);&&&&&&glutCreateWindow(&"Triangle&demo"&);&&&&&&&&&&&&glewInit();&&&&&&userInit();&&&&&&glutReshapeFunc(reshape);&&&&&&glutDisplayFunc(&display&);&&&&&&glutKeyboardFunc(&keyboardAction&);&&&&&&glutMainLoop();&&&&&&return&0;&&}&&//自定义初始化函数&&void&userInit()&&{&&&&&&&glClearColor(&0.0,&0.0,&0.0,&0.0&);&&&&&&&glColor4f(1.0,1.0,0.0,0.0);&&}&&//调整窗口大小回调函数&&void&reshape(int&w,int&h)&&{&&&&&&glViewport(0,0,(GLsizei)w,(GLsizei)h);&&}&&//绘制回调函数&&void&display(&void&)&&{&&&&&&glClear(&GL_COLOR_BUFFER_BIT);&&&&&&//利用顶点数组,绘制三角形&&&&&&const&int&num_indices&=&3;&&&&&&&//创建保存顶点的结构体数组&&&&&&&vec3f&*vertices&=&new&vec3f[num_indices];&&&&&&&//&顶点1&&&&&&&vertices[0].x&=&-0.5f;&&&&&&&&vertices[0].y&=&-0.5f;&&&&&&&&vertices[0].z&=&0.0f;&&&&&&&&//&顶点2&&&&&&&vertices[1].x&=&0.5f;&&&&&&&&vertices[1].y&=&0.0f;&&&&&&&&vertices[1].z&=&0.0f;&&&&&&&&//顶点3&&&&&&&vertices[2].x&=&0.0f;&&&&&&&&vertices[2].y&=&0.5f;&&&&&&&vertices[2].z&=&0.0f;&&&&&&&//&启用vertex&arrays&&&&&&&&glEnableClientState(GL_VERTEX_ARRAY);&&&&&&&//定义顶点数组&&&&&&&glVertexPointer(&&&&&&&&&&&&3,&&&&&&&&&//&每个顶点的维度&&&&&&&&&&&&GL_FLOAT,&&//&顶点数据类型&&&&&&&&&&&0,&&&&&&&&&//&连续顶点之间的间隙,这里为0&&&&&&&&&&&vertices&&&//指向第一个顶点的第一个坐标的指针&&&&&&);&&&&&&glDrawArrays(GL_TRIANGLES,&0,&num_indices);&&&&&&glDisableClientState(GL_VERTEX_ARRAY);&&&&&&//释放内存空间&&&&&&delete[]&&&&&&&glFlush();&&}&&//键盘按键回调函数&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&)&&{&&&&&&switch(&key&)&&&&&&&{&&&&&&&&&&case&033:&&//&Escape&key&&&&&&&&&&&&&&exit(&EXIT_SUCCESS&);&&&&&&&&&&&&&&&&&&&&}&&}&&
2.2&现代VBO VAO绘图
首先了解下VBO和VAO。
根据文[4]所述:
A Vertex Array Object (VAO) is an object which contains one or more Vertex Buffer Objects and is designed to store the information for a complete rendered object. In our example this is a diamond consisting of four vertices as
well as a color for each vertex.
A Vertex Buffer Object (VBO) is a memory buffer in the high speed memory of your video card designed to hold information about vertices. In our example we have two VBOs, one that describes the coordinates of our vertices and
another that describes the color associated with each vertex. VBOs can also store information such as normals, texcoords, indicies, etc.
VAO即Vertex Array Object ,是一个包含一个或多个VBO的对象,被设计用来存储一个完整被渲染对象所需的信息。
VBO即Vertex Buffer Object,是一个在高速视频卡中的内存缓冲,用来保存顶点数据,也可用于包含诸如归一化向量、纹理和索引等数据。
根据[1]中所述:
VBO stores actual vertex data. The most important thing about a VBO is not that it stores data, though it is its primary function, but where it is stored. A VBO object resides on GPU, the graphics processing
unit. This means it is very fast, it is stored in memory on the graphics card itself. How cool is that? Storing data on the computer processor or RAM is slow mostly because it needs to be transferred to the GPU, and this transfer can be costly.
VAO represents properties, rather than actual data. But these properties do describe the objects actually stored in theVBO.VAO can be thought of as anadvanced memory
pointer to objects. Similar to C-language pointers, they do a whole lot more tracking than just the address. They are very sophisticated.
VAOs are a lot like helpers, rather than actual data storage. That's what they're for. They also keep track of properties to be used in current rendering process. Finally, they describe properties of objects, rather than the
raw data of those objects that is by all means already stored in a VBO.
VAOs are not directly related to VBOs, although it may seem that way at first. VAOs simply save time to enable a certain application state needed to be set. Without VAO, you would have to call a bunch of gl* commands to do the
same thing.
VBO存储了实际的数据,真正重要的不是它存储了数据,而是他将数据存储在GPU中。这意味着VBO它会很快,因为存在RAM中的数据需要被传送到GPU中,因此这个传送是有代价的。
VAO代表的是一些描述存储在VBO中对象的属性。VAO可以被视为指向对象的高级内存指针,有点类似于C语言指针,但比地址多了跟多的跟踪作用。他们很复杂。
VAO很像是辅助对象,而不是实际的数据存储对象。他们记录了当前绘制过程中的属性,描述对象的属性,而不是已经存储在VBO中原始数据。
VAO并不与VBO直接相关,进过初看起来如此。VAOs节省了设置程序所需的状态的时间。如果没有VAO,你需要调用一堆类似gl*之类的命令。这里从songho[5]文的用户反馈列表中找到一个示例解释了VAO节省时间的例子:
提问:How do Vertex Buffer Objects relate to Vertex Array Objects?
songho回答:
The name, VAO (Vertex Array Object) looks somewhat related to VBO, but it is not. VAO is for encapsulating vertex array states/functions into it. Therefore,
you can replace the multiple OpenGL calls to a single call of glBindVertexArray(), in order to setup various vertex array states and attributes before drawing.
The following example gives a better sense of VAO
[cpp] //&draw&with&VAO&&glBindVertexArray(vaoId);&//&bind&vao&&glDrawElements(...);&&glBindVertexArray(0);&&&&&//&unbind&vao&&&&//&draw&without&VAO&&//&need&to&set&many&states&before&drawing&&glEnableClientState(GL_VERTEX_ARRAY);&//&enable&client&states&&glEnableClientState(GL_NORMAL_ARRAY);&&glBindBuffer(GL_ARRAY_BUFFER,&vboId);&//&bind&vbo&&glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,&iboId);&&glVertexPointer(3,&GL_FLOAT,&0,&0);&//&vertex&attributes&&glNormalPointer(GL_FLOAT,&0,&offset);&//&normal&attributes&&glDrawElements(...);&&glBindBuffer(GL_ARRAY_BUFFER,&0);&//&unbind&vbo&&glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,&0);&&glDisableClientState(GL_VERTEX_ARRAY);&&glDisableClientState(GL_NORMAL_ARRAY);&/span&&/span&&&You can dramatically reduce the function call overhead and make the code much simpler with VAO.
But the overall performance gain is very minimal.
从这里可以看出使用VAO方式的好处,更多关于VAO、VBO讨论不在此处展开。
单独使用VBO执行绘制的示例代码如下:
(这里并没有使用VAO,因此仍然要使用glEnableClientState(GL_VERTEX_ARRAY),稍后会给出VAO与VBO结合例子)
[cpp] //依赖库glew32.lib&freeglut.lib&&//使用VBO绘制三角形(现代OpenGL方式)&&#&&include&&GL/glew.h&&&#&&include&&GL/freeglut.h&&&&&void&userInit();&&void&reshape(int&w,int&h);&&void&display(&void&);&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&);&&//VBO句柄&&GLuint&vboId;&&int&main(&int&argc,&char&**argv&)&&{&&&&&&glutInit(&argc,&argv);&&&&&&&&glutInitDisplayMode(&GLUT_RGBA|GLUT_SINGLE);&&&&&&glutInitWindowPosition(100,100);&&&&&&glutInitWindowSize(&512,&512&);&&&&&&glutCreateWindow(&"Triangle&demo"&);&&&&&&&&&&&&&glewInit();&&&&&&userInit();&&&&&&glutReshapeFunc(reshape);&&&&&&glutDisplayFunc(&display&);&&&&&&glutKeyboardFunc(&keyboardAction&);&&&&&&glutMainLoop();&&&&&&return&0;&&}&&//自定义初始化函数&&void&userInit()&&{&&&&&&&glClearColor(&0.0,&0.0,&0.0,&0.0&);&&&&&&&glColor4f(1.0,1.0,0.0,0.0);&&&&&&&//创建顶点数据&&&&&&&GLfloat&vertices[]&=&{&&&&&&&&&&-0.5,-0.5,0.0,&&&&&&&&&&0.5,0.0,0.0,&&&&&&&&&&0.0,0.5,0.0&&&&&&&};&&&&&&&//分配vbo句柄&&&&&&&glGenBuffersARB(1,&vboId);&&&&&&&//GL_ARRAY_BUFFER_ARB表示作为顶点数组解析&&&&&&&glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboId);&&&&&&&//拷贝数据&&&&&&&glBufferDataARB(GL_ARRAY_BUFFER_ARB,sizeof(vertices),&&&&&&&&&&&&&&vertices,GL_STATIC_DRAW_ARB);&&&&&&&glBindBufferARB(GL_VERTEX_ARRAY,0);&&}&&//调整窗口大小回调函数&&void&reshape(int&w,int&h)&&{&&&&&&glViewport(0,0,(GLsizei)w,(GLsizei)h);&&}&&//绘制回调函数&&void&display(&void&)&&{&&&&&&glClear(&GL_COLOR_BUFFER_BIT);&&&&&&glBindBufferARB(GL_ARRAY_BUFFER_ARB,&vboId);//绑定vbo&&&&&&glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性&&&&&&glVertexPointer(3,&GL_FLOAT,&0,&0);//如何解析vbo中数据&&&&&&glDrawArrays(GL_TRIANGLES,&0,&3);&&&&&&glDisableClientState(GL_VERTEX_ARRAY);&&&&&&glBindBufferARB(GL_ARRAY_BUFFER_ARB,0);//解除绑定&&&&&&glFlush();&&}&&//键盘按键回调函数&&void&keyboardAction(&unsigned&char&key,&int&x,&int&y&)&&{&&&&&&switch(&key&)&&&&&&&{&&&&&&&&&&case&033:&&//&Escape&key&&&&&&&&&&&&&&exit(&EXIT_SUCCESS&);&&&&&&&&&&&&&&&&&&&&}&&}&&
2.3&结合Shader绘图
结合现代Shader的绘图,不再此处展开,请参见另外一篇博客《》。
3.总结几种方式利弊
使用立即模式的缺点很明显,数据量大一点的话,代码量增加,而且数据发送到服务端需要开销;
使用显示列表,显示列表是一个服务端函数,因此它免除了传送数据的额外开销。但是,显示列表一旦编译后,其中的数据无法修改。
使用顶点数组,可以减少函数调用和共享顶点数据的冗余。但是,使用顶点数组时,顶点数组相关函数是在客户端,因此数组中数据在每次被解引用时必须重新发送到服务端,额外开销不可忽视。
使用VBO在服务端创建缓存对象,并且提供了访问函数来解引用数组;例如在顶点数组中使用的函数如 glVertexPointer(), glNormalPointer(), glTexCoordPointer()。同时,VBO内存管理会根据用户提示,"target"& 和"usage"模式,将缓存对象放在最佳地方。因此内存管理会通过在系统内存、AGP内存和视频卡内存(system, AGP and video memory)这3中内存见平衡来优化缓存。另外,不像显示列表,VBO中数据可以通过映射到客户端内存空间而被用户读取和更新。VBO的另外一个优势是它像显示列表和纹理一样,能和多个客户端共享缓存对象。可见使用VBO优势很明显。(参考自:[5])
4.参考资料
[2]: 《OpenGL编程指南》&红宝书&第七版
TA的最新馆藏[转]&[转]&[转]&[转]&[转]&[转]&
喜欢该文的人也喜欢

我要回帖

更多关于 显存的作用 的文章

 

随机推荐