Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411423 Posts in 69363 Topics- by 58416 Members - Latest Member: JamesAGreen

April 19, 2024, 12:30:34 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)OpenGL Interleaved VBOs, Strides and Offsets
Pages: [1]
Print
Author Topic: OpenGL Interleaved VBOs, Strides and Offsets  (Read 6053 times)
Sean A.
Level 8
***



View Profile
« on: March 18, 2013, 07:15:13 PM »

So I've been trying to wrap my head around VBOs and I can't seem to get it to work. I've been using LWJGL. I'm trying to draw a cube and I'm using an interleaved buffer with v3f,n3f,c4f,t2f setup. I am putting the arrays one after another into the buffer giving a VVCCNNTT result. Now when I set up the pointers from my understanding this is how the bytes should be arranged for each vertex (floats are 4 bytes):
Code:
             Amount    Bytes  Offset
Vertex  3f = 12bytes =  0-11  0
Normal  3f = 12bytes = 12-23  12
Color   4f = 16bytes = 24-39  24
Texture 2f = 8bytes  = 40-47  40

Stride = Total Bytes per Vertex = 12 * 4 = 48

So the code:
glVertexPointer( 3, GL_FLOAT, 48, 0);
glNormalPointer( GL_FLOAT, 48, 12);
glColorPointer( 4, GL_FLOAT, 48, 24);
glTexCoordPointer( 2, GL_FLOAT, 48, 40);

Should point to the correct places in the arrays

The results I get make it clear that it is looking in the wrong place. So I'm am hoping someone can point me in the right direction. If you need more context or code around this let me know.
Logged
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #1 on: March 18, 2013, 09:18:32 PM »

Looks right to me. Maybe paste your VBO data initialization and glBufferData call? Also a screenshot, if it's drawn anything cool looking from plugging random memory into places it doesn't belong. Grin
Logged

Polly
Level 6
*



View Profile
« Reply #2 on: March 19, 2013, 07:08:59 AM »

I am putting the arrays one after another into the buffer giving a VVCCNNTT result.

This kind of sounds like your buffer first contains all vertex positions, then all vertex colors etc. A interleaved buffer should first contain all attributes / data of the first vertex, then the second vertex etc. But perhaps i simply misunderstood your statement Smiley
Logged
zalzane
Level 5
*****


View Profile
« Reply #3 on: March 19, 2013, 08:42:09 AM »

I don't use opengl so I'm not of much technical help here. However I can offer moral support! VBOs are a serious pain in the ass to implement in opengl for the first time, and to be quite frank it's probably one of the biggest hurdles for new devs. After you get it working for the first time, I suggest writing a wrapper class so that you never have to deal directly with the buffers again.

Also, if you've got data in the buffers and it isn't appearing correctly, you can inspect the contents of the buffers directly using gDEBugger.
Logged
Sean A.
Level 8
***



View Profile
« Reply #4 on: March 19, 2013, 07:22:01 PM »

Ok thanks for all the comments.

@Polly As far as I know you can interleave buffers in either way http://www.java-gaming.org/index.php?topic=24272.0 That tutorial describes both methods for interleaving.

So I should mention I'm indexing the VBO as well to reuse vertices, anyway here is some more code. This is basically the whole rendering, the only part missing is the arrays being created to store the data.
Code:
//Create Float Buffers
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertices.length+normals.length+colors.length+textures.length);
IntBuffer indexBuffer = BufferUtils.createIntBuffer(indices.length);

//Put the points
vertexBuffer.put(vertices);
vertexBuffer.put(normals);
vertexBuffer.put(colors);
vertexBuffer.put(textures);
vertexBuffer.flip();

indexBuffer.put(indices);
indexBuffer.flip();

IntBuffer ib = BufferUtils.createIntBuffer(2);

glGenBuffersARB(ib);
int vertexHandle = ib.get(0);
int indexHandle = ib.get(1);

glEnableClientState(GL11.GL_VERTEX_ARRAY);
glEnableClientState(GL11.GL_NORMAL_ARRAY);
glEnableClientState(GL11.GL_COLOR_ARRAY);
glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);

glBindBufferARB(GL_ARRAY_BUFFER_ARB,vertexHandle);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexBuffer, GL_STATIC_DRAW_ARB);
glVertexPointer(3,GL_FLOAT,12 * 4, 0);
glNormalPointer(GL_FLOAT,12 * 4, 3 * 4);
glColorPointer(4,GL_FLOAT,12 * 4, 6 * 4);
glTexCoordPointer(2,GL_FLOAT,12 * 4, 10 * 4);

glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,indexHandle);
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexBuffer, GL_STATIC_DRAW_ARB);

glDrawArrays(GL_TRIANGLES,0,indices.length);
glBindBufferARB(GL_ARRAY_BUFFER_ARB,0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,0);

glDisableClientState(GL11.GL_VERTEX_ARRAY);
glDisableClientState(GL11.GL_NORMAL_ARRAY);
glDisableClientState(GL11.GL_COLOR_ARRAY);
glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);

ib.put(0, vertexHandle);
ib.put(1, indexHandle);
glDeleteBuffersARB(ib);

Here is an output image trying to draw one cube:


Right after I copied the text I realized glDrawArrays() is wrong and should be glDrawRangeElements() but I don't know how to use that.

Just a few addition VBO questions:

1. All of the tutorials I have read don't mention which parts of the rendering code can be executed once on object creation and what needs to be run every render loop

2. The LWJGL site mentions GL_DYNAMIC_DRAW_ARB and GL_STREAM_DRAW_ARB, I assume these have something to do with modifying the buffers once they are created but I'm not sure
Logged
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #5 on: March 19, 2013, 09:08:15 PM »

@Polly As far as I know you can interleave buffers in either way http://www.java-gaming.org/index.php?topic=24272.0 That tutorial describes both methods for interleaving.

Ahh yeah, there's your problem. Both ways work, but they require different setup in the gl*Pointer calls. You have the data layout of the VVVCCC example (I believe this is called "planar"), but the gl*Pointer invocations of the VCVCVC example (non-planar).

If you want to stick with planar, you could change this:

Code:
glVertexPointer(3,GL_FLOAT,12 * 4, 0);
glNormalPointer(GL_FLOAT,12 * 4, 3 * 4);
glColorPointer(4,GL_FLOAT,12 * 4, 6 * 4);
glTexCoordPointer(2,GL_FLOAT,12 * 4, 10 * 4);

...to this:

Code:
glVertexPointer(3,GL_FLOAT, 0, 0);
glNormalPointer(GL_FLOAT, 0, vertices.length * 4);
glColorPointer(4,GL_FLOAT, 0, (vertices.length + normals.length) * 4);
glTexCoordPointer(2,GL_FLOAT, 0, (vertices.length + normals.length + colors.length) * 4);

Not 100% sure those offsets are correct, but you get the idea I hope! Passing 0 for stride makes GL assume your data is tightly packed, which is the case with the planar layout.

1. All of the tutorials I have read don't mention which parts of the rendering code can be executed once on object creation and what needs to be run every render loop

In (almost?) all cases, your GL state will stay exactly as you set it until you tell it to change. If you don't unbind or delete your buffers,  leave your client states enabled, and don't mess with gl*Pointer() elsewhere, your render loop can be as simple as the glDrawArrays()/glDrawElements()/glDrawRangeElements() call and nothing else. Basically anything that doesn't directly have to do with drawing to the screen or updating your visuals for animation can be left out of the render loop and only done once.

2. The LWJGL site mentions GL_DYNAMIC_DRAW_ARB and GL_STREAM_DRAW_ARB, I assume these have something to do with modifying the buffers once they are created but I'm not sure

Yep, those are hints you can give GL that may in some cases allow it to optimize your VBO for your particular usage of it. In simple terms, you want to use GL_STATIC_DRAW for data that will be drawn multiple times and never modified; GL_DYNAMIC_DRAW for data that will be drawn multiple times and modified at least once (using glBufferSubData()/glMapBuffer()/whatever); and GL_STREAM_DRAW for data that will be drawn only once.
Logged

Polly
Level 6
*



View Profile
« Reply #6 on: March 20, 2013, 03:32:52 AM »

As far as I know you can interleave buffers in either way http://www.java-gaming.org/index.php?topic=24272.0 That tutorial describes both methods for interleaving.

That tutorial uses the incorrect term for what is actually a regular batched buffer. Check out the "Formatting VBO Data" section on the OpenGL wiki.
Logged
Sean A.
Level 8
***



View Profile
« Reply #7 on: March 20, 2013, 05:38:45 AM »

Ohhhhh ok thanks, is there any performance advantage for VVVCCC or VCVCVC? But yeah the tutorials put everything I posted in the render loop which is weird to me, so I can safely move all of the buffer putting and binding and glEnableClientState calls into an init() function, and then call glDrawRangeElements in the render loop and then delete the delete the buffer in a destroy() function?
Logged
Polly
Level 6
*



View Profile
« Reply #8 on: March 20, 2013, 05:49:01 AM »

Ohhhhh ok thanks, is there any performance advantage for VVVCCC or VCVCVC?

A interleaved buffer will ( generally ) cause fewer cache misses. However, alignment is a big factor as well ( obviously .. since of how the memory addresses are calculated ), so depending on your exact vertex attribute formatting it might not always be the preferred approach. And when you want to update various attributes / ranges dynamically, it complicates the choice even further Smiley
Logged
powly
Level 4
****



View Profile WWW
« Reply #9 on: March 20, 2013, 06:54:10 AM »

Not going to say this is how it works, but it would make sense if the driver rearranged the data to be in the most efficient format for that GPU no matter how you input it.
Logged
zalzane
Level 5
*****


View Profile
« Reply #10 on: March 20, 2013, 08:18:08 AM »

Not going to say this is how it works, but it would make sense if the driver rearranged the data to be in the most efficient format for that GPU no matter how you input it.

If you gave the driver information that was not in the format that it planned on using, it would have to waste god knows how much overhead rearranging your data to match the technique it uses.
Logged
powly
Level 4
****



View Profile WWW
« Reply #11 on: March 20, 2013, 08:48:18 AM »

Doing AAABBBCCC<->ABCABCABC doesn't produce 'god knows how much overhead'. It's a pretty small thing and probably well worth it if it makes the cache usage while rendering more efficient. Could be done as a relatively simple circuit in the harware, too - the thing is pretty much out[i*dimensions+dimension_i] = in[i+dimension_i_start] for all vertices i and dimensions (position, color, normal, ...) dimension_i. And once again, I will stop with the offtopic after this one.
Logged
ham and brie
Level 3
***



View Profile
« Reply #12 on: March 20, 2013, 10:11:43 AM »

It would be risky for an OpenGL implementation to second-guess the programmer, who may well have laid out the contents of the buffer knowing that they will later overwrite part of the data and want it in a contiguous block.

It would be difficult to do the rearrangement too. For one thing, setting the data in a buffer is separate from the calls which control how the data in a buffer is to be used, which can come only just before a draw call and can be different for multiple uses of the same buffer.
Logged
powly
Level 4
****



View Profile WWW
« Reply #13 on: March 20, 2013, 12:41:41 PM »

...  and can be different for multiple uses of the same buffer.

True, doesn't make sense. The API would also probably force either way if it mattered.
Logged
Sean A.
Level 8
***



View Profile
« Reply #14 on: March 20, 2013, 06:18:24 PM »

Awesome all I had to do was fix those pointer values and now it is working well. Two more questions though:

1. Lets say I wanted to do a textured cube with the same texture on every side. I can't use indexed VBOs can I because if I reuse vertices the texture coordinates won't work right?

2. glDrawRangeElements, I understand all of the arguments except for the 'end' and 'count' arguments (the 3rd and 4th ones), I know they have to do with the amount of things being drawn but is it the amount of triangles or vertices or what?
Logged
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #15 on: March 20, 2013, 07:19:30 PM »

1. Lets say I wanted to do a textured cube with the same texture on every side. I can't use indexed VBOs can I because if I reuse vertices the texture coordinates won't work right?

Kinda. You can still use indexed VBOs, but some of your entries might need to have the same vertex positions with different normals and texture coordinates. Off the top of my head, I don't think there's a way to index the different arrays separately, so duplicating some of the data is the normal way to go about things.

2. glDrawRangeElements, I understand all of the arguments except for the 'end' and 'count' arguments (the 3rd and 4th ones), I know they have to do with the amount of things being drawn but is it the amount of triangles or vertices or what?

I'm pretty sure count is the number of vertices. start and end seem a bit confusing, though... I've only ever used glDrawElements, but from what I'm reading of the glDrawRangeElements man page, it looks like you just need to specify the minimum and maximum allowable values that will be pulled from your index array. I guess that makes it safer than glDrawElements, since you won't be able to index an element outside your VBO if your index buffer is filled with memory garbage or something?
Logged

Sean A.
Level 8
***



View Profile
« Reply #16 on: March 20, 2013, 08:07:46 PM »

Ok thanks, everything's running smoothly now but I started moving everything into init() render() and exit() functions. init() is called once on object creation and contains all of the code before glDrawRangeElements(), which is the only code in the render loop. exit() is called when objects are destroyed and contains everything after glDrawRangeElements(). The result of this is that it only draws one cube, if I create multiple cubes while everything was in the render loop it worked fine but as soon as I moved it, it only draws one.
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic