接触OpenGL有一段时间了,大多数时候都是使用OSG在进行开发,前几天同事问起OpenGL中VBO的相关内容,才发现很多OpenGL的知识生疏了。这两天花了点时间重新复习了一下与VBO相关的内容,记录在此以备忘。文中大部分内容来自网络。
OpenGL从OpenGL 3.0开始将API分成了两种类型:即旧式的OpenGL(Legacy OpenGL)和新式的OpenGL(Core Profile),OpenGL3.3 的官方API文档完整地描述了新式的OpenGL API,旧式的OpenGL API可以在OpenGL 2.1中查看。
对于OpenGL渲染的的第一站:也就是把顶点数据(包括顶点位置、顶点法线、顶点颜色、纹理坐标等)传入到OpenGL,旧式的OpenGL中有以下几种方式实现:
- 立即模式(glBegin/glEnd) 这种方式恐怕是学习OpenGL最早接触到的API吧,至少我是这样。不过很遗憾它已经被新式OpenGL抛弃了
- 顶点数组(VA:Vertex Array)相比立即模式减少了函数的调用开销。目前也被新式OpenGL抛弃
- 缓冲区对象(VBO:Vertex Buffer Object)相比较VA,将数据存储到服务器端(VA存储在客户端)。目前是新式OpenGL支持的唯一数据传入方式
- 显示列表(Display List)将若干条OpenGL命令编译好,直接由显卡调用。由于编译好的模块无法修改,丧失了灵活性。也被新式OpenGL抛弃
此外VBO在Legacy OpenGL中和Core Profile OpenGL中的使用也有着不同的方式。
(1) 开启和关闭VA
-
- void glEnableClientState(GLenum cap);
- void glDisableClientState(GLenum cap);
-
-
- GL_VERTEX_ARRAY
- GL_COLOR_ARRAY
- GL_EDGE_FLAG_ARRAY
- GL_FOG_COORD_ARRAY
- GL_INDEX_ARRAY
- GL_NORMAL_ARRAY
- GL_SECONDARY_COLOR_ARRAY
- GL_TEXTURE_COORD_ARRAY
(2) 设置数据到顶点
-
-
-
-
-
-
-
-
-
-
- void glVertexPointer( GLint size=4, GLenum type=GL_FLOAT, GLsizei stride=0, const GLvoid *pointer=0);
-
- void glColorPointer( GLint size=4, GLenum type=GL_FLOAT, GLsizei stride=0, const GLvoid *pointer=0);
-
- void glEdgeFlagPointer( GLsizei stride=0, const GLvoid *pointer=0);
-
- void glFogCoordPointer( GLenum type=GL_FLOAT, GLsizei stride=0, GLvoid *pointer=0);
-
- void glIndexPointer( GLenum type=GL_FLOAT, GLsizei stride=0, const GLvoid *pointer=0);
-
- void glNormalPointer( GLenum type=GL_FLOAT, GLsizei stride=0, const GLvoid *pointer=0);
-
- void glSecondaryColorPointer( GLint size=3, GLenum type=GL_FLOAT, GLsizei stride=0, const GLvoid *pointer=0);
-
- void glTexCoordPointer( GLint size=4, GLenum type=GL_FLOAT, GLsizei stride=0, const GLvoid *pointer=0);
(3) VA绘制
-
-
-
- void glDrawArrays( GLenum mode, GLint first, GLsizei count);
-
-
-
-
-
- void glDrawElements( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
-
-
-
-
-
-
- void glDrawRangeElements( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices);
-
-
-
-
-
-
-
以上就是VA所涉及到的基本API,使用过程如下
首先开启客户端VertexArray状态,接着绑定数据到顶点状态,最后进行绘制:
- static float vertices[][3] = {
- 1.0f, 0.0f, 0.0f,
- 0.0f, 1.0f, 0.0f,
- -1.0f, 0.0f, 0.0f
- };
-
- static float colors[][4] = {
- 1.0f, 0.0f, 0.0f, 1.0f,
- 0.0f, 1.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 1.0f
- };
-
-
- void renderScene()
- {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glTranslatef(0.0f, 0.0f, -5.0f);
-
-
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_COLOR_ARRAY);
-
- glVertexPointer(3, GL_FLOAT, 0, vertices);
- glColorPointer(4, GL_FLOAT, 0, colors);
-
- glDrawArrays(GL_TRIANGLES, 0, 3);
-
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_COLOR_ARRAY);
-
- }
前文描述了VBO在Legacy 和Core Profile中有两种不同的方式:
(1)创建(初始化)VBO对象
-
-
- void glGenBuffers( GLsizei n, GLuint * buffers);
-
-
-
-
-
- void glBindBuffer( GLenum target, GLuint buffer);
-
-
-
- GL_ARRAY_BUFFER
- GL_ELEMENT_ARRAY_BUFFER
- GL_PIXEL_PACK_BUFFER
- GL_PIXEL_UNPACK_BUFFER
-
-
- void glBufferData( GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);
-
-
-
- GL_ARRAY_BUFFER
- GL_ELEMENT_ARRAY_BUFFER
- GL_PIXEL_PACK_BUFFER
- GL_PIXEL_UNPACK_BUFFER
-
-
-
- GL_STREAM_DRAW
- GL_STREAM_READ
- GL_STREAM_COPY
- GL_STATIC_DRAW
- GL_STATIC_READ
- GL_STATIC_COPY
- GL_DYNAMIC_DRAW
- GL_DYNAMIC_READ
- GL_DYNAMIC_COPY
(2)开启/关闭VBO
当创建完VBO之后,我们并不知道VBO中存储的是顶点位置、还是顶点颜色或者是顶点法线,于是使用下面的API来描述某一个VBO中到底是顶点什么方面的数据
-
-
- void glEnableClientState(GLenum cap);
- void glDisableClientState(GLenum cap);
-
-
-
- void glVertexPointer( GLint size, GLenum type, GLsizei stride, const GLvoid * pointer);
-
-
-
-
-
-
-
-
-
-
-
-
-
- glColorPointer
- glEdgeFlagPointer
- glFogCoordPointer
- glIndexPointer
- glNormalPointer
- glSecondaryColorPointer
- glTexCoordPointer
(3)VBO绘制
-
-
-
- void glDrawArrays( GLenum mode, GLint first, GLsizei count);
-
-
-
- void glDrawElements( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
-
-
-
-
-
以上便是旧式OpenGL中实现VBO的API,使用过程如下:
首先创建缓冲区并设置缓冲区类型和填满缓冲区,接着指定缓冲里面是什么(到底存的是位置还是颜色或者是法线),最后绘制
- static float vertices[][3] = {
- 1.0f, 0.0f, 0.0f,
- 0.0f, 1.0f, 0.0f,
- -1.0f, 0.0f, 0.0f
- };
-
- static float colors[][4] = {
- 1.0f, 0.0f, 0.0f, 1.0f,
- 0.0f, 1.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 1.0f
- };
-
- void renderScene()
- {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glTranslatef(0.0f, 0.0f, -5.0f);
-
-
-
- GLuint vertexBufferID, colorBufferID;
- glGenBuffers(1, &vertexBufferID);
- glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glGenBuffers(1, &colorBufferID);
- glBindBuffer(GL_ARRAY_BUFFER, colorBufferID);
- glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
-
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_COLOR_ARRAY);
- glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
- glVertexPointer(3, GL_FLOAT, 0, 0);
- glBindBuffer(GL_ARRAY_BUFFER, colorBufferID);
- glColorPointer(4, GL_FLOAT, 0, 0);
-
-
- glDrawArrays(GL_TRIANGLES, 0, 3);
-
-
-
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_COLOR_ARRAY);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-
- }
(1)创建VBO
参考Legacy VBO中内容,二者是一样的
(2)开启/关闭VBO
-
- void glEnableVertexAttribArray( GLuint index);
- void glDisableVertexAttribArray( GLuint index);
-
-
-
-
- void glVertexAttribPointer( GLuint index,
- GLint size,
- GLenum type,
- GLboolean normalized,
- GLsizei stride,
- const GLvoid * pointer
- );
-
-
-
-
-
-
-
-
(3)绘制
参考Legacy VBO中内容,二者是一样的
以上便是Core Profile 下的VBO,使用方式如下:
首先创建VBO对象,接着开启VBO并且用来说明VBO中数据是什么样组织的(但是并没有说明数据是顶点位置、顶点颜色还是法线),这一点与Legacy VBO不同,因为它的说明部分(哪个VBO存储着位置、哪个VBO存储着颜色、哪个VBO存储着法线等)是在着色语言Shader中指明的,最后还是使用同样的API绘制:
-
- GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
- 0.5f, 0.0f, 0.0f,
- 0.0f, 0.5f, 0.0f };
-
- GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,
- 0.0f, 1.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 1.0f };
-
-
- void RenderWidget::paintGL()
- {
-
-
- glGenBuffers(1, &_vertexBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vVerts), vVerts, GL_STATIC_DRAW);
-
- glGenBuffers(1, &_colorBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vColors), vColors, GL_STATIC_DRAW);
-
- setShaders();
-
-
- glEnableVertexAttribArray(0);
- glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
- glVertexAttribPointer(
- 0,
- 3,
- GL_FLOAT,
- GL_FALSE,
- 0,
- (void*)0
- );
- glEnableVertexAttribArray(1);
- glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);
- glVertexAttribPointer(
- 1,
- 4,
- GL_FLOAT,
- GL_FALSE,
- 0,
- (void*)0
- );
-
-
- glDrawArrays(GL_TRIANGLES, 0, 3);
-
-
- glDisableVertexAttribArray(1);
- glDisableVertexAttribArray(0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
Shader部分
:
顶点Shader
- #version 330
- layout(location = 0) in vec4 vertex;
- layout(location = 1) in vec4 color;
- out vec4 inFragColor;
-
- void main( void )
- {
- gl_Position = vertex;
- inFragColor = color;
- }
片元Shader
- #version 330
- in vec4 inFragColor;
- out vec4 outFragColor;
-
- void main( void )
- {
- outFragColor = inFragColor;
- }
通过顶点Shader可以知道索引值为0的VBO解释为顶点位置,它传给gl_Position,索引值为1的VBO解释为顶点的颜色
VAO的介绍可以参考 <<AB是一家?VAO与VBO>>
(1)初始化
-
-
-
- void glGenVertexArrays(GLsizei n, GLuint *arrays);
-
-
-
-
-
- void glBindVertexArray(GLuint array);
-
-
-
(2)开启/关闭VAO
VAO的开启当使用glBindVertexArray时自动开启,使用glBindVertexArray(0),传入一个0值可以视为将VAO关闭
设置VAO的过程就是调用VBO中(2)的过程
(3) 绘制
同VBO中绘制(绘制之前先启用VAO)
以上就是VAO涉及到的API,使用过程如下:
首先创建VAO,接着设置VAO(在调用VBO的数据设置过程中VAO自动完成了设置),最后开启VAO并绘制:
-
- GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
- 0.5f, 0.0f, 0.0f,
- 0.0f, 0.5f, 0.0f };
-
- GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,
- 0.0f, 1.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 1.0f };
-
-
- void RenderWidget::paintGL()
- {
-
-
- glGenBuffers(1, &_vertexBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vVerts), vVerts, GL_STATIC_DRAW);
-
- glGenBuffers(1, &_colorBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vColors), vColors, GL_STATIC_DRAW);
-
- setShaders();
-
-
-
- GLuint vaoBuffer;
- glGenVertexArrays(1, &vaoBuffer);
- glBindVertexArray(vaoBuffer);
-
-
- glEnableVertexAttribArray(0);
- glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
- glVertexAttribPointer(
- 0,
- 3,
- GL_FLOAT,
- GL_FALSE,
- 0,
- (void*)0
- );
- glEnableVertexAttribArray(1);
- glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);
- glVertexAttribPointer(
- 1,
- 4,
- GL_FLOAT,
- GL_FALSE,
- 0,
- (void*)0
- );
- glBindVertexArray(0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-
-
-
-
- glBindVertexArray(vaoBuffer);
- glDrawArrays(GL_TRIANGLES, 0, 3);
- glBindVertexArray(0);
- }