My Vertex Buffer Object code is supposed to render textures nicely but instead the textures are being rendered oddly with some triangle shapes.
What happens - http://godofgod.co.uk/my_files/wrong.png
What is supposed to happen - http://godofgod.co.uk/my_files/right.png
This function creates the VBO and sets the vertex and texture coordinate data:
extern "C" GLuint create_box_vbo(GLdouble size[2]){
GLuint vbo;
glGenBuffers(1,&vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLsizeiptr data_size = 8*sizeof(GLdouble);
GLdouble vertices[] = {0,0, 0,size[1], size[0],0, size[0],size[1]};
glBufferData(GL_ARRAY_BUFFER, data_size, vertices, GL_STATIC_DRAW);
data_size = 8*sizeof(GLint);
GLint textcoords[] = {0,0, 0,1, 1,0, 1,1};
glBufferData(GL_ARRAY_BUFFER, data_size, textcoords, GL_STATIC_DRAW);
return vbo;
}
Here is some relavant code from another function which is supposed to draw the textures with the VBO.
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor4d(1,1,1,a/255);
glBindTexture(GL_TEXTURE_2D, texture);
glTranslated(offset[0],offset[1],0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexPointer(2, GL_DOUBLE, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer (2, GL_INT, 0, 0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawArrays(GL_TRIANGLES, 1, 3);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
I would have hoped for the code to use the first three coordinates (top-left,bottom-left,top-right) and the last three (bottom-left,top-right,bottom-right) to draw the triangles with the texture data correctly in the most efficient way. I don't see why triangles should make it more efficient but apparently that's the way to go. It, of-course, fails for some reason.
I am asking what is broken but also am I going about it in the right way generally?
Thank you.
If you want to use the one VBO for both vertex and texture coordinates you need to group them using a struct.
Define your data:
typedef struct {
GLdouble x, y;
GLint s, t;
} VertexData;
VertexData data[] = {
// x y s t
{0.0, 0.0, 0, 0},
{0.0, size[1], 0, 1},
{size[0], 0.0, 1, 0},
{size[0], size[1], 1, 1}
};
Copy it into VBO:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), (GLvoid*)data, GL_STATIC_DRAW);
Set pointers. Note that stride is your struct's size and pointer itself serves as offset:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexPointer(2, GL_DOUBLE, sizeof(VertexData), (GLvoid*)offsetof(VertexData, x));
glTexCoordPointer(2, GL_INT, sizeof(VertexData), (GLvoid*)offsetof(VertexData, s));
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
And draw.
EDIT: Implemented offset with offsetof() as Bahbar suggested.
You're loading data twice to the vbo. The second call to glBufferData is replacing the first one. Then both calls to gl*Pointer actually use the same data when calling draw.
Related
I want to draw multiple vertex arrays. This is the initialization:
unsigned int va1;
unsigned int vb1;
void init_va1() {
glGenVertexArrays(1, &va1);
glBindVertexArray(va1);
glGenBuffers(1, &vb1);
glBindBuffer(GL_ARRAY_BUFFER, vb1);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(vec2), nullptr, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(vec2), nullptr);
glEnableVertexAttribArray(0);
}
unsigned int va2;
unsigned int vb2;
void init_va2() {
glGenVertexArrays(1, &va2);
glBindVertexArray(va2);
glGenBuffers(1, &vb2);
glBindBuffer(GL_ARRAY_BUFFER, vb2);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(vec2), nullptr, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(vec2), nullptr);
glEnableVertexAttribArray(0);
}
At initialization:
init_va1();
init_va2();
At draw:
glBindVertexArray(va1);
vec2 a1[] = {
vec2(0.0, 0.0),
vec2(0.1, 0.0),
vec2(0.1, 0.1),
vec2(0.0, 0.1),
};
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(a1), a1);
glUniform3f(polygon_color_loc, 0, 1, 0);
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindVertexArray(va2);
vec2 a2[] = {
vec2(0.0, 0.0),
vec2(-0.1, 0.0),
vec2(-0.1, -0.1),
vec2(0.0, -0.1),
};
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(a2), a2);
glUniform3f(polygon_color_loc, 1, 0, 0);
glDrawArrays(GL_LINE_LOOP, 0, 4);
Whichever vertex array I initialize last will be drawn properly, and the other one will not be drawn. For example, if I call init_va1() and then init_va2(), the drawing using va2 will be shown, and the drawing using va1 will not. If I reorder the calls, then the drawing using va1 will be shown, and the drawing using va2 will not. How do I draw both vertex arrays?
glBufferSubData changes the data of the buffer that is currently bound to the specified target. The current ARRAY_BUFFER is a globale state. you need to bind the proper buffer object when you want to update the data store of the buffer:
glBindBuffer(GL_ARRAY_BUFFER, vb1);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(a1), a1);
glBindVertexArray(va1);
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, vb2);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(a2), a2);
glBindVertexArray(va2);
glDrawArrays(GL_LINE_LOOP, 0, 4);
In contrast to the ARRAY_BUFFER, the Index buffers (ELEMENT_ARRAY_BUFFER) is stated in the Vertex Array Object. If you want to change the contents of an index buffer, it is also possible just to bind the the VAO in which the index buffer is stated.
My goal is to render a list of segments using VBOs with different colors and if possible with different widths.
Each vertex is defined by:
class Vector2f {
public:
float x, y;
};
The list of segments consists in pairs of vertexes that define a segment.
Then I initialize the VBO:
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vector2f) * segments.size(), &segments[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
And then I draw using:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2f), (void*)(sizeof(float) * 0));
glColor3f(0.0f, 1.0f, 0.0f);
glDrawArrays(GL_LINES, 0, segments.size());
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
In my example, I want to give to each segment a color. The color is previously defined and can only be 1 from 3 options.
How can I do it? And can I optimize it by using indexes for color instead of repeating them all over?
If so, how?
Also, is it possible to define the width of each individual segment?
How can I do it?
Extend your vertex struct to contain color values:
class Vector2f
{
public:
float x, y;
unsigned char r, g, b;
};
And use GL_COLOR_ARRAY + glColorPointer():
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vector2f), offsetof( Vector2f, x ) );
glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(Vector2f), offsetof( Vector2f, r ) );
glDrawArrays(GL_LINES, 0, segments.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
Also, is it possible to define the width of each individual segment?
Not really with fixed-function. You either end up with a glLineWidth() + draw-call per segment (losing the performance benefit of batching draw-calls) or converting the line into triangle geometry on the CPU (significantly more complicated).
I'm using shaders and modern OpenGL. I tried glGetError() checks but no error is returned, I also tried debugging with apitrace, but I couldn't find anything. I'm not even sure if the problem is initialization or drawing code.
Sprite init:
void Sprite::init(float _x, float _y, float _width, float _height, const char* texture_path) {
x = _x;
y = _y;
width = _width;
height = _height;
texture.init(texture_path);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
// This array will hold our vertex data
// We need 4 vertices, and each vertex has 2 floats for X and Y
Vertex vertexData[4];
// Top right
vertexData[0].set_position(x + width, y + height);
vertexData[0].set_uv(1.0f, 1.0f);
// Bottom right
vertexData[1].set_position(x + width, y);
vertexData[1].set_uv(1.0f, 0.0f);
// Bottom left
vertexData[2].set_position(x, y);
vertexData[2].set_uv(0.0f, 0.0f);
// Top left
vertexData[3].set_position(x, y + height);
vertexData[3].set_uv(0.0f, 1.0f);
for (int i = 0; i < 4; i++) {
vertexData[i].set_color(255, 255, 255, 255);
}
GLuint indices[] = { // Note that we start from 0!
0, 1, 3, // First Triangle
1, 2, 3 // Second Triangle
};
// Bind the vertex buffer object (active buffer)
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// Upload the buffer data to GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Unbind the buffer
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Sprite draw:
void Sprite::draw() {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.id);
// Bind the buffer object
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// Tell OpenGL that we want to use the first attribute array
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
// This is the position attribute pointer
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
// This is the color attribute pointer
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));
// This is the UV attribute pointer
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv));
// Draw the 4 vertices to the screen
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Disable the vertex attrib array
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
// Unbind the VBO and EBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Rendering code:
Sprite sprite;
sprite.init(0, 0, 500, 500, "assets/textures/awesomeface.png");
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable shader
shader_program.enable();
sprite.draw();
// Disable shader
shader_program.disable();
// Swap buffers
window.swap_window();
You need to call glEnable(GL_TEXTURE_2D); to enable use of textures. It would also be preferable to disable it as soon as you are done using that utility, simply by putting glDisable(GL_TEXTURE2D); as soon as you have finished drawing, or whenever you are done working with textures. Hope this helps! I had this problem as well, and it took me a good 3 days of staring at a blinking cursor to figure out.
I am trying to draw a quad using a VBO, here is the full code but I'll post the code step by step: here is how I initialize the buffer:
data= vector<GLfloat> { // Global variable vector<GLfloat> data;
// Viewport: glViewport(0,0,width,height);
width/2+20,height/2+20,0.0,
width/2+20,height/2-20,0.0,
width/2-20, height/2-20,0.0,
width/2-20, height/2+20,0.0
};
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat), data.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
Then in the display function I try to draw the quad (I use double buffering):
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, data.data());
glDrawArrays(GL_QUADS, 0, 4); // Here I get segmentation fault
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glutSwapBuffers();
I get segmentation fault at the line where I call glDrawArrays, I also tried to get the OpenGL errors with glGetError(), but there's no error (it returns zero).
You are using the following line in the initialization:
glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat), data.data(), GL_STATIC_DRAW);
When drawing you set up your attrib pointers like this:
glVertexPointer(3, GL_FLOAT, 0, data.data());
I suspect that data.data() is the same in both cases - making this an error. When an ARRAY_BUFFER is bound, the pointer argument of the various gl*Pointer() functions does not refer to a client memory address, but to a byte offset in the VBOs. When drawing, the GL will try to fetch this data which is very likely to be way out of bounds of the buffer object - hence the crash. You probably meant:
glVertexPointer(3, GL_FLOAT, 0, NULL);
Note that the original VBO extension has this often-used macro in the examples section:
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
Using that, you can conveniently hide those ugly pointer arithmetic nicely and can address byte offsets in your buffer:
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));
I know, it's quite frustrating. I can't get anything to show up in my OpenGL application - all I see is an empty viewport.
When I first started writing the application, I was manually drawing the vertices (using GL_QUADS) and everything worked fine. Then I decided to switch to VBOs (Vertex Buffer Objects). Now nothing works.
Here is the structure for vertices:
struct SimpleVertex
{
GLfloat x, y;
GLbyte r, g, b, a;
};
As you can see, it is very simple - x and y coords for the vertices and RGBA color data. Here is the code that fills the vertex and index buffer:
const SimpleVertex rect_vertices[] = {
{ -0.8, 0.8, 0, 255, 0, 128 },
{ 0.8, 0.8, 0, 255, 0, 128 },
{ 0.8, -0.8, 0, 255, 0, 128 },
{ -0.8, -0.8, 0, 255, 0, 128 }
};
const GLuint rect_indices[] = {
0, 1, 2, 3
};
GLuint vertices;
GLuint indices;
glGenBuffers(1, &vertices);
glBindBuffer(GL_ARRAY_BUFFER, vertices);
glBufferData(GL_ARRAY_BUFFER,
sizeof(rect_vertices),
rect_vertices,
GL_STATIC_DRAW);
glGenBuffers(1, &indices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(rect_indices),
rect_indices,
GL_STATIC_DRAW);
And last but certainly not least, here is the code that is supposed to draw the rectangle:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindBuffer(GL_ARRAY_BUFFER, vertices);
glVertexPointer(2, GL_FLOAT, 0, NULL);
glColorPointer(4, GL_BYTE, 0,
(const GLvoid *)(sizeof(GLfloat) * 2));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices);
glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, NULL);
glDisable(GL_BLEND);
I can't figure out why nothing is being rendered. The vertex and color data is essentially unchanged from the previous version that used glVertex2f().
Simply calling the gl*Pointer functions is not enough; you need to tell OpenGL that it should pull from those particular arrays. For the built-in arrays (glVertexPointer, glColorPointer, etc), you use glEnableClientState(), for the particular array in question.
For example:
glBindBuffer(GL_ARRAY_BUFFER, vertices);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, NULL);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_BYTE, 0, (const GLvoid *)(sizeof(GLfloat) * 2));
That should provide better results.
You should also use glDisableClientState() on those arrays after you are finished rendering with them.
You seem to be missing a few steps:
VAO binding
enabling client state (VBO), e.g. glEnableClientState(GL_VERTEX_ARRAY)