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);
Related
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.
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.
I was hoping to get some help on an OpenGL question concerning the creation and rendering of VAOs. The goal here is simply to take this:
glBegin(GL_TRIANGLES);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
glVertex3f(-0.5f, 0.5f, 0.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glVertex3f(0.5f, -0.5f, 0.0f);
glVertex3f(-0.5f, 0.5f, 0.0f);
glVertex3f(0.5f, -0.5f, 0.0f);
glVertex3f(0.5f, 0.5f, 0.0f);
glEnd();
which renders a red square in the middle of the window, and turn it into a VAO with vertices, colors, and indices. Now, this is what I have so far as far as creating the VAO (sorry if this is a bit long for the code):
//initialize all data
verts_amt = 6;
Vertex* verts = (Vertex*)malloc(sizeof(Vertex) * verts_amt);
int* indices = (int*)malloc(sizeof(int) * verts_amt);
verts[0] = createVertex(-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
verts[1] = createVertex(-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
verts[2] = createVertex(0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
verts[3] = createVertex(-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
verts[4] = createVertex(0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
verts[5] = createVertex(0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
int i;
for(i = 0; i < 6; i ++)
indices[i] = i;
unsigned int vbo, ibo;
//create, bind, set data, and then unbind for the vbo and ibo
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, verts_amt * sizeof(Vertex), verts, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, verts_amt * sizeof(int), indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//create vao, bind vao, bind and set data for vbo, bind ibo, then unbind vao
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), NULL);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, r));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBindVertexArray(0);
in case you are wondering, Vertex is just a struct made up of seven floats in the order x, y, z, r, g, b, a. After looking through similar questions on this topic, I'm still not seeing what I'm missing and/or not doing right, because when I render it with these simple lines:
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, verts_amt, GL_UNSIGNED_INT, NULL);
it renders just a competey white square. Keep in mind I'm not changing anything else about my rendering loop except of course getting rid of the Vertex3f, glBegin/glEnd, and Color4f calls. I should also mention that I'm not using a shader program, I'm not entirely sure if that changes anything drastically here. Any help on this would be much appreciated!
I should also mention that I'm not using a shader program.
You cannot use glVertexAttribPointer without a shader. These functions cannot interface with the OpenGL fixed function pipeline (FFP).
If you want to use the FFP and still use buffer objects, then you should use the appropriate functions. glVertexPointer (no "Attrib") and glColorPointer are the array equivalents to glVertex and glColor. These work with VAOs just fine.
So the only thing you need to change is your two glVertexAttribPointer calls to be glVertexPointer and glColorPointer (and of course adjusting the parameters accordingly).
I'm trying to run my first OpenGL program. In the main() function I have infinity loop:
do {
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(programID);
_collection[0].draw();
_collection[1].draw();
glfwSwapBuffers(window);
glfwPollEvents();
} while(glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0)
The function _collection[].draw() should draw rectangles:
static const GLfloat g_vertex_buffer_data[] = {
x, y, 0.0f, // lewy górny
x, y - 0.4f, 0.0f, // lewy dolny
x + 0.4f, y - 0.4f, 0.0f, // prawy dolny
x + 0.4f, y, 0.0f, // lewy górny
x + 0.02f, y - 0.02f, 0.0f, // lewy górny
x + 0.02f, y - 0.4f + 0.02f, 0.0f, // lewy dolny
x + 0.4f - 0.02f, y - 0.4f + 0.02f, 0.0f, // prawy dolny
x + 0.4f - 0.02f, y - 0.02f, 0.0f, // lewy górny
};
static const GLfloat g_color_buffer_data[] = {
1.0f, 1.0f, 1.0f, // lewy górny
1.0f, 1.0f, 1.0f, // lewy dolny
1.0f, 1.0f, 1.0f, // prawy dolny
1.0f, 1.0f, 1.0f, // lewy górny
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
};
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertexPosition_modelspaceID);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
vertexPosition_modelspaceID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// przekazuję kolory wierzchołków
glEnableVertexAttribArray(vertexColorID);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(
vertexColorID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// rysuję wszystko
glDrawArrays(GL_QUADS, 0, 8);
glDisableVertexAttribArray(vertexPosition_modelspaceID);
glDisableVertexAttribArray(vertexColorID);
My problem is that: When I run the program I see only the effect of a run the first function draw() - this with index 0. Then I change places these functions:
_collection[1].draw();
_collection[0].draw();
I still see the effect of the first function - in this case with index number 1.
It looks like there is something blocking the code from the second draw() function to run.
What is the problem? How can I fix it?
The second draw function isn't being blocked from executing. Since your vertice and color information is defined as static inside the body of your draw() function, those values won't change regardless of which element of _collection you are drawing. That's why drawing the two collections yields the same result -- you are drawing your vertices in the same location, and with the same colors.
To fix the problem, you only want to store vertex and color information once. Each of your collections should only contain x and y values, indicating their position. You don't want multiple collections of vertices and colors, you want a single collection of vertices and colors which you draw in several different locations.
You should create your vertex and color arrays in your main function before you enter your main loop. You should also use glGenBuffers and glBindBuffer followed by glBufferData to tell OpenGL about your vertex and color arrays in your main program before your main loop as well. Then you can take the calls to glGenBuffers and glBufferData out of your draw function. You should also call glVertexAttribPointer for both the vertex and color arrays in your main function and remove them from your draw() function.
// Note that your vertex data isn't contingent on 'x' and 'y' positions.
// You will use the vertex shader to move your boxes around later.
GLfloat g_vertex_buffer_data[] = {
0.0f, 0, 0.0f, // lewy górny
0.0f, 0.4f, 0.0f, // lewy dolny
0.4f, 0.4f, 0.0f, // prawy dolny
0.4f, 0.0f, 0.0f, // lewy górny
0.02f, 0.02f, 0.0f, // lewy górny
0.02f, 0.4f + 0.02f, 0.0f, // lewy dolny
0.4f - 0.02f, 0.4f + 0.02f, 0.0f, // prawy dolny
0.4f - 0.02f, 0.02f, 0.0f, // lewy górny
};
GLfloat g_color_buffer_data[] = {
1.0f, 1.0f, 1.0f, // lewy górny
1.0f, 1.0f, 1.0f, // lewy dolny
1.0f, 1.0f, 1.0f, // prawy dolny
1.0f, 1.0f, 1.0f, // lewy górny
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
};
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
glVertexAttribPointer(
vertexPosition_modelspaceID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
glVertexAttribPointer(
vertexColorID, // The attribute we want to configure
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// All of the above information you only need to specify to openGL once, not every time you draw a frame!
You need to change your shader so that it accepts the x and y offset from each of your collections:
#version 150
uniform float collectionX;
uniform float collectionY;
in vec3 vertexPosition_modelspaceID; // This is the vertex attribute which the name 'vertexPosition_modelspaceID' corresponds to.
// Remember that your shader will also accept a color and give it to the fragment shader, include that code as well.
void main()
{
gl_Position = vec4(vertexPosition_modelspaceID.x + collectionX, vertexPosition_modelspaceID.y + collectionY, vertexPosition_modelspaceID.z, 1.0);
}
And you need to get the locations of the uniform variables you just added to your shader in your main program before the loop:
// Call these functions after you compile and link your shaders. programID should be your compiled and linked shader program.
GLuint collectionXID = glGetUniformLocation(programID, "collectionX");
GLuint collectionYID = glGetUniformLocation(programID, "collectionY");
Your draw function will be very simple now:
void draw()
{
glDrawArrays(GL_QUADS, 0, 8);
}
Finally, your main loop will look something like this:
do
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(programID);
glEnableVertexAttribArray(vertexPosition_modelspaceID);
glEnableVertexAttribArray(vertexColorID);
glUniform1f(collectionXID, _collection[0].x);
glUniform1f(collectionYID, _collection[0].y);
_collection[0].draw();
glUniform1f(collectionXID, _collection[1].x);
glUniform1f(collectionYID, _collection[1].y);
_collection[1].draw();
glfwSwapBuffers(window);
glDisableVertexAttribArray(vertexPosition_modelspaceID);
glDisableVertexAttribArray(vertexColorID);
glfwPollEvents();
} while(glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0)
Note that you are now specifying the location at which to draw the vertices to your shader program by passing your individual collection's x and y position with the glUniform1f function. It is more common to move your vertices around with a transformation matrix, but that is a rather complicated topic itself.
Assuming the collections have different x and y positions, they will now draw in different locations.
I am learning OpenGL 4.0 and I want to draw a simple triangle using OpenGL 4.0 and GLSL. I'm using VAO with interleaved arrays to do it, but the result it display is not what I want:
Now I post part of my code:
void SceneBasic::setupVAOInterleavedArrays()
{
//三角形的顶点和颜色信息数组:混合数组
float positionAndColorData[] = {
-0.8f, -0.8f, 0.0f,1.0f, 0.0f, 0.0f,
0.8f, -0.8f, 0.0f,0.0f, 1.0f, 0.0f,
0.0f, 0.8f, 0.0f,0.0f, 0.0f, 1.0f };
//glInterleavedArrays(GL_C3F_V3F,0,positionAndColorData)
GLuint vboHandle;//VBO
glGenBuffers(1,&vboHandle);
glBindBuffer(GL_ARRAY_BUFFER,vboHandle);
glBufferData(GL_ARRAY_BUFFER,18 * sizeof(float),
positionAndColorData,GL_STATIC_DRAW);
//VAO
glGenVertexArrays(1,&vaoHandle);
glBindVertexArray(vaoHandle);
//enable the generic vertex attribute indexes
//indicates that the values for the attributes will be accessed
//and used for rendering
glEnableVertexAttribArray(0);//position
glEnableVertexAttribArray(1);//color
//make the connection between the buffer objects and the generic
//vertex attributes indexes
glBindBuffer(GL_ARRAY_BUFFER,vboHandle);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3 * sizeof(float),BUFFER_OFFSET(0));
glBindBuffer(GL_ARRAY_BUFFER,vboHandle);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,3 * sizeof(float),BUFFER_OFFSET(3 * sizeof(float)));
}
void SceneBasic::initScene()
{
compileAndLinkShader();
//setupVAO();
setupVAOInterleavedArrays();
}
void SceneBasic::render()
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vaoHandle);
glDrawArrays(GL_TRIANGLES,0,3);
glBindVertexArray(0);
}
But if I don't use interleaved array, the result is right:
This is the part of my code when I don't use interleaved arrays:
void SceneBasic::setupVAO()
{
//三角形的顶点和颜色信息数组
float positionData[] = {
-0.8f, -0.8f, 0.0f,
0.8f, -0.8f, 0.0f,
0.0f, 0.8f, 0.0f };
float colorData[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f };
glGenBuffers(2,vboHandles);
GLuint positionBufferHandle = vboHandles[0];
GLuint colorBufferHandle = vboHandles[1];
glBindBuffer(GL_ARRAY_BUFFER,positionBufferHandle);
glBufferData(GL_ARRAY_BUFFER,9 * sizeof(float),
positionData,GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,colorBufferHandle);
glBufferData(GL_ARRAY_BUFFER,9 * sizeof(float),
colorData,GL_STATIC_DRAW);
//VAO
glGenVertexArrays(1,&vaoHandle);
glBindVertexArray(vaoHandle);
//enable the generic vertex attribute indexes
//indicates that the values for the attributes will be acessed
//and used for rendering
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//make the connection between the buffer objects abd the generic
//vertex attributes indexes
glBindBuffer(GL_ARRAY_BUFFER,positionBufferHandle);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,BUFFER_OFFSET(0));
glBindBuffer(GL_ARRAY_BUFFER,colorBufferHandle);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,BUFFER_OFFSET(0));
}
I am so curious, why is my code not producing the expected result when I use interleaved arrays?
The stride is wrong, since you have 6 elements per vertex, you need to pass 6 * sizeof (float) as the stride.