1.在给shader传递顶点数据的时候,如果每次都要向GPU发送一大块数据,而这数据其实并没有修改过,那么这传输就是冗余的。所以这里添加了缓冲区对象,将顶点数组存储在服务器端的缓冲区对象中。
2.在GPU中,VBO(vertex buffer object)负责实际数据的存储;而VAO(vertex array object)则记录数据的存储和如何使用的细节信息。
3.使用VAO的优势就在于,如果有多个物体需要绘制,那么我们设置一次绘制物体需要的顶点数据、数据解析方式等信息,然后通过VAO保存起来后,后续的绘制操作不再需要重复这一过程,只需要将VAO设定为当前VAO,那么OpenGL则会使用这些状态信息。当场景中物体较多时,优势十分明显。VAO和VBO的关系如下图所示:
上图中表示,顶点属性包括位置、纹理坐标、法向量、颜色等多个属性,每个属性的数据可以存放在不同的buffer中。我们可以根据需求,在程序中创建多个VBO和VAO
a.函数中target参数表示绑定的目标,包括像GL_ARRAY_BUFFER用于Vertex attributes(顶点属性),GL_ELEMENT_ARRAY_BUFFER用于索引绘制等目标。b.size参数表示需要分配的空间大小,以字节为单位。c.data参数用于指定数据源,如果data不为空将会拷贝其数据来初始化这个缓冲区,否则只是分配预定大小的空间。预分配空间后,后续可以通过glBufferSubData来更新缓冲区内容。d.usage参数指定数据使用模式,例如GL_STATIC_DRAW指定为静态绘制,数据保持不变, GL_DYNAMIC_DRAW指定为动态绘制,数据会经常更新 。
a. 参数index 表示顶点属性的索引 这个索引即是在顶点着色器中的属性索引,索引从0开始记起。b. 参数size 每个属性数据由几个分量组成。例如上面顶点每个属性为3个float组成的,size即为3。分量的个数必须为1,2,3,4c. 参数type表示属性分量的数据类型,例如上面的顶点数据为float则填写GL_FLOAT.d. 参数normalized 表示是否归一化,当存储整型时,如果设置为GL_TRUE,那么当被以浮点数形式访问时,有符号整型转换到[-1,1],无符号转换到[0,1]。否则直接转换为float型,而不进行归一化。e. 参数stride表示连续的两个顶点属性之间的间隔,以字节大小计算。当顶点属性紧密排列(tightly packed)时,可以填0,由OpenGL代替我们计算出该值。f. 参数pointer表示当前绑定到 GL_ARRAY_BUFFER缓冲对象的缓冲区中,顶点属性的第一个分量距离数据的起点的偏移量,以字节为单位计算。
这里以绘制一个三角形为例,这个三角形包含的顶点属性包括:位置、颜色、以及纹理坐标,着色器中将颜色和纹理进行混合。有了上面对glVertexAttribPointer的理解,我们可以有三种方式传送顶点数据:
为每个顶点属性指定一个独立的VBO
单个顶点属性连续存放,整个顶点属性作为一个一个VBO
顶点属性之间交错存放,整体作为一个VBO
这三种方式都是支持的,通过这三种方式的实现,我们加深对glVertexAttribPointer的理解,具体的实现方法见如下:
http://blog.csdn.net/wangdingqiaoit/article/details/52662270
更多关于glBufferSubData和glMapBuffer传送顶点数据的方法参考下面链接:http://blog.csdn.net/wangdingqiaoit/article/details/52662270
使用顶点数组方式,需要利用glEnableClientState(GL_VERTEX_ARRAY)
开启顶点特性。用户定义好存储顶点的数据,在调用glDrawArrays、glDrawElements之类的函数时,通过glVertexPointer
设定的指针,传送数据到GPU。当调用完glDrawArrays后,GPU中已经有了绘图所需数据,这时可以释放数据空间
# include
# include
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[] vertices;
glFlush();
}
void keyboardAction( unsigned char key, int x, int y )
{
switch( key )
{
case 033: // Escape key
exit( EXIT_SUCCESS );
break;
}
}
//g++ vbo.cpp -lGL -lGLEW -lglut
# include
# include 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 }; glGenBuffersARB(1,&vboId); 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); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, 0); 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 ); break; } }
// g++ triangle.cpp -lGLEW -lglfw3
#define GLEW_STATIC
#include
#include
#include
#include
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;
int main(int argc, char** argv)
{
if (!glfwInit())
{
std::cout << "Error::GLFW could not initialize GLFW!" << std::endl;return -1;
}
std::cout << "Start OpenGL core profile version 3.3" << std::endl;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT,"Demo of triangle", NULL, NULL);
if (!window)
{
std::cout << "Error::GLFW could not create winddow!" << std::endl;glfwTerminate();return -1;
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// 初始化GLEW获取OpenGL函数
glewExperimental = GL_TRUE; //让GLEW获取所有extension函数
GLenum status = glewInit();
if (status != GLEW_OK)
{
std::cout << "Error::GLEW glew version:" << glewGetString(GLEW_VERSION)
<< " error string:" << glewGetErrorString(status) << std::endl;
glfwTerminate();return -1;
}
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
// Section1 准备顶点数据
GLfloat vertices[] = {
-0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f };
GLuint VAOId, VBOId;
// Step1: 创建并绑定VAO
glGenVertexArrays(1, &VAOId);
glBindVertexArray(VAOId);
// Step2:创建并绑定VBO
glGenBuffers(1, &VBOId);
glBindBuffer(GL_ARRAY_BUFFER, VBOId);
// Step3: 分配空间传送数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Step4: 指定GPU解析数据的方式
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Section2 准备shader
// Step1: shader source
const GLchar* vertexShaderSource = "#version 330\n"
"layout(location = 0) in vec3 position;\n"
"void main()\n"
"{\n gl_Position = vec4(position, 1.0);\n}";
const GLchar* fragShaderSource = "#version 330\n"
"out vec4 color;\n"
"void main()\n"
"{\n color = vec4(0.8, 0.8, 0.0, 1.0);\n}";
// Step2 create Shader object
// vertex shader
GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderId, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShaderId);
GLint compileStatus = 0;
glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(vertexShaderId, GL_INFO_LOG_LENGTH, &maxLength);
std::vector errLog(maxLength);
glGetShaderInfoLog(vertexShaderId, maxLength, &maxLength, &errLog[0]);
std::cout << "Error::shader vertex shader compile failed," << &errLog[0] << std::endl;
}
// fragment shader
GLuint fragShaderId = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragShaderId, 1, &fragShaderSource, NULL);
glCompileShader(fragShaderId);
glGetShaderiv(fragShaderId, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(fragShaderId, GL_INFO_LOG_LENGTH, &maxLength);
std::vector errLog(maxLength);
glGetShaderInfoLog(fragShaderId, maxLength, &maxLength, &errLog[0]);
std::cout << "Error::shader fragment shader compile failed," << &errLog[0] << std::endl;
}
// Step3 link shader
GLuint shaderProgramId = glCreateProgram();
glAttachShader(shaderProgramId, vertexShaderId);
glAttachShader(shaderProgramId, fragShaderId);
glLinkProgram(shaderProgramId);
GLint linkStatus;
glGetProgramiv(shaderProgramId, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(shaderProgramId, GL_INFO_LOG_LENGTH, &maxLength);
std::vector errLog(maxLength);
glGetProgramInfoLog(shaderProgramId, maxLength, &maxLength, &errLog[0]);
std::cout << "Error::shader link failed," << &errLog[0] << std::endl;
}
// 链接完成之后可以detach shader
glDetachShader(shaderProgramId, vertexShaderId);
glDetachShader(shaderProgramId, fragShaderId);
// 不需要连接到其他shader program时可以将这个shader释放
glDeleteShader(vertexShaderId);
glDeleteShader(fragShaderId);
//绘图主循环
while (!glfwWindowShouldClose(window))
{
glfwPollEvents(); // 捕捉鼠标键盘事件
glClearColor(0.18f, 0.04f, 0.14f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 绘制
glBindVertexArray(VAOId);
glUseProgram(shaderProgramId);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glUseProgram(0);
glfwSwapBuffers(window); // swap
}
// release data
glDeleteProgram(shaderProgramId);
glDeleteVertexArrays(1, &VAOId);
glDeleteBuffers(1, &VBOId);
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
}