VBO must be bound after VAO - c++

Code:
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
GLfloat vertices[6][4] = {
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f }
};
glUseProgram(shadptr->progGraphics);
glBindVertexArray(vao_g);
glBindBuffer(GL_ARRAY_BUFFER, vbo_g);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, 6);
glUseProgram(shadptr->progText);
glBindVertexArray(vao_t);
glBindBuffer(GL_ARRAY_BUFFER, vbo_t);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, 6);
SDL_GL_SwapWindow(winptr->window);
}
void set_buffers()
{
glGenVertexArrays(1, &vao_g);
glBindVertexArray(vao_g);
glGenBuffers(1, &vbo_g);
glBindBuffer(GL_ARRAY_BUFFER, vbo_g);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
glGenVertexArrays(1, &vao_t);
glBindVertexArray(vao_t);
glGenBuffers(1, &vbo_t);
glBindBuffer(GL_ARRAY_BUFFER, vbo_t);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
}
I have a problem. I don't know why I have to bind vbo after binding vao in render function. When I don't bind vbo, in RenderDoc I have an error "glDrawArrays has generated an error (GL_OUT_OF_MEMORY)" and in mesh output, in VS input I see wrong values or --- value.

I don't know why I have to bind vbo after binding vao in render function"
Because you try to create and initialize the buffer object 's data store by glBufferData and for this the buffer has to be bound.
I recommend to move the code, which creates and initializes the buffer to set_buffers. Then it will be sufficient to bind the vertex array object only, in the function render:
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shadptr->progGraphics);
glBindVertexArray(vao_g);
glDrawArrays(GL_TRIANGLES, 0, 6);
glUseProgram(shadptr->progText);
glBindVertexArray(vao_t);
glDrawArrays(GL_TRIANGLES, 0, 6);
SDL_GL_SwapWindow(winptr->window);
}
void set_buffers()
{
....
GLfloat vertices[6][4] = {
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f }
};
glBindBuffer(GL_ARRAY_BUFFER, vbo_g);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo_t);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
I recommend to use glBufferSubData, to change the content of a buffer. This will update the buffer, but doesn't creates and initializes a completely new data store and thus would be much more efficient.

Related

SegFault after calling glDrawArrays

I am attempting to write a sprite batcher.
Originally, I was uploading the following data to openGL in order to see if I could get anything to the screen:
static GLfloat const vertexData[] = {
//Position //Color //UV Coords
0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f
};
These are my Vertex and Fragment Shaders:
const char* vert = "#version 330 core\n"
"layout (location = 0) in vec2 vPos;\n"
"layout (location = 1) in vec4 vColor;\n"
"layout (location = 2) in vec2 vTexPos;\n"
"\n"
"out vec4 fColor;\n"
"out vec2 fTexPos;\n"
"\n"
"void main() {\n"
" gl_Position = vec4(vPos.xy, 0.0, 1.0);\n"
" fColor = vColor;\n"
" fTexPos = vec2(vTexPos.s, 1.0 - vTexPos.t);\n"
"}";
const char* frag = "#version 330 core\n"
"in vec4 fColor;\n"
"in vec2 fTexPos;\n"
"out vec4 finalColor;\n"
"\n"
"uniform sampler2D textureUniform;\n"
"\n"
"void main() {\n"
" vec4 textureColor = texture(textureUniform, fTexPos);"
" finalColor = fColor * textureColor;\n"
"}";
I set up the VAO and VBO like so:
auto vaoID_ = 0;
auto vboID_ = 0;
glGenVertexArrays(1, &vaoID_);
glGenBuffers(1, &vboID_);
glBindVertexArray(vaoID_);
glBindBuffer(GL_ARRAY_BUFFER, vboID_);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, (void*)0); // Screen Position
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 8, (void*)(2 *sizeof(GL_FLOAT)); //Color
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8, (void*)(6 * sizeof(GL_FLOAT)); //UV
And finally, on each loop, I would orphan the buffer and then sub the data in.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), nullptr, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertexData), vertexData);
glDrawArrays(GL_TRIANGLES, 0, 6);
This worked fine!
So, I attempted to begin to write a basic sprite batch.
I grouped my vertex data into a POD struct:
struct VertexData {
struct {
GLfloat screenx;
GLfloat screeny;
} position;
struct {
GLfloat r;
GLfloat g;
GLfloat b;
GLfloat a;
} color;
struct {
GLfloat texu;
GLfloat texv;
} uv;
}
And used a vector to batch everything together:
std::vector<VertexData> spritebatch_
I changed the calls to glVertexAttribPointer to match this struct (although I think this change was unneeded?)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, position)); // Screen Position
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, color)); //Color
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, uv)); //UV
To test it, on every loop I would feed it the same data that was in the static GLfloat array:
spritebatch_.push_back({0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
spritebatch_.push_back({-0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f});
spritebatch_.push_back({0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f});
spritebatch_.push_back({-0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f});
spritebatch_.push_back({-0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f});
spritebatch_.push_back({0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f});
And then attempt to orphan the buffer and sub the data in:
glBufferData(GL_VERTEX_ARRAY, spritebatch_.size() * sizeof(VertexData), nullptr, GL_DYNAMIC_DRAW);
glBufferSubData(GL_VERTEX_ARRAY, 0, spritebatch_.size() * sizeof(VertexData), spritebatch_.data());
glDrawArrays(GL_TRIANGLES, 0, spritebatch_.size());
Finally, I would reset the spritebatch_: spritebatch_.clear();
However, this segfaults. Am I doing something incorrect when I go to orphan the buffer and sub the data in? The data formats still match (VertexData POD members are all GLfloats still), the size of spritebatch_ is still 6 (6 vertices to make a square), and the location of that data is still the same within each vertex.
The 5th parameter of glVertexAttribPointer is the "stride", the byte offset between consecutive generic vertex attributes.
So the change from
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, (void*)0); // Screen Position
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 8, (void*)(2 *sizeof(GL_FLOAT)); //Color
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8, (void*)(6 * sizeof(GL_FLOAT)); //UV
to
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, position)); // Screen Position
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, color)); //Color
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)offsetof(VertexData, uv)); //UV
is important, because sizeof(VertexData) is not 8, but it is 8*sizeof(GL_FLOAT) .
The first parameter of glBufferData respectively glBufferSubData has to be the enumerator constant for the target, which is one of GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, ... .
GL_VERTEX_ARRAY identifies the vertex array for the fixed function client-side capability - See glEnableClientState.
Change:
glBufferData(GL_VERTEX_ARRAY, ...);
glBufferData(GL_ARRAY_BUFFER, ...);
glBufferSubData(GL_VERTEX_ARRAY, ...);
glBufferSubData(GL_ARRAY_BUFFER, ...);
Note, if you would check for OpenGL errors (glGetError or Debug Output), you would get a GL_INVALID_ENUM error.
The data store for the array buffer is not created, this causes the segment fault.

Setting up a second vertex array object has led to odd rendering

So i have a couple classes. A renderer and box2drenderer. Now both use their own vertex buffer and their own vertex array object. The Renderer is instantiated first with the following code:
glGenBuffers(1, &ebo);
glGenBuffers(1, &vbo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLfloat vertices[] = {
// Position(2) Color(3) Texcoords(2)
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, // Top-left
1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, // Top-right
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, // Bottom-right
0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f // Bottom-left
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 0);
GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), (void*)(5 * sizeof(GLfloat)));
glBindVertexArray(0);
glUseProgram(shaderProgram);
projection = glm::ortho(0.0f, width, height, 0.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUseProgram(0);
Then I call the setup function for the box2d:
void Box2DRenderer::setRenderer(Renderer * r) {
this->renderer = r;
const GLchar * fragSource =
"#version 150 core\n\
precision mediump float;\n\
uniform vec4 u_color;\n\
out vec4 Color;\n\
\n\
void main()\n\
{\n\
Color = u_color;\n\
}";
const GLchar * vertSource =
"#version 150 core\n\
uniform mediump mat4 u_projection;\n\
uniform mediump float u_pointSize;\n\
in vec2 a_position;\n\
\n\
void main()\n\
{\n\
gl_PointSize = u_pointSize;\n\
gl_Position = u_projection * vec4(a_position, 0.0, 1.0);\n\
}";
this->renderer->compileProgram(vertSource, fragSource, vertShader, fragShader, shaderProgram);
glUseProgram(shaderProgram);
glGenBuffers(1, &vbo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "u_projection"), 1, GL_FALSE, glm::value_ptr(this->renderer->getProjectionMatrix()));
GLuint positionLocation = glGetAttribLocation(shaderProgram, "a_position");
glEnableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
glBindVertexArray(0);
colorLocation = glGetUniformLocation(shaderProgram, "u_color");
pointSizeLocation = glGetUniformLocation(shaderProgram, "u_pointSize");
glUseProgram(0);
}
The Renderer for now just draws textures. So i draw the player via the method:
void Renderer::renderTexture(sf::FloatRect &bounds, Texture &texture, Region *region) {
glm::mat4 model;
model = glm::translate(model, glm::vec3(bounds.left, bounds.top, 0.0f));
model = glm::scale(model, glm::vec3(bounds.width, bounds.height, 0.0f));
GLint modelMat = glGetUniformLocation(shaderProgram, "mMatrix");
glUniformMatrix4fv(modelMat, 1, GL_FALSE, glm::value_ptr(model));
float x = region->pos.x / texture.getWidth();
float y = region->pos.y / texture.getHeight();
float rx = (region->width + region->pos.x) / texture.getWidth();
float ry = (region->height + region->pos.y) / texture.getHeight();
GLfloat vertices[] = {
// Position(2) Color(3) Texcoords(2)
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, x, y, // Top-left
1.0f, 0.0f, 1.0f, 1.0f, 1.0f, rx, y, // Top-right
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, rx, ry, // Bottom-right
0.0f, 1.0f, 1.0f, 1.0f, 1.0f, x, ry // Bottom-left
};
glBindTexture(GL_TEXTURE_2D, texture.getTextureId());
glBindVertexArray(vao);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
Now, if i don't initialize the box2d renderer, it works fine. If i have the box2d renderer, the texture coords are getting messed up. The whole texture seems to get drawn across the screen instead of regions at their correct place.
Given i'm turning on and off the BindVertexArray, I feel like I shouldn't have an issue, but for the life of me I can't figure it out. I can post screenshots of the difference if you'd like.
You probably fell victim to a fairly common misconception: Contrary to what you might have expected, the GL_ARRAY_BUFFER binding is not part of the VAO state.
At the tail end of the posted code you have this sequence:
glBindVertexArray(vao);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
The glBufferSubData() call will modify data in the currently bound GL_ARRAY_BUFFER, which is the buffer that you last made a glBindBuffer(GL_ARRAY_BUFFER, ...) call for. This is unrelated to the buffer you had bound when you previously used the VAO.
For additional illustration, here is a typical call sequence:
glBindVertexArray(vaoA);
glBindBuffer(GL_ARRAY_BUFFER, vboA);
glBindVertexArray(vaoB);
glBindBuffer(GL_ARRAY_BUFFER, vboB);
glBindVertexArray(vaoA);
The current GL_ARRAY_BUFFER binding at the end of this sequence is vboB. Since the binding is not part of the VAO state, it is simply based on the most recent glBindBuffer() call.
All you need to do to fix this is to add the glBindBuffer() call before glBufferSubData().
Note that the GL_ELEMENT_ARRAY_BUFFER binding is part of the VAO state. This may seem inconsistent, but it's not. The VAO bundles all the vertex setup state that is used by draw commands. The GL_ELEMENT_ARRAY_BUFFER binding controls which index buffer is used by draw commands, so it is part of this state. On the other hand, the current GL_ARRAY_BUFFER binding has no effect on the draw command, and is therefore not part of the VAO state.

How do OpenGL's buffers work?

I don't understood how do OpenGL's buffers work. I learn OpenGL, by means of OpenGL Redbook 8th edition.
For example, I have an array of position, an array of color and an array of indices:
static const GLfloat strip_position[] =
{
-4.0f, 0.0f, -1.0f, 1.0f, //0
-3.5f, -1.0f, -1.0f, 1.0f, //1
-3.0f, 0.0f, -1.0f, 1.0f, //2
-2.5f, -1.0f, -1.0f, 1.0f, //3
-2.0f, 0.0f, -1.0f, 1.0f, //4
-1.5f, -1.0f, -1.0f, 1.0f, //5
-1.0f, 0.0f, -1.0f, 1.0f, //6
-0.5f, -1.0f, -1.0f, 1.0f, //7
0.0f, 0.0f, -1.0f, 1.0f //8
};
static const GLfloat strip_colors[] =
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
};
static const GLushort strip_indices[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8
};
Good.Then I create Vertex Array Object is follows:
GLuint vao[1]; // vertex array object
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
In my understanding, first parameter (GLsizei n) is number of an arrays of position(or coordinate of vertices of ONE my object).
Then I create Element Array Buffer is follows:
GLuint ebo[1]; // element buffer object
glGenBuffers(1, ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(strip_indices),
strip_indices,
GL_STATIC_DRAW
);
Then I create Vertex Buffer Object is follows:
GLuint vbo[1]; // vertex buffer object
glGenBuffers(1, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(strip_position) + sizeof(strip_colors),
NULL,
GL_STATIC_DRAW
);
glBufferSubData(
GL_ARRAY_BUFFER,
0, //offset
sizeof(strip_position), //size date
strip_position //data
);
glBufferSubData(
GL_ARRAY_BUFFER,
sizeof(strip_position), //offset
sizeof(strip_colors), //size data
strip_colors //data
);
Next I call glVertexAttribPointer() is follows:
glVertexAttribPointer(
0, //index
4, //size
GL_FLOAT, //type
GL_FALSE, //normalized
0, //stride
NULL //pointer
);
glVertexAttribPointer(
1,
4,
GL_FLOAT,
GL_FALSE,
0,
(const GLvoid*)sizeof(strip_position)
);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
What does that function?(glVertexAttribPointer() and glEnableVertexAttribArray())
Okay. I finished initialize a my data. Now I can draw it's follows:
glBindVertexArray(vao[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL);
How OpenGL understood, which buffer need to use and where it is? Word "bind" is mean a relation? i.e. something bind with something? And If I want to display a two object, what do I do?
For example, I have a two arrays of position, a two arrays of position and a two arrays of indices?
static const GLfloat TWOstrip_colors[] =
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f
};
static const GLfloat TWOstrip_colors[] =
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
};
static const GLushort TWOstrip_indices[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8
};
How do this?
OpenGL has the notion of so called objects. Those are not models or geometrical objects, but encapsulations of internal state. If you're familiar with object oriented programming, and the C++ STL OpenGL objects can be thought of kind of class instances.
The call glGenBuffers(count, out_names) could be roughtly interpreted into something like
std::map<GLuint, openglobject*> bufferobjects;
glGenBuffers(GLuint count, std::vector<GLuint> *out_names)
{
out_names->resize(count);
for(int i=0; i < count; i++) {
GLuint name = get_next_free_handle_ID();
bufferobjects[name] = NULL;
out_names.set(i, name);
}
}
So what it does is, it reserves a handle ID (OpenGL calls them names) and allocates a slot for it in the internal mapping between handles and bufferobject instance pointers.
The call glBindBuffer actually creates the buffer object, something like that
glBindBuffer(GLenum target, GLuint name)
{
openglobject *objinstance = NULL;
if( name != 0 ) {
if( !bufferobjects.has_key(name) ) {
push_openglerror( INVALID_NAME );
return;
}
objinstance = bufferobjects[name];
if( NULL == bufferobjects[name] ) {
switch(target) {
case GL_ARRAY_BUFFER:
objinstance = new OpenGLArrayBuffer; break;
case GL_ELEMENT_ARRAY_BUFFER:
objinstance = new OpenGLElementArrayBuffer; break;
/* ... and so on */
default:
push_openglerror( INVALID_TARGET ); return;
}
bufferobjects[name] = objinstance;
}
}
}
if( objinstance != NULL && target_of(objinstance) != target ) {
opengl_pusherror( INVALID_TARGET );
}
switch( target ) {
case GL_ARRAY_BUFFER:
/* this would be a static function of the subclass setting
* global singleton instance pointer
*/
OpenGLArrayBuffer::make_current(objinstance);
break;
/* ... and so on */
}
}
I think you can see there this is going: The buffer target specifies the type of subclass the instance is you're working with and its static members.
glBufferData then actually allocates memory of the particular object, and can initialize it with the contents of a buffer you pass to it. glBufferSubData just copies data to the internal storage.
So much for the Buffer Objects (of which there are several kinds).
The other part are the Vertex Array Objects. Those are special OpenGL objects that create an association between vertex attributes, which are per-vertex data passed to the shaders based on their attribute index and the array buffer objects from which this data is takes.
When you call glGenVertexArray something like this happens:
std::map<GLuint, openglobject*> vertexarrayobjects;
glGenVertexArrays(GLuint count, std::vector<GLuint> *out_names)
{
out_names->resize(count);
for(int i=0; i < count; i++) {
GLuint name = get_next_free_handle_ID();
vertexarrayrobjects[name] = NULL;
out_names.set(i, name);
}
}
Looks familiar, doesn't it? The only difference is, that a different mapping structure is used. glBindVertexArray does the allocation of an instance and so on.
Now the calls glEnableVertexAttribute and glVertexAttribPointer can be thought as the following:
glEnableVertexAttribute(GLuint idx)
{
((OpenGLVertexArrayObject*)currentvertexarray)->add_attribute(idx);
}
glVertexAttribPointer(GLuint idx, ..., void *ptr)
{
((OpenGLVertexArrayObject*)currentvertexarray)->bind_attribute(
idx,
OpenGLArrayBuffer::get_current(),
(off_t)ptr );
}
Okay, that last bit requires some explanation. That you pass a pointer to glVertexAttribPointer is a legacy from OpenGL-1.1 where there were no OpenGL buffer objects and instead you pointed directly to memory of your program. Then buffer objects got introduced and those don't require a pointer but a byte sized offset when binding. So the OpenGL devs went the dirty route and just lied to the compilers about it. I did explain the details in my answer to the question "What is the result of NULL + int?"
Note that OpenGL-4 introduced a new, much more powerfull and flexible API to create VAO attribute ←→ VBO bindings.
there is always a "current Buffer" of each target set by glBindBuffer(target, id) on which most the buffer operations know to operate.
openGL uses glEnableVertexAttribArray to know which attributes it should look for, if not called then openGL will not use the data.
glVertexAttribPointer tells openGL where in the currently bound GL_ARRAY_BUFFER the attributes must be found for the current vertexArrays. in your example: (assuming vbo[0] is still bound to GL_ARRAY_BUFFER)
attribute for index 0 is found in vbo[0] with 4 floats per vertex tightly packed and starting from 0
attribute for index 1 is found in vbo[0] with 4 floats per vertex tightly packed and starting from sizeof(strip_position)
these bindings persist over glBindBuffer calls so if you want to rebind then you'll need to bind the other buffer call glVertexAttribPointer and then you can unbind again
I suggest you always call glBindBuffer with a 0 buffer so openGL knows you don't want to work with the current buffer anymore and avoid strange behaviors
to create the second object you can refill the various buffers each time you switch objects
or you can either create 2 sets of buffers:
GLuint vao[2]; // vertex array object
glGenVertexArrays(2, vao);
glBindVertexArray(vao[0]);
GLuint ebo[2]; // element buffer object
glGenBuffers(2, ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(strip_indices),
strip_indices,
GL_STATIC_DRAW
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[1]);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(strip_indices),
TWO_strip_indices,
GL_STATIC_DRAW
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
GLuint vbo[2]; // vertex buffer object
glGenBuffers(2, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(strip_position) + sizeof(strip_colors),
NULL,
GL_STATIC_DRAW
);
glBufferSubData(
GL_ARRAY_BUFFER,
0, //offset
sizeof(strip_position), //size date
strip_position //data
);
glBufferSubData(
GL_ARRAY_BUFFER,
sizeof(strip_position), //offset
sizeof(strip_colors), //size data
strip_colors //data
);
//fill other buffer (assuming the first TWOstrip_colors was actually TWOstrip_position
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(TWOstrip_position) + sizeof(TWOstrip_colors),
NULL,
GL_STATIC_DRAW
);
glBufferSubData(
GL_ARRAY_BUFFER,
0, //offset
sizeof(TWOstrip_position), //size date
strip_position //data
);
glBufferSubData(
GL_ARRAY_BUFFER,
sizeof(TWOstrip_position), //offset
sizeof(TWOstrip_colors), //size data
strip_colors //data
);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(vao[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0])
glVertexAttribPointer(
0, //index
4, //size
GL_FLOAT, //type
GL_FALSE, //normalized
0, //stride
NULL //pointer
);
glVertexAttribPointer(
1,
4,
GL_FLOAT,
GL_FALSE,
0,
(const GLvoid*)sizeof(strip_position)
);
glBindVertexArray(vao[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(
0, //index
4, //size
GL_FLOAT, //type
GL_FALSE, //normalized
0, //stride
NULL //pointer
);
glVertexAttribPointer(
1,
4,
GL_FLOAT,
GL_FALSE,
0,
(const GLvoid*)sizeof(TWOstrip_position)
);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
then to draw:
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(vao[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL);
glBindVertexArray(vao[1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[1]);
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL);

VBO texturing does not work

The problem is that this code display nothing. I have problem with my loads of textures. If I comment the code lines about the textures treatments it works perfectly. It displays a 4 colored square. Can you help me ?
#define OFFSET_BUFFER(bytes) ((GLfloat *)NULL + bytes)
GLfloat vertices[12] =
{
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
};
GLfloat colors[12] =
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
};
GLfloat texture[8] =
{
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_WM_SetCaption("Texture Mapping",NULL);
SDL_SetVideoMode(500, 500, 32, SDL_OPENGL);
bool continuer = true;
SDL_Event event;
GLuint buf[2];
GLuint texture1;
glClearDepth(1.0f);
glClearColor(0.1f, 0.1f, 0.1f, 0.1f);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70.0f, (float)500.0f / (float)500.0f, 1.0f, 3000.0f);
glewInit();
texture1 = loadTexture("caisse.jpg");
glBindTexture(GL_TEXTURE_2D, texture1);
glGenBuffers(2, &buf[0]);
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, texture1);
glBufferData(GL_ARRAY_BUFFER, sizeof(texture), texture, GL_STATIC_DRAW);
while (continuer)
{
SDL_WaitEvent(&event);
switch(event.type)
{
case SDL_QUIT:
continuer = false;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glVertexPointer(3, GL_FLOAT, 0, OFFSET_BUFFER(0));
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glColorPointer(3, GL_FLOAT, 0, OFFSET_BUFFER(0));
glBindBuffer(GL_ARRAY_BUFFER, texture1);
glTexCoordPointer(2, GL_FLOAT, 0, OFFSET_BUFFER(0));
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
glFlush();
SDL_GL_SwapBuffers();
}
SDL_Quit();
return 0;
}
Firstly, you need to start putting glGetError() in your code for debugging, as you're certainly generating some errors here.
You're binding your texture texture1 with glBindBuffer, which is not correct to do so. Texture coordinates are not the same as textures. You should be generating 3 buffers instead of 2, and loading the texture[8] texcoords into the third buffer.

Texture binding with VBO does not work correctly

the purpose of my program is to display 2 quads with 2 different textures. The problem is that the oldest texture loaded is set on the two quads (only texQuadB). I tried to replace the two call of buf[3] by texQuadA and texQuadB but it doesn't work. I cannot find the right way to bind a texture with a specific VBO.
#define OFFSET_BUFFER(bytes) ((GLfloat *)NULL + bytes)
GLfloat verticesQuadA[12] =
{
-1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f,
};
GLfloat verticesQuadB[12] =
{
0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f,
};
GLfloat colors[12] =
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
};
GLfloat texture[8] =
{
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_WM_SetCaption("Texture Mapping",NULL);
SDL_SetVideoMode(500, 500, 32, SDL_OPENGL);
bool continuer = true;
SDL_Event event;
GLuint buf[4];
GLuint texQuadA, texQuadB;
glClearDepth(1.0f);
glClearColor(0.1f, 0.1f, 0.1f, 0.1f);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70.0f, (float)500.0f / (float)500.0f, 1.0f, 3000.0f);
glewInit();
texQuadA = loadTexture("caisse.jpg");
texQuadB = loadTexture("metal.jpg");
//glBindTexture(GL_TEXTURE_2D, texQuadA);
//glBindTexture(GL_TEXTURE_2D, texQuadB);
glGenBuffers(4, buf);
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesQuadA), verticesQuadA, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesQuadB), verticesQuadB, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buf[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buf[3]);
glBufferData(GL_ARRAY_BUFFER, sizeof(texture), texture, GL_STATIC_DRAW);
while (continuer)
{
SDL_WaitEvent(&event);
switch(event.type)
{
case SDL_QUIT:
continuer = false;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
//Draw Quad A ---------------------------------------
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glVertexPointer(3, GL_FLOAT, 0, OFFSET_BUFFER(0));
glBindBuffer(GL_ARRAY_BUFFER, buf[2]);
glColorPointer(3, GL_FLOAT, 0, OFFSET_BUFFER(0));
glBindBuffer(GL_ARRAY_BUFFER, buf[3]);
glTexCoordPointer(2, GL_FLOAT, 0, OFFSET_BUFFER(0));
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
//Draw Quad B ---------------------------------------
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glVertexPointer(3, GL_FLOAT, 0, OFFSET_BUFFER(0));
glBindBuffer(GL_ARRAY_BUFFER, buf[2]);
glColorPointer(3, GL_FLOAT, 0, OFFSET_BUFFER(0));
glBindBuffer(GL_ARRAY_BUFFER, buf[3]);
glTexCoordPointer(2, GL_FLOAT, 0, OFFSET_BUFFER(0));
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
//----------------------------------------------------
glFlush();
SDL_GL_SwapBuffers();
}
glDeleteBuffers(3, buf);
glDisable(GL_TEXTURE_2D);
SDL_Quit();
return 0;
}
OpenGL is a state machine. Binding a texture is setting part of that state. When textures are enabled, OpenGL will use the current texture state (the last texture you bound) when it goes to draw the geometry. Before you draw Quad A, bind texQuadA. Before you draw Quad B, bind texQuadB:
glBindTexture(GL_TEXTURE_2D, texQuadA);
// Draw Quad A
glBindTexture(GL_TEXTURE_2D, texQuadB);
// Draw Quad B
VBOs are unrelated in this case.
VBOs have no interaction with texture objects. Binding a VBO does not associate it to a texture.
To select which texture you want to use for drawing you call glBindTexture before doing the drawing commands using it.