OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别,常量顶点属性

1.glVertex

  最原始的设置顶点方法,在glBegin和glEnd之间使用。OpenGL3.0已经废弃此方法。每个glVertex与GPU进行一次通信,十分低效。

glBegin(GL_TRIANGLES);

    glVertex(0, 0);

    glVertex(1, 1);

    glVertex(2, 2);

glEnd();

 

还有一种类似不过应该叫做VBO(逐顶点的)一般用于带顶点着色器的:

glVertexAttrib2f  glVertexAttrib*这些也叫常量顶点属性

1). Constant Vertex Attribute

一个常量顶点属性对于一个图元的所有顶点都是相同的,所以只需指定一次值就可以了:

void glVertexAttrib1f(GLuint index, GLfloat x);
void glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y);
void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z);
void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);

void glVertexAttrib1fv(GLuint index, const GLfloat * values);
void glVertexAttrib2fv(GLuint index, const GLfloat * values);
void glVertexAttrib3fv(GLuint index, const GLfloat * values);
void glVertexAttrib4fv(GLuint index, const GLfloat * values);

上面这些命令用来给由index指定的顶点属性加载数据。其中glVertexAttrib1fglVertexAttrib1fv会加载(x, 0.0, 0.0, 1.0),glVertexAttrib2fglVertexAttrib2fv会加载(x, y, 0.0, 1.0),glVertexAttrib3fglVertexAttrib3fv会加载(x, y, z, 1.0),glVertexAttrib4fglVertexAttrib4fv会加载(x, y, z, w)。在实际中,常量顶点属性提供了和uniform等价的功能,两者都可以选用。

2.显示列表(glCallList)

  每个glVertex调用都与GPU进行一次通信,显示列表是收集好所有的顶点,一次性的发送给GPU。缺点是在绘制之前就要把要传给GPU的顶点准备好,传后就不能修改了。

GLuint glassList;
2 glNewList(glassList, GL_COMPILE);
3     DrawGlass();
4 glEndList();
5 
6 glCallList(glassList); //DrawGlass();

3.顶点数组(Vertex Array)

  顶点数组也是收集好所有的顶点,一次性发送给GPU。不过数据不是存储于GPU中的,绘制速度上没有显示列表快,优点是可以修改数据。

显示列表和顶点数组都是过时的东西了,下面的VBO和VAO才是重点!

#define MEDIUM_STARS   40

M3DVector2f vMediumStars[MEDIUM_STARS];

//在这做点vMediumStars的设置//

glVertexPointer(2, GL_FLOAT, 0, vMediumStars);

glDrawArrays(GL_POINTS, 0, MEDIUM_STARS);

 

4.VBO(Vertex Buffer Object)顶点缓冲区对象

  VBO,全称为Vertex Buffer Object,与FBO,PBO并称,但它实际上老不少。就某种意义来说,他就是VA(Vertex Array)的升级版。VBO出现的背景是人们发现VA和显示列表还有让人不满足的地方。一般,在OpenGL里,提高顶点绘制的办法:

 (1)显示列表:把常规的glBegin()-glEnd()中的代码放到一个显示列表中(通常在初始化阶段完成),然后每遍渲染都调用这个显示列表。

 (2)VA:使用顶点数组,把顶点以及顶点属性数据作为数组,渲染的时候直接用一个或几个函数调动这些数组里的数据进行绘制,形式上是减少函数调用的次数(告别glVertex),提高绘制效率。

  但是,这两种方法都有缺点。VA是在客户端设置的,所以执行这类函数(glDrawArray或glDrawElement)后,客户端还得把得到的顶点数据向服务端传输一次(所谓的“二次处理”),这样一来就有了不必要的动作了,降低了效率——如果我们写的函数能直接把顶点数据发送给服务端就好了——这正是VBO的特性之一。显示列表的缺点在于它的古板,一旦设定就不容许修改,所以它只适合对一些“固定”的东西的绘制进行包装。(我们无办法直接在硬件层改顶点数据,因为这是脱离了流水线的事物)。而VBO直接把顶点数据交到流水线的第一步,与显示列表的效率还是有差距,但它这样就得到了操作数据的弹性——渲染阶段,我们的VBO绘制函数持续把顶点数据交给流水线,在某一刻我们可以把该帧到达了流水线的顶点数据取回客户端修改(Vertex mapping),再提交回流水线(Vertex unmapping),或者用glBufferData或glBufferSubData重新全部或buffer提交修改了的顶点数据,这是VBO的另一个特性。

      VBO结合了VA和显示列表这个说法不太妥当,应该说它结合了两者的一些特性,绘制效率在两者之间,且拥有良好的数据更改弹性。这种折衷造就了它一直为目前最高的地位。

//创建VBO及VBO赋值

glGenBuffers(1, &m_nPositionVBO);

glBufferData(GL_ARRAY_BUFFER,

    sizeof(posData), posData, GL_STREAM_DRAW);



glGenBuffers(1, &m_nTexcoordVBO);

glBufferData(GL_ARRAY_BUFFER,

    sizeof(texData), texData, GL_STREAM_DRAW);



glGenBuffers(1, &m_nIndexVBO);

glBufferData(GL_ELEMENT_ARRAY_BUFFER,

    sizeof(indexData), indexData, GL_STATIC_DRAW);



//代码一,不使用shader VBO已经创建好了

glBindBuffer(GL_ARRAY_BUFFER, m_nPositionVBO);

glEnableClientState(GL_VERTEX_ARRAY);

glVertexPointer(2, GL_FLOAT, 0, NULL);



glBindBuffer(GL_ARRAY_BUFFER, m_nTexcoordVBO);

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glTexCoordPointer(2, GL_FLOAT, 0, NULL);



glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIndexVBO);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);



glDisableClientState(GL_TEXTURE_COORD_ARRAY);

glDisableClientState(GL_VERTEX_ARRAY);



glBindBuffer(GL_ARRAY_BUFFER, NULL);



//代码二,使用shader

glBindBuffer(GL_ARRAY_BUFFER, m_nPositionVBO);

glEnableVertexAttribArray(VAT_POSITION);

glVertexAttribPointer(VAT_POSITION, 2, GL_INT, GL_FALSE, 0, NULL);



glBindBuffer(GL_ARRAY_BUFFER, m_nTexcoordVBO);

glEnableVertexAttribArray(VAT_TEXCOORD);

glVertexAttribPointer(VAT_TEXCOORD, 2, GL_INT, GL_FALSE, 0, NULL);



glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIndexVBO);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);



glDisableVertexAttribArray(VAT_POSITION);

glDisableVertexAttribArray(VAT_TEXCOORD);



glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);

glBindBuffer(GL_ARRAY_BUFFER, NULL);

 

5.VAO(Vertex Array Object)顶点数组对象

  VBO将顶点信息放到GPU中,GPU在渲染时去缓存中取数据,二者中间的桥梁是GL-Context。GL-Context整个程序一般只有一个,所以如果一个渲染流程里有两份不同的绘制代码,GL-context就负责在他们之间进行切换。这也是为什么要在渲染过程中,在每份绘制代码之中会有glBindbuffer、glEnableVertexAttribArray、glVertexAttribPointer。那么优化的方法来了,把这些都放到初始化时候完成吧!VAO记录该次绘制所需要的所有VBO所需信息,把它保存到VBO特定位置,绘制的时候直接在这个位置取信息绘制。

  VAO的全名是Vertex Array Object,首先,它不是Buffer-Object,所以不用作存储数据;其次,它针对“顶点”而言,也就是说它跟“顶点的绘制”息息相关。(VAO和VA没有任何关系)

  VAO记录的是一次绘制中所需要的信息,这包括“数据在哪里glBindBuffer”、“数据的格式是怎么样的glVertexAttribPointer”、shader-attribute的location的启用glEnableVertexAttribArray。

glGenBuffers(1, &m_nQuadPositionVBO); 

glBindBuffer(GL_ARRAY_BUFFER, m_nQuadPositionVBO); 

glBufferData(GL_ARRAY_BUFFER, sizeof(fQuadPos), fQuadPos, GL_STREAM_DRAW); 

  

glGenBuffers(1, &m_nQuadTexcoordVBO); 

glBindBuffer(GL_ARRAY_BUFFER, m_nQuadTexcoordVBO); 

glBufferData(GL_ARRAY_BUFFER, sizeof(fQuadTexcoord), fQuadTexcoord, GL_STREAM_DRAW); 



glGenBuffers(1, &m_nQuadIndexVBO); 

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nQuadIndexVBO); 

glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(nQuadIndex), nQuadIndex, GL_STREAM_DRAW); 



//VAO 初始化部分

glGenVertexArrays(1, &m_nQuadVAO);

glBindVertexArray(m_nQuadVAO);



//开始保存状态

glBindBuffer(GL_ARRAY_BUFFER, m_nQuadPositionVBO); 

glEnableVertexAttribArray(VAT_POSITION); 

glVertexAttribPointer(VAT_POSITION, 2, GL_INT, GL_FALSE, 0, NULL);

   

glBindBuffer(GL_ARRAY_BUFFER, m_nQuadTexcoordVBO); 

glEnableVertexAttribArray(VAT_TEXCOORD); 

glVertexAttribPointer(VAT_TEXCOORD, 2, GL_INT, GL_FALSE, 0, NULL); 



glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nQuadIndexVBO);  

//保存结束

glBindVertexArray(NULL);



glBindBuffer(GL_ARRAY_BUFFER, NULL);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);

 

  以上就是VAO的使用方法了。VAO可以理解为一个状态容器,记录VBO的状态。

参考:

1.学一学,VBO

2.AB是一家,VAO与VBO

3.OpenGL超级宝典(第4版)

4.OpenGL ES 3.0 编程指南

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值