使用 VertexBuffers 的有效方法是什么?我正在绘制一个简单的立方体(链接到代码)。理想情况下,我应该将顶点加载到 gpu 上一次,然后我不必每次都将它们复制到 gpu(这是我在 renderScene 中所做的,第 115 行)。我怎么做?这里也提供了代码(它来自一个教程):
//
// This code was created by Jeff Molofee '99 (ported to Linux/GLUT by Richard Campbell '99)
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe
// (email Richard Campbell at ulmont@bellsouth.net)
//
#include"stdafx.h"
//#include<glload\gl_3_1.h>
#include<Windows.h>
#include<GL/glew.h>
#include <GL/glut.h> // Header File For The GLUT Library
#include <GL/gl.h> // Header File For The OpenGL32 Library
#include <GL/glu.h> // Header File For The GLu32 Library
#include<GL/freeglut_ext.h>
//#include <unistd.h> // Header File For sleeping.
/* ASCII code for the escape key. */
#include <stdlib.h>
#include <GL/glut.h>
// Location/Normals
#define X_POS 0
#define Y_POS 1
#define Z_POS 2
// Texture Coordinates
#define U_POS 0
#define V_POS 1
// Colours
#define R_POS 0
#define G_POS 1
#define B_POS 2
#define A_POS 3
// Vertex structure
typedef struct
{
GLfloat location[3];
GLfloat tex[2];
GLfloat normal[3];
GLfloat colour[4];
GLubyte padding[16];
} Vertex;
// A helper macro to get a position
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
#define INC_SCALE (1.00001)
// GLUT functions
void renderScene();
void changeSize(int w, int h);
void processNormalKeys(unsigned char key, int x, int y);
// VBO functions
void initVBO();
// Other functions
void buildCube();
// Globals - typically these would be in classes,
// but in this simple example, I'm using globals
Vertex verts[24]; // We're making a cube, 6 faces * 4 verticies per face
GLubyte index[36]; // 2 Triangles per face (possible to use quads, but they're being phased out of OpenGL3, so we're using triangles instead)
GLuint vboID; // Vertex Buffer Object ID
GLuint indexVBOID; // Index Buffer Object ID
float angle = 0.0; // Just for display rotation
int main (int argc, char * argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(320, 320);
glutCreateWindow("VBO Example");
//glutFullScreen();
glutDisplayFunc(renderScene);
glutIdleFunc(renderScene);
glutReshapeFunc(changeSize);
glutKeyboardFunc(processNormalKeys);
glEnable(GL_DEPTH_TEST);
buildCube();
glewInit();
initVBO();
glMatrixMode(GL_PROJECTION);
gluLookAt(0.0, 0.0, 5.0,
0.0, 0.0, -1.0,
0.0, 1.0, 0.0);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glutMainLoop();
return 0;
}
void renderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
initVBO();
glPushMatrix();
glRotatef(angle, 1.0, 1.0, 1.0);
// Bind our buffers much like we would for texturing
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBOID);
void* b= glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
for(int i=0;i<24;i++)
{
Vertex* v= &verts[i];
v->location[X_POS]*=INC_SCALE;
v->location[Y_POS]*=INC_SCALE;
v->location[Z_POS]*=INC_SCALE;
}
memcpy(b, verts, 24*sizeof(Vertex));
glUnmapBuffer(GL_ARRAY_BUFFER);
// Set the state of what we are drawing (I don't think order matters here, but I like to do it in the same
// order I set the pointers
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Resetup our pointers. This doesn't reinitialise any data, only how we walk through it
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(12));
glNormalPointer(GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(20));
glColorPointer(4, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(32));
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(0));
// Actually do our drawing, parameters are Primative (Triangles, Quads, Triangle Fans etc), Elements to
// draw, Type of each element, Start Offset
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
// Disable our client state back to normal drawing
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
glutSwapBuffers();
angle += 0.01;
}
void changeSize(int w, int h)
{
if (h == 0) // Prevent divide by zero
h = 1;
float ratio = 1.0 * w / h;
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glViewport(0, 0, w, h);
gluPerspective(45, ratio, 1, 1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 0.0, 5.0,
0.0, 0.0, -1.0,
0.0, 1.0, 0.0);
}
void processNormalKeys(unsigned char key, int x, int y)
{
if (key == 27)
exit(0);
}
void initVBO()
{
glGenBuffers(1, &vboID); // Create the buffer ID, this is basically the same as generating texture ID's
glBindBuffer(GL_ARRAY_BUFFER, vboID); // Bind the buffer (vertex array data)
// Allocate space. We could pass the mesh in here (where the NULL is), but it's actually faster to do it as a
// seperate step. We also define it as GL_STATIC_DRAW which means we set the data once, and never
// update it. This is not a strict rule code wise, but gives hints to the driver as to where to store the data
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 24, NULL, GL_STREAM_DRAW);
//glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertex) * 24, verts);
// Set the pointers to our data. Except for the normal value (which always has a size of 3), we must pass
// the size of the individual component. ie. A vertex has 3 points (x, y, z), texture coordinates have 2 (u, v) etc.
// Basically the arguments are (ignore the first one for the normal pointer), Size (many components to
// read), Type (what data type is it), Stride (how far to move forward - in bytes - per vertex) and Offset
// (where in the buffer to start reading the data - in bytes)
// Make sure you put glVertexPointer at the end as there is a lot of work that goes on behind the scenes
// with it, and if it's set at the start, it has to do all that work for each gl*Pointer call, rather than once at
// the end.
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(12));
glNormalPointer(GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(20));
glColorPointer(4, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(32));
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(0));
// When we get here, all the vertex data is effectively on the card
glGenBuffers(1, &indexVBOID); // Generate buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBOID); // Bind the element array buffer
// Upload the index array, this can be done the same way as above (with NULL as the data, then a
// glBufferSubData call, but doing it all at once for convenience)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 36 * sizeof(GLubyte), index, GL_STATIC_DRAW);
}
void buildCube()
{
// Ugh, ugly code this
// Top
verts[0].location[X_POS] = 1; verts[0].location[Y_POS] = 1; verts[0].location[Z_POS] = -1;
verts[0].normal[X_POS] = 0; verts[0].normal[Y_POS] = 1; verts[0].normal[Z_POS] = 0;
verts[0].tex[U_POS] = 0; verts[0].tex[V_POS] = 0;
verts[1].location[X_POS] = -1; verts[1].location[Y_POS] = 1; verts[1].location[Z_POS] = -1;
verts[1].normal[X_POS] = 0; verts[1].normal[Y_POS] = 1; verts[1].normal[Z_POS] = 0;
verts[1].tex[U_POS] = 0; verts[1].tex[V_POS] = 1;
verts[2].location[X_POS] = -1; verts[2].location[Y_POS] = 1; verts[2].location[Z_POS] = 1;
verts[2].normal[X_POS] = 0; verts[2].normal[Y_POS] = 1; verts[2].normal[Z_POS] = 0;
verts[2].tex[U_POS] = 1; verts[2].tex[V_POS] = 1;
verts[3].location[X_POS] = 1; verts[3].location[Y_POS] = 1; verts[3].location[Z_POS] = 1;
verts[3].normal[X_POS] = 0; verts[3].normal[Y_POS] = 1; verts[3].normal[Z_POS] = 0;
verts[3].tex[U_POS] = 1; verts[3].tex[V_POS] = 0;
// Bottom
verts[4].location[X_POS] = 1; verts[4].location[Y_POS] = -1; verts[4].location[Z_POS] = 1;
verts[4].normal[X_POS] = 0; verts[4].normal[Y_POS] = -1; verts[4].normal[Z_POS] = 0;
verts[4].tex[U_POS] = 0; verts[4].tex[V_POS] = 0;
verts[5].location[X_POS] = -1; verts[5].location[Y_POS] = -1; verts[5].location[Z_POS] = 1;
verts[5].normal[X_POS] = 0; verts[5].normal[Y_POS] = -1; verts[5].normal[Z_POS] = 0;
verts[5].tex[U_POS] = 0; verts[5].tex[V_POS] = 1;
verts[6].location[X_POS] = -1; verts[6].location[Y_POS] = -1; verts[6].location[Z_POS] = -1;
verts[6].normal[X_POS] = 0; verts[6].normal[Y_POS] = -1; verts[6].normal[Z_POS] = 0;
verts[6].tex[U_POS] = 1; verts[6].tex[V_POS] = 1;
verts[7].location[X_POS] = 1; verts[7].location[Y_POS] = -1; verts[7].location[Z_POS] = -1;
verts[7].normal[X_POS] = 0; verts[7].normal[Y_POS] = -1; verts[7].normal[Z_POS] = 0;
verts[7].tex[U_POS] = 1; verts[7].tex[V_POS] = 0;
// Front
verts[8].location[X_POS] = 1; verts[8].location[Y_POS] = 1; verts[8].location[Z_POS] = 1;
verts[8].normal[X_POS] = 0; verts[8].normal[Y_POS] = 0; verts[8].normal[Z_POS] = 1;
verts[8].tex[U_POS] = 0; verts[8].tex[V_POS] = 0;
verts[9].location[X_POS] = -1; verts[9].location[Y_POS] = 1; verts[9].location[Z_POS] = 1;
verts[9].normal[X_POS] = 0; verts[9].normal[Y_POS] = 0; verts[9].normal[Z_POS] = 1;
verts[9].tex[U_POS] = 0; verts[9].tex[V_POS] = 1;
verts[10].location[X_POS] = -1; verts[10].location[Y_POS] = -1; verts[10].location[Z_POS] = 1;
verts[10].normal[X_POS] = 0; verts[10].normal[Y_POS] = 0; verts[10].normal[Z_POS] = 1;
verts[10].tex[U_POS] = 1; verts[10].tex[V_POS] = 1;
verts[11].location[X_POS] = 1; verts[11].location[Y_POS] = -1; verts[11].location[Z_POS] = 1;
verts[11].normal[X_POS] = 0; verts[11].normal[Y_POS] = 0; verts[11].normal[Z_POS] = 1;
verts[11].tex[U_POS] = 1; verts[11].tex[V_POS] = 0;
// Back
verts[12].location[X_POS] = 1; verts[12].location[Y_POS] = -1; verts[12].location[Z_POS] = -1;
verts[12].normal[X_POS] = 0; verts[12].normal[Y_POS] = 0; verts[12].normal[Z_POS] = -1;
verts[12].tex[U_POS] = 0; verts[12].tex[V_POS] = 0;
verts[13].location[X_POS] = -1; verts[13].location[Y_POS] = -1; verts[13].location[Z_POS] = -1;
verts[13].normal[X_POS] = 0; verts[13].normal[Y_POS] = 0; verts[13].normal[Z_POS] = -1;
verts[13].tex[U_POS] = 0; verts[13].tex[V_POS] = 1;
verts[14].location[X_POS] = -1; verts[14].location[Y_POS] = 1; verts[14].location[Z_POS] = -1;
verts[14].normal[X_POS] = 0; verts[14].normal[Y_POS] = 0; verts[14].normal[Z_POS] = -1;
verts[14].tex[U_POS] = 1; verts[14].tex[V_POS] = 1;
verts[15].location[X_POS] = 1; verts[15].location[Y_POS] = 1; verts[15].location[Z_POS] = -1;
verts[15].normal[X_POS] = 0; verts[15].normal[Y_POS] = 0; verts[15].normal[Z_POS] = -1;
verts[15].tex[U_POS] = 1; verts[15].tex[V_POS] = 0;
// Left
verts[16].location[X_POS] = -1; verts[16].location[Y_POS] = 1; verts[16].location[Z_POS] = 1;
verts[16].normal[X_POS] = -1; verts[16].normal[Y_POS] = 0; verts[16].normal[Z_POS] = 0;
verts[16].tex[U_POS] = 0; verts[16].tex[V_POS] = 0;
verts[17].location[X_POS] = -1; verts[17].location[Y_POS] = 1; verts[17].location[Z_POS] = -1;
verts[17].normal[X_POS] = -1; verts[17].normal[Y_POS] = 0; verts[17].normal[Z_POS] = 0;
verts[17].tex[U_POS] = 0; verts[17].tex[V_POS] = 1;
verts[18].location[X_POS] = -1; verts[18].location[Y_POS] = -1; verts[18].location[Z_POS] = -1;
verts[18].normal[X_POS] = -1; verts[18].normal[Y_POS] = 0; verts[18].normal[Z_POS] = 0;
verts[18].tex[U_POS] = 1; verts[18].tex[V_POS] = 1;
verts[19].location[X_POS] = -1; verts[19].location[Y_POS] = -1; verts[19].location[Z_POS] = 1;
verts[19].normal[X_POS] = -1; verts[19].normal[Y_POS] = 0; verts[19].normal[Z_POS] = 0;
verts[19].tex[U_POS] = 1; verts[19].tex[V_POS] = 0;
// Right
verts[20].location[X_POS] = 1; verts[20].location[Y_POS] = 1; verts[20].location[Z_POS] = -1;
verts[20].normal[X_POS] = 1; verts[20].normal[Y_POS] = 0; verts[20].normal[Z_POS] = 0;
verts[20].tex[U_POS] = 0; verts[20].tex[V_POS] = 0;
verts[21].location[X_POS] = 1; verts[21].location[Y_POS] = 1; verts[21].location[Z_POS] = 1;
verts[21].normal[X_POS] = 1; verts[21].normal[Y_POS] = 0; verts[21].normal[Z_POS] = 0;
verts[21].tex[U_POS] = 0; verts[21].tex[V_POS] = 1;
verts[22].location[X_POS] = 1; verts[22].location[Y_POS] = -1; verts[22].location[Z_POS] = 1;
verts[22].normal[X_POS] = 1; verts[22].normal[Y_POS] = 0; verts[22].normal[Z_POS] = 0;
verts[22].tex[U_POS] = 1; verts[22].tex[V_POS] = 1;
verts[23].location[X_POS] = 1; verts[23].location[Y_POS] = -1; verts[23].location[Z_POS] = -1;
verts[23].normal[X_POS] = 1; verts[23].normal[Y_POS] = 0; verts[23].normal[Z_POS] = 0;
verts[23].tex[U_POS] = 1; verts[23].tex[V_POS] = 0;
// Colours
for (int i = 0; i < 24; i++)
{
verts[i].colour[R_POS] = 1.0;
verts[i].colour[G_POS] = 1.0;
// verts[i].colour[B_POS] = 1.0;
verts[i].colour[A_POS] = 1.0;
}
// Index Array (define our triangles)
// A Face looks like (numbers are the array index number of the vertex)
// 1 2
// +------+
// | |
// | |
// +------+
// 0 3
index[0] = 0; index[1] = 1; index[2] = 2;
index[3] = 2; index[4] = 3; index[5] = 0;
index[6] = 4; index[7] = 5; index[8] = 6;
index[9] = 6; index[10] = 7; index[11] = 4;
index[12] = 8; index[13] = 9; index[14] = 10;
index[15] = 10; index[16] = 11; index[17] = 8;
index[18] = 12; index[19] = 13; index[20] = 14;
index[21] = 14; index[22] = 15; index[23] = 12;
index[24] = 16; index[25] = 17; index[26] = 18;
index[27] = 18; index[28] = 19; index[29] = 16;
index[30] = 20; index[31] = 21; index[32] = 22;
index[33] = 22; index[34] = 23; index[35] = 20;
}