Often different vertex attributes (such as position, color, etc.) are not stored separately, but in a common data structure that is created for each vertex, for example:
If vertexData is created as one large VBO, memory access to the individual components is defined by specifying the stride, which is the number of bytes between consecutive elements of the same type, and the start address offset:
// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
using namespace std;
class Renderer {
private:
struct Vertex
{
float position[3];
float color[4];
float texCoord[2];
float normal[3];
};
public:
float t;
int mode;
private:
GLuint vertBufID;
GLuint texID;
int vertNo;
public:
// constructor
Renderer() : t(0.0), mode(0), vertBufID(0), texID(0), vertNo(0) {}
//destructor
~Renderer() {
if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
if (texID != 0) glDeleteTextures(1, &texID);
}
public:
void init() {
glEnable(GL_DEPTH_TEST);
// generating VBO input data
float vertexData[] = {
0.0f, 0.0f, 2.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f, 0.0000f,-0.9701f, 0.2425f,
-0.5f,-0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0000f,-0.9701f, 0.2425f,
0.5f,-0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0000f,-0.9701f, 0.2425f,
0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.5f, 1.0f, 0.9701f, 0.0000f, 0.2425f,
0.5f,-0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.9701f, 0.0000f, 0.2425f,
0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.9701f, 0.0000f, 0.2425f,
0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f, 1.0f, 0.0000f, 0.9701f, 0.2425f,
0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0000f, 0.9701f, 0.2425f,
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0000f, 0.9701f, 0.2425f,
0.0f, 0.0f, 2.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.5f, 1.0f,-0.9701f, 0.0000f, 0.2425f,
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,-0.9701f, 0.0000f, 0.2425f,
-0.5f,-0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,-0.9701f, 0.0000f, 0.2425f
};
vertNo = 12;
// generating vertex VBO
glGenBuffers(1, &vertBufID);
glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
glBufferData(GL_ARRAY_BUFFER, vertNo * sizeof(Vertex), vertexData, GL_STATIC_DRAW);
std::string fileName("checkerboard.ppm");
texID = loadTexture(fileName);
}
void resize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0, (float)w / (float)h, 2.0, 10.0);
}
void display() {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// set camera
gluLookAt(3.0, -1.0, 4.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0);
// draw scene
glRotatef(t, 0.0f, 0.0f, 1.0f);
// activating VBO
glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
int stride = sizeof(Vertex);
char* offset = (char*)NULL;
// position
glVertexPointer(3, GL_FLOAT, stride, offset);
glEnableClientState(GL_VERTEX_ARRAY);
// color
offset = (char*)NULL + 3 * sizeof(float);
glColorPointer(4, GL_FLOAT, stride, offset);
glEnableClientState(GL_COLOR_ARRAY);
// texture
offset = (char*)NULL + (3 + 4) * sizeof(float);
glTexCoordPointer(2, GL_FLOAT, stride, offset);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// normals
offset = (char*)NULL + (3 + 4 + 2) * sizeof(float);
glNormalPointer(GL_FLOAT, stride, offset);
glEnableClientState(GL_NORMAL_ARRAY);
// bind texture
if (mode == 0) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texID);
}
else {
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
// render data
glDrawArrays(GL_TRIANGLES, 0, vertNo);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisable(GL_TEXTURE_2D);
}
private:
// returns a valid textureID on success, otherwise 0
GLuint loadTexture(std::string& filename) {
unsigned width;
unsigned height;
int level = 0;
int border = 0;
std::vector<unsigned char> imgData;
// load image data
if (!loadPPMImageFlipped(filename, width, height, imgData)) return 0;
// data is aligned in byte order
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//request textureID
GLuint textureID;
glGenTextures(1, &textureID);
// bind texture
glBindTexture(GL_TEXTURE_2D, textureID);
//define how to filter the texture (important but ignore for now)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//texture colors should modulate the original color values
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// specify the 2D texture map
glTexImage2D(GL_TEXTURE_2D, level, GL_RGB, width, height, border, GL_RGB, GL_UNSIGNED_BYTE, &imgData[0]);
// return unique texture identifier
return textureID;
}
bool loadPPMImageFlipped(std::string& filename, unsigned& width, unsigned& height, std::vector<unsigned char>& imgData) {
ifstream input(filename.c_str(), ifstream::in | ifstream::binary);
if (!input) { // cast istream to bool to see if something went wrong
cerr << "Can not find texture data file " << filename.c_str() << endl;
return false;
}
input.unsetf(std::ios_base::skipws);
string line;
input >> line >> std::ws;
if (line != "P6") {
cerr << "File is not PPM P6 raw format" << endl;
return false;
}
width = 0;
height = 0;
unsigned depth = 0;
unsigned readItems = 0;
unsigned char lastCharBeforeBinary;
while (readItems < 3) {
input >> std::ws;
if (input.peek() != '#') {
if (readItems == 0) input >> width;
if (readItems == 1) input >> height;
if (readItems == 2) input >> depth >> lastCharBeforeBinary;
readItems++;
}
else { // skip comments
std::getline(input, line);
}
}
if (depth >= 256) {
cerr << "Only 8-bit PPM format is supported" << endl;
return false;
}
unsigned byteCount = width * height * 3;
imgData.resize(byteCount);
input.read((char*)&imgData[0], byteCount * sizeof(unsigned char));
// vertically flip the image because the image origin
// in OpenGL is the lower-left corner
unsigned char tmpData;
for (unsigned y = 0; y < height / 2; y++) {
int sourceIndex = y * width * 3;
int targetIndex = (height - 1 - y) * width * 3;
for (unsigned x = 0; x < width * 3; x++) {
tmpData = imgData[targetIndex];
imgData[targetIndex] = imgData[sourceIndex];
imgData[sourceIndex] = tmpData;
sourceIndex++;
targetIndex++;
}
}
return true;
}
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
renderer->resize(w, h);
}
static void glutDisplay()
{
renderer->display();
glutSwapBuffers();
glutReportErrors();
}
static void timer(int v)
{
float offset = 1.0f;
renderer->t += offset;
glutDisplay();
glutTimerFunc(unsigned(20), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
bool redraw = false;
std::string modeStr;
switch (key) {
case '1':
if (renderer->mode == 1) renderer->mode = 0;
else renderer->mode = 1;
redraw = true;
break;
}
if (redraw) {
glutDisplay();
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(320, 320);
glutCreateWindow("Interleaved Data VBO Demo");
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
}
glutDisplayFunc(glutDisplay);
//glutIdleFunc(glutDisplay);
glutReshapeFunc(glutResize);
glutKeyboardFunc(glutKeyboard);
renderer = new Renderer;
renderer->init();
glutTimerFunc(unsigned(20), timer, 0);
glutMainLoop();
}
Vertex Array Objects
Vertex Array Objects
When examining the display( ) function in the previous example, it is noticeable that in addition to the actual render call with glDrawArrays(…) several additional function calls to glBindBuffer(…), glXXXPointer(…) and glEnableClientState(…) are needed
A Vertex Array Object(VAO) encapsulated these calls by storing the states of these additional functions and later restoring them automatically when binding the VAO again
A VAO contains only the states, but no vertex data. The vertex data is still stored in the VBOs
Creating Vertex Array Objects
The creation of VAOs follows the usual pattern:
Creating a unique ID with
glGenVertexArrays(1, &vaoID);
Creating a VAO with glBindVertexArray(…) and setting of the VBO states, they are stored to the currently bound VAO
댓글