一、顶点数组,顶点数组对象和缓冲区对象
顶点数组及顶点数组对象可以有效地解决渲染过程中的数据冗余以及大量的添加额外信息的问题,而缓冲区对象则避免渲染过程中反复的读写数据导致的效率损耗。二者结合可以大幅度的提高渲染的效率。
1、顶点数组的使用步骤:
(1) 激活数组。这些数组描述了图元的几何属性,例如,顶点坐标,表面法线,RGBA颜色,辅助颜色,颜色索引,雾坐标,纹理坐标,多边形边界标志等。
glEnableClientState(GLenum array)
(2)把数据放入数组中。 其他属性采用 glColorPointer(...), glTextureCoordPointer(...)等等,不一一列举。
glVertexPointer(GLint sizei, GLenum type, GLsizei stride, const GLvoid * pointer)
(3) 绘制几何图元。
glDrawElements(GLenum Mode, GLsizei count, GLenum type, const GLvoid* indices)
在大场景渲染过程中,通常多个对象之间的顶点属性并不相同,这时候需要频繁调用glVetexPointer这些函数。而顶点数组对象则对这些调用进行集合。简单点来讲,针对每个需要渲染的对象创建一个顶点数组对象,此时该顶点数组对象包含了该对象所有的属性,在渲染的过程中,只需要调用该对象的顶点数组对象,便可以在多个对象之间进行切换,从而避免了glVetexPointer函数的反复调用。
2、顶点数组对象相关的函数:
(1)创建顶点数组对象
glGenVertexArray(GLsizei n, GLuint* arrarys)
(2)初始化新对象
glBindVertexArray(GLuint array)
3、使用缓冲区对象的基本步骤:
(1) 创建缓冲区对象
glGenBuffers(GLsizei n, GLuint *buffers)
(2)激活缓冲区对象
glBindBuffers(GLenum target, GLuint buffer)
(3)用数据分配和初始化缓冲区对象
glBufferData*GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)
二、下面结合一个三维场景渲染的实例具体介绍函数的用法。代码中采用了Qt的图像数据结构QImage.
1.全局变量
GLuint element_num[2];//存储顶点和面片的个数
GLuint arrayIndex;// 顶点数组对象的索引</span>
2.设置场景数据
void GLViewer::makeObjects()
{
// 采用Qt加载纹理图像
QImage tex;
tex = convertToGLFormat(*g_texture_image_);
glGenTextures(1, &g_texture_id_);// 生成纹理对象
glBindTexture(GL_TEXTURE_2D, g_texture_id_);//激活当前纹理
glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//***********************************************************************//
glewInit();// OpenGL的扩扩展库glew中的函数,这句必须调用,否则运行不通
//************************************************************************//
glGenVertexArrays(1, &arrayIndex); //1. 生成顶点数组对象,可以理解成每个顶点数组对应一个需要渲染的对象
enum{ Verices, TexCoords, Colors, Elements, NumAttris }; // 顶点数组的属性
GLuint buffers[NumAttris];
GLfloat *vertices = new GLfloat [element_num[0]*3]; //顶点坐标
GLfloat *texturecoords = new GLfloat [element_num[0]*3];// 纹理坐标
GLfloat *colors = new GLfloat[element_num[0] * 3]; //颜色
GLuint * facets = new GLuint [element_num[1] * 3];// 面片索引信息
//********************************************************************//
//* vertices, texturecoords, colors, facets 需要进行初始化 *//
NumElements = (GLsizei)g_element_num_[1] * 3;
//**********************************************************************//
// 这里需要注意的是
// GLfloat * a = new GLfloat[3];
// GLfloat b[3];
// sizeof(a) 和 sizeof(b) 大小是不相同的
//***********************************************************************//
glBindVertexArray(arrayIndex);// 2.激活顶点数组
glGenBuffers(NumAttris, buffers);//3. 生成缓冲区对象,每个属性对应一个缓冲区对象 // 针对每个属性将数据放到对应的缓冲区中
//VERTEX
glBindBuffer(GL_ARRAY_BUFFER, buffers[Verices]);//4. 激活对应的缓冲区对象
glBufferData(GL_ARRAY_BUFFER, element_num[0] * 3 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);//5 缓冲区数据分配,缓冲区大小不能直接用sizeof(vertices)
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));// 6 把数据放入顶点数组中
glEnableClientState(GL_VERTEX_ARRAY);// 7. 激活顶点数组属性 // 其他属性的设置方式类似
//COLOR
glBindBuffer(GL_ARRAY_BUFFER, buffers[Colors]);
glBufferData(GL_ARRAY_BUFFER, element_num[0] * 3 * sizeof(GLfloat), colors, GL_STATIC_DRAW);
glColorPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));glEnableClientState(GL_COLOR_ARRAY);
//TEXTURE COORDS
for (int i = 0; i < g_element_num_[0]; i++)
{g_texture_coords_[i * 2 + 1] = 1 - g_texture_coords_[i * 2 + 1];}
glBindBuffer(GL_ARRAY_BUFFER, buffers[TexCoords]);
glBufferData(GL_ARRAY_BUFFER, element_num[0] * 2 * sizeof(GLfloat), texturecoords, GL_STATIC_DRAW);
glTexCoordPointer(2, GL_FLOAT, 0, BUFFER_OFFSET(0));glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//Face ELEMENT
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[Elements]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, element_num[1] * 3 * sizeof(GLuint),facets, GL_STATIC_DRAW);
3.渲染过程
渲染过程中可以调用glPolygonMode(Glenum face, GLenum mode) 将场景渲染成,顶点,火柴架,和面片的形式。
void GLViewer::draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_LIGHTING);
glShadeModel(GL_FLAT);
// display mesh vertices
if (g_display_vertices_ == true){
glBindVertexArray(arrayIndex); //激活当前顶点数组对象,本例中只有一个顶点数组对象
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 将场景的前后面渲染成定点的形式
glDrawElements(GL_TRIANGLES, NumElements, GL_UNSIGNED_INT, BUFFER_OFFSET(0));// 渲染
//****************************************************************//
// 注意数据的格式 GL_UNSIGNED_INT 应该和保持一致
//****************************************************************//
}
// display mesh in wire frame mode
if (g_display_wire_frame_ == true){
glBindVertexArray(arrayIndex);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, NumElements, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
}
// display mesh in Flat Mode
if (g_display_flat_ == true){
glBindVertexArray(arrayIndex);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDrawElements(GL_TRIANGLES, NumElements, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
}
// draw mesh with texture
if (g_display_texture_ == true){
//******************************************************************//
//* 启用纹理贴图模式, 非常重要 !! *//
glEnable(GL_TEXTURE_2D);
glBindVertexArray(arrayIndex);
glBindTexture(GL_TEXTURE_2D, g_texture_id_);// 激活当前纹理
//*****************************************************************//
//*纹理渲染的过程中禁用颜色属性,顶点数组可以根据需要禁用相应的属性
glDisableClientState(GL_COLOR_ARRAY);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDrawElements(GL_TRIANGLES, NumElements, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glEnableClientState(GL_COLOR_ARRAY);
glDisable(GL_TEXTURE_2D);
}
glFlush();
}