views:

230

answers:

2

Hello. 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.

A: 

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.

Bahbar
Thank you for your answer.I read somewhere it could be done this way. This was on a topic and the person who wanted a solution said it worked.If I put the texture coordinates after the vertices, what do I put inside "glTexCoordPointer"? I don't understand how to get it to point to the 9th element in the array where the texture coordinates would start.Can I put those point functions in the create_box_vbo function and use them only once?Thank you again.
Matthew Mitchell
Well I just tried moving the functions and it make it worse so I guess I can't do that.
Matthew Mitchell
I just thought to myself that the texture coordinates will be the same for each texture so why not just have one VBO for the texture coordinates. It's stupid not to do that.
Matthew Mitchell
@Matthew: Try generating two VBO buffers in your code (so you would have vbo1 and vbo2, or an GLuint array), and after the first `glBindData()`, do another `glBindBuffer()` at the second VBO before you upload the texture coordinates. You can still re-use either of them, but they can't be in the same buffer unless you want to use interleaved data.
Xavier Ho
Thank you for your input. I would like to know how to interleave the data into a VBO. I will need to have different texture coordinate data for each of my rounded textures as well as the vertex data. Having two VBOs will be more awkward with my return values (especially since I'm using C++ with ctypes for python) and I assume it isn't the more effective way.
Matthew Mitchell
A: 

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.

Ivan Baldin
real nitpicking, but you'd better use some form of offsetof to find the offset for the texture coordinates. Here there is likely no padding between y and s, but say you had 3 shorts for position, then the story would be very different (3*sizeof(GLshort) would be the wrong offset, as the offset would likely be 8 bytes rather than 6).
Bahbar
Thank you very much for this. I'll be looking into it now.
Matthew Mitchell
Using GLdouble for the texture coordinates as well as the vertices, surely I can just use a multidimensional array? I will need to use doubles for the texture coordinates for my rounded textures.
Matthew Mitchell
At the moment some things aren't being drawn or are being draw on the top-left of the screen. I suppose I'll look at it later.
Matthew Mitchell