随着OpenGL状态和固定管线模式的移除,我们不在用任何glEnable函数调用,而且也不会有glVertex、glColor等函数调用。这就意味着我们需要一种新的方式来将数据传输到图形卡以渲染图形。我们可以采用VBO,或者是在OpenGL3以上版本引入的新的特性,叫做VAO。通过它,我们可以把顶点数据和颜色存储在不同的VBO中,但是在同一个VAO中。对于法线数据或者其他的顶点信息也是一样。
VAO,是这样一种方式:把对象信息直接存储在图形卡中,而不是在当我们需要的时候传输到图形卡。这就是Direct3D所采用得方式,而在OpenGL中只有OpenGL3.X以上的版本中采用。这就意味着我们的应用程序不用将数据传输到图形卡或者是从图形卡输出,这样也就获得了额外的性能提升。
使用VAO并不难。我们不需要大量的glVertex调用,而是把顶点数据存储在数组中,然后放进VBO,最后在VAO中存储相关的状态。记住:VAO中并没有存储顶点的相关属性数据。OpenGL会在后台为我们完成其他的功能。</span></p>
一个三角形顶点数据
float position[9]={
-0.8f, -0.8f, 0.0f,
0.8f, -0.8f, 0.0f,
0.0f, 0.8f, 0.0f};
一、
/***单独vao绘制***/
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0,position);/*size:指定每个顶点对应的坐标个数,可取2,3,4
type:指定数组中每个顶点坐标的数据类型:GL_BYTE, GL_SHORT,GL_FIXED,GL_FLOAT;
stride:指定连续顶点间的字节排列方式,即<从一个数据的开始到下一个数据的开始,
所相隔的字节数>,为0表示数组中的顶点被认为按照紧凑方式排列,默认为0;
pointer:指定了数组中第一个顶点的首地址,默认值为0。*/
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_VERTEX_ARRAY);</span>
二、
/***单独vbo绘制***/
GLuint vboNum;
glGenBuffers(1,&vboNum);
glBindBuffer(GL_ARRAY_BUFFER,vboNum);
glBufferData(GL_ARRAY_BUFFER,9*sizeof(float),position,GL_STATIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_VERTEX_ARRAY);
三、
/***vbo***/
GLuint vboNum;
glGenBuffers(1,&vboNum);
glBindBuffer(GL_ARRAY_BUFFER,vboNum);
glBufferData(GL_ARRAY_BUFFER,9*sizeof(float),position,GL_STATIC_DRAW);
/***vao***/
GLuint vaoNum;
glGenVertexArrays(1,&vaoNum);
glBindVertexArray(vaoNum);
glBindBuffer(GL_ARRAY_BUFFER,vboNum);
glVertexPointer(3, GL_FLOAT, 0,(void *)NULL);
glEnableClientState(GL_VERTEX_ARRAY);
glBindVertexArray(vaoNum);
glDrawArrays(GL_TRIANGLES, 0, 3 );
glDisableClientState(GL_VERTEX_ARRAY);
四、
/***使用glVertexAttribPointer()、vbo和vao传递数据***/
/***vbo***/
GLuint vboNum;
glGenBuffers(1,&vboNum);
glBindBuffer(GL_ARRAY_BUFFER,vboNum);
glBufferData(GL_ARRAY_BUFFER,9*sizeof(float),position,GL_STATIC_DRAW);
/***vao***/
GLuint vaoNum;
glGenVertexArrays(1,&vaoNum);
glBindVertexArray(vaoNum);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER,vboNum);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void *)NULL);
glBindVertexArray(vaoNum);
glDrawArrays(GL_TRIANGLES, 0, 3 );
- void InitializeVertexBuffer()
- {
- glGenBuffers(1,%vertexBufferObject);
- glBindBuffer(GL_ARRAY_BUFFER,vertexBufferObject);
- glBufferData(GL_ARRAY_BUFFER,sizeof(vertexData),vertexData,GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER,0);
- glGenBuffers(1,&indexBufferObject);
- glBindBuffer(GL_ARRAY_BUFFER,indexBufferObject);
- glBufferData(GL_ARRAY_BUFFER,sizeof(indexData),indexData,GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER,0);
- }
void InitializeVertexArrayObjects()
{
glGenVertexArrays(1, &vaoObject1);
glBindVertexArray(vaoObject1);
size_t colorDataOffset = sizeof(float) * 3 * numberOfVertices;
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)colorDataOffset);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject);
glBindVertexArray(0);
glGenVertexArrays(1, &vaoObject2);
glBindVertexArray(vaoObject2);
size_t posDataOffset = sizeof(float) * 3 * (numberOfVertices/2);
colorDataOffset += sizeof(float) * 4 * (numberOfVertices/2);
//Use the same buffer object previously bound to GL_ARRAY_BUFFER.
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)posDataOffset);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)colorDataOffset);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject);
glBindVertexArray(0);
}
Draw:
glBindVertexArray(vaoObject1);
glUniform3f(offsetUniform, 0.0f, 0.0f, 0.0f);
glDrawElements(GL_TRIANGLES, ARRAY_COUNT(indexData), GL_UNSIGNED_SHORT, 0);
glBindVertexArray(vaoObject2);
glUniform3f(offsetUniform, 0.0f, 0.0f, -1.0f);
glDrawElements(GL_TRIANGLES, ARRAY_COUNT(indexData), GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0);
例子:
#include <stdlib.h>
#include <stdio.h>
/* Ensure we are using opengl's core profile only */
#define GL3_PROTOTYPES 1
#include <GL3/gl3.h>
#include <SDL.h>
#define PROGRAM_NAME "Tutorial2"
/* A simple function that will read a file into an allocated char pointer buffer */
char* filetobuf(char *file)
{
FILE *fptr;
long length;
char *buf;
fptr = fopen(file, "rb"); /* Open file for reading */
if (!fptr) /* Return NULL on failure */
return NULL;
fseek(fptr, 0, SEEK_END); /* Seek to the end of the file */
length = ftell(fptr); /* Find out how many bytes into the file we are */
buf = (char*)malloc(length+1); /* Allocate a buffer for the entire length of the file and a null terminator */
fseek(fptr, 0, SEEK_SET); /* Go back to the beginning of the file */
fread(buf, length, 1, fptr); /* Read the contents of the file in to the buffer */
fclose(fptr); /* Close the file */
buf[length] = 0; /* Null terminator */
return buf; /* Return the buffer */
}
/* A simple function that prints a message, the error code returned by SDL, and quits the application */
void sdldie(char *msg)
{
printf("%s: %s\n", msg, SDL_GetError());
SDL_Quit();
exit(1);
}
void setupwindow(SDL_WindowID *window, SDL_GLContext *context)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) /* Initialize SDL's Video subsystem */
sdldie("Unable to initialize SDL"); /* Or die on error */
/* Request an opengl 3.2 context.
* SDL doesn't have the ability to choose which profile at this time of writing,
* but it should default to the core profile */
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
/* Turn on double buffering with a 24bit Z buffer.
* You may need to change this to 16 or 32 for your system */
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
/* Create our window centered at 512x512 resolution */
*window = SDL_CreateWindow(PROGRAM_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
512, 512, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
if (!*window) /* Die if creation failed */
sdldie("Unable to create window");
/* Create our opengl context and attach it to our window */
*context = SDL_GL_CreateContext(*window);
/* This makes our buffer swap syncronized with the monitor's vertical refresh */
SDL_GL_SetSwapInterval(1);
}
void drawscene(SDL_WindowID window)
{
int i; /* Simple iterator */
GLuint vao, vbo[2]; /* Create handles for our Vertex Array Object and two Vertex Buffer Objects */
int IsCompiled_VS, IsCompiled_FS;
int IsLinked;
int maxLength;
char *vertexInfoLog;
char *fragmentInfoLog;
char *shaderProgramInfoLog;
/* We're going to create a simple diamond made from lines */
const GLfloat diamond[4][2] = {
{ 0.0, 1.0 }, /* Top point */ |
{ 1.0, 0.0 }, /* Right point */ |
{ 0.0, -1.0 }, /* Bottom point */
{ -1.0, 0.0 } }; /* Left point */
const GLfloat colors[4][3] = {
{ 1.0, 0.0, 0.0 }, /* Red */
{ 0.0, 1.0, 0.0 }, /* Green */
{ 0.0, 0.0, 1.0 }, /* Blue */
{ 1.0, 1.0, 1.0 } }; /* White */
/* These pointers will receive the contents of our shader source code files */
GLchar *vertexsource, *fragmentsource;
/* These are handles used to reference the shaders */
GLuint vertexshader, fragmentshader;
/* This is a handle to the shader program */
GLuint shaderprogram;
/* Allocate and assign a Vertex Array Object to our handle */
glGenVertexArrays(1, &vao);
/* Bind our Vertex Array Object as the current used object */
glBindVertexArray(vao);
/* Allocate and assign two Vertex Buffer Objects to our handle */
glGenBuffers(2, vbo);
/* Bind our first VBO as being the active buffer and storing vertex attributes (coordinates) */
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
/* Copy the vertex data from diamond to our buffer */
/* 8 * sizeof(GLfloat) is the size of the diamond array, since it contains 8 GLfloat values */
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), diamond, GL_STATIC_DRAW);
/* Specify that our coordinate data is going into attribute index 0, and contains two floats per vertex */
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
/* Enable attribute index 0 as being used */
glEnableVertexAttribArray(0);
/* Bind our second VBO as being the active buffer and storing vertex attributes (colors) */
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
/* Copy the color data from colors to our buffer */
/* 12 * sizeof(GLfloat) is the size of the colors array, since it contains 12 GLfloat values */
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), colors, GL_STATIC_DRAW);
/* Specify that our color data is going into attribute index 1, and contains three floats per vertex */
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
/* Enable attribute index 1 as being used */
glEnableVertexAttribArray(1);
/* Read our shaders into the appropriate buffers */
vertexsource = filetobuf("tutorial2.vert");
fragmentsource = filetobuf("tutorial2.frag");
/* Create an empty vertex shader handle */
vertexshader = glCreateShader(GL_VERTEX_SHADER);
/* Send the vertex shader source code to GL */
/* Note that the source code is NULL character terminated. */
/* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
glShaderSource(vertexshader, 1, (const GLchar**)&vertexsource, 0);
/* Compile the vertex shader */
glCompileShader(vertexshader);
glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &IsCompiled_VS);
if(IsCompiled_VS == FALSE)
{
glGetShaderiv(vertexshader, GL_INFO_LOG_LENGTH, &maxLength);
/* The maxLength includes the NULL character */
vertexInfoLog = (char *)malloc(maxLength);
glGetShaderInfoLog(vertexshader, maxLength, &maxLength, vertexInfoLog);
/* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
/* In this simple program, we'll just leave */
free(vertexInfoLog);
return;
}
/* Create an empty fragment shader handle */
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
/* Send the fragment shader source code to GL */
/* Note that the source code is NULL character terminated. */
/* GL will automatically detect that therefore the length info can be 0 in this case (the last parameter) */
glShaderSource(fragmentshader, 1, (const GLchar**)&fragmentsource, 0);
/* Compile the fragment shader */
glCompileShader(fragmentshader);
glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &IsCompiled_FS);
if(IsCompiled_FS == FALSE)
{
glGetShaderiv(fragmentshader, GL_INFO_LOG_LENGTH, &maxLength);
/* The maxLength includes the NULL character */
fragmentInfoLog = (char *)malloc(maxLength);
glGetShaderInfoLog(fragmentshader, maxLength, &maxLength, fragmentInfoLog);
/* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
/* In this simple program, we'll just leave */
free(fragmentInfoLog);
return;
}
/* If we reached this point it means the vertex and fragment shaders compiled and are syntax error free. */
/* We must link them together to make a GL shader program */
/* GL shader programs are monolithic. It is a single piece made of 1 vertex shader and 1 fragment shader. */
/* Assign our program handle a "name" */
shaderprogram = glCreateProgram();
/* Attach our shaders to our program */
glAttachShader(shaderprogram, vertexshader);
glAttachShader(shaderprogram, fragmentshader);
/* Bind attribute index 0 (coordinates) to in_Position and attribute index 1 (color) to in_Color */
/* Attribute locations must be setup before calling glLinkProgram. */
glBindAttribLocation(shaderprogram, 0, "in_Position");
glBindAttribLocation(shaderprogram, 1, "in_Color");
/* Link our program */
/* At this stage, the vertex and fragment programs are inspected, optimized and a binary code is generated for the shader. */
/* The binary code is uploaded to the GPU, if there is no error. */
glLinkProgram(shaderprogram);
/* Again, we must check and make sure that it linked. If it fails, it would mean either there is a mismatch between the vertex */
/* and fragment shaders. It might be that you have surpassed your GPU's abilities. Perhaps too many ALU operations or */
/* too many texel fetch instructions or too many interpolators or dynamic loops. */
glGetProgramiv(shaderprogram, GL_LINK_STATUS, (int *)&IsLinked);
if(IsLinked == FALSE)
{
/* Noticed that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. */
glGetProgramiv(shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);
/* The maxLength includes the NULL character */
shaderProgramInfoLog = (char *)malloc(maxLength);
/* Notice that glGetProgramInfoLog, not glGetShaderInfoLog. */
glGetProgramInfoLog(shaderprogram, maxLength, &maxLength, shaderProgramInfoLog);
/* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
/* In this simple program, we'll just leave */
free(shaderProgramInfoLog);
return;
}
/* Load the shader into the rendering pipeline */
glUseProgram(shaderprogram);
/* Loop our display increasing the number of shown vertexes each time.
* Start with 2 vertexes (a line) and increase to 3 (a triangle) and 4 (a diamond) */
for (i=2; i <= 4; i++)
{
/* Make our background black */
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
/* Invoke glDrawArrays telling that our data is a line loop and we want to draw 2-4 vertexes */
glDrawArrays(GL_LINE_LOOP, 0, i);
/* Swap our buffers to make our changes visible */
SDL_GL_SwapWindow(window);
/* Sleep for 2 seconds */
SDL_Delay(2000);
}
/* Cleanup all the things we bound and allocated */
glUseProgram(0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDetachShader(shaderprogram, vertexshader);
glDetachShader(shaderprogram, fragmentshader);
glDeleteProgram(shaderprogram);
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
glDeleteBuffers(2, vbo);
glDeleteVertexArrays(1, &vao);
free(vertexsource);
free(fragmentsource);
}
void destroywindow(SDL_WindowID window, SDL_GLContext context)
{
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
SDL_Quit();
}
/* Our program's entry point */
int main(int argc, char *argv[])
{
SDL_WindowID mainwindow; /* Our window handle */
SDL_GLContext maincontext; /* Our opengl context handle */
/* Create our window, opengl context, etc... */
setupwindow(&mainwindow, &maincontext);
/* Call our function that performs opengl operations */
drawscene(mainwindow);
/* Delete our opengl context, destroy our window, and shutdown SDL */
destroywindow(mainwindow, maincontext);
return 0;
}
IBO+VBO
// 使用EBO float vertices[] = { // positions // colors // texture coords 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left }; unsigned int indices[] = { 0, 1, 3, // first triangle 1, 2, 3 // second triangle };
{
glGenBuffers(1, &_vboid); glGenBuffers(1, &_iboid); glBindBuffer(GL_ARRAY_BUFFER, _vboid); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboid); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // position attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // color attribute glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // texture coord attribute glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2);
}
glUseProgram(gProgram); checkGlError("glUseProgram"); // Bind the texture object glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureid); glUniform1i(NormalTextureID, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboid); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);