remopini's picture

VBO with VB.NET

Hi all

I'm trying to switch from Display Lists to VBOs.

Here's my code:

FLOAT_SIZE = 8
BYTE_SIZE = 1
 
vertices = New Double() {-25.0F, +25.0F, +25.0F, _
                                 +25.0F, +25.0F, +25.0F, _
                                 +25.0F, -25.0F, +25.0F, _
                                 -25.0F, -25.0F, +25.0F, _
                                 -25.0F, +25.0F, -25.0F, _
                                 +25.0F, +25.0F, -25.0F, _
                                 +25.0F, -25.0F, -25.0F, _
                                 -25.0F, -25.0F, -25.0F}
 
        indices = New Byte() {0, 1, 2, 3, _
                              4, 5, 1, 0, _
                              3, 2, 6, 7, _
                              5, 4, 7, 6, _
                              1, 5, 6, 2, _
                              4, 0, 3, 7}
 
GL.GenBuffers(1, vertexbuffer)
GL.BindBuffer(BufferTarget.ArrayBuffer, vertexbuffer)
GL.BufferData(BufferTarget.ArrayBuffer, FLOAT_SIZE * vertices.Count, vertices, BufferUsageHint.StaticDraw)
 
GL.GenBuffers(1, indexbuffer)
GL.BindBuffer(BufferTarget.ArrayBuffer, indexbuffer)
GL.BufferData(BufferTarget.ArrayBuffer, BYTE_SIZE * indices.Count, indices, BufferUsageHint.StaticDraw)

...

 Public Sub Render()
        GL.Clear(ClearBufferMask.ColorBufferBit Or ClearBufferMask.DepthBufferBit)
 
 
        GL.LoadIdentity()
        GL.Rotate(alpha, 0, 0, 1)
 
        'vertex buffer objects
        '---------------------
        GL.EnableClientState(EnableCap.VertexArray)
        GL.BindBuffer(BufferTarget.ArrayBuffer, vertexbuffer)
        GL.VertexPointer(3, VertexPointerType.Double, 0, IntPtr.Zero)
        GL.BindBuffer(BufferTarget.ArrayBuffer, indexbuffer)
        GL.DrawElements(BeginMode.Quads, 24, DrawElementsType.UnsignedByte, IntPtr.Zero)
 
        'old fashioned way
        '-----------------
        'GL.Begin(BeginMode.Quads)
        'For i = 0 To indices.Count - 1
        '    GL.Vertex3(vertices(indices(i) * 3 + 0), vertices(indices(i) * 3 + 1), vertices(indices(i) * 3 + 2))
        'Next
        'GL.End()
 
        alpha += 0.5
 
 
 
        GlControl1.SwapBuffers()
    End Sub

If I use the "old fashioned way", it works fine, however, the VBO way crashes (memory access exception)...what am I missing?

Cheers

Remo


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kvark's picture

1) FLOAT_SIZE * vertices.Count is not correct as long as you are using Double for verteces

2) For the index buffer you have to use BufferTarget.ElementArrayBuffer bind point instead of BufferTarget.ArrayBuffer

the Fiddler's picture

1) FLOAT_SIZE = 8. This should either be DOUBLE_SIZE = 8 or SINGLE_SIZE = 4. Doesn't VB.Net have a SizeOf operator, i.e. SizeOf(Double)?

2) BufferTarget.ElementArrayBuffer is the real issue. The code should work as soon as you change that.

remopini's picture

Ok, the change to BufferTarget.ElementArrayBuffer did the trick.

VB.NET actually DOES have a Sizeof(Double) function, it's hidden in the System.Runtime.InteropServices.Marshal class.

So the "final" initialization parts look like this:

        GL.GenBuffers(1, vertexbuffer)
        GL.BindBuffer(BufferTarget.ArrayBuffer, vertexbuffer)
        GL.BufferData(BufferTarget.ArrayBuffer, SizeOf(GetType(Single)) * vertices.Count, vertices, BufferUsageHint.StaticDraw)
 
        GL.GenBuffers(1, indexbuffer)
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexbuffer)
        GL.BufferData(BufferTarget.ElementArrayBuffer, SizeOf(GetType(Byte)) * indices.Count, indices, BufferUsageHint.StaticDraw)

And the actual Render looks like this:

        GL.EnableClientState(EnableCap.VertexArray)
        GL.BindBuffer(BufferTarget.ArrayBuffer, vertexbuffer)
        GL.VertexPointer(3, VertexPointerType.Float, 0, IntPtr.Zero)
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexbuffer)
        GL.DrawElements(BeginMode.Quads, 24, DrawElementsType.UnsignedByte, IntPtr.Zero)

Thank you for the help :)

Cheers

Remo

PS: The vertex array has been changed to type Single() of course...

remopini's picture

I now added the same logic for texture coordinates:

GL.GenBuffers(1, verbuf)
GL.BindBuffer(BufferTarget.ArrayBuffer, verbuf)
GL.BufferData(BufferTarget.ArrayBuffer, SizeOf(GetType(Single)) * vertices.Count, vertices, BufferUsageHint.StaticDraw)
 
GL.GenBuffers(1, idxbuf)
GL.BindBuffer(BufferTarget.ElementArrayBuffer, idxbuf)
GL.BufferData(BufferTarget.ElementArrayBuffer, SizeOf(GetType(Integer)) * indices.Count, indices, BufferUsageHint.StaticDraw)
 
GL.GenBuffers(1, texbuf)
GL.BindBuffer(BufferTarget.ArrayBuffer, texbuf)
GL.BufferData(BufferTarget.ArrayBuffer, SizeOf(GetType(Single)) * texcoord.Count, texcoord, BufferUsageHint.StaticDraw)

texcoord is an array of Single() containing u1, v1, u2, v2, ...

I then draw all of it like so:

GL.BindBuffer(BufferTarget.ArrayBuffer, verbuf)
GL.VertexPointer(3, VertexPointerType.Float, 0, IntPtr.Zero)
GL.BindBuffer(BufferTarget.ArrayBuffer, texbuf)
GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, IntPtr.Zero)
GL.BindBuffer(BufferTarget.ElementArrayBuffer, idxbuf)
GL.DrawElements(BeginMode.Triangles, indices.Count, DrawElementsType.UnsignedInt, IntPtr.Zero)

From the result, I can see, that the texture coordinates must be wrong, however, when I do it the old fashioned way (Display lists), the coordinates are correct... I guess I'm missing something yet again...

Cheers

Remo

kvark's picture

Did you forget to enable client state for texture coordinates?
GL.EnableClientState(EnableCap.TextureCoordArray)

Inertia's picture

You can only use 1 BufferTarget.ArrayBuffer, so you must format your data in one of those ways:

t[x] = TexCoord
v[x] = Vertex position

1) t0, t1, t2, t3, ... tn, v0, v1, v2, v3, ... vn
2) t0, v0, t1, v1, t2, v2, ..., tn, vn

The GL.***Pointer() command's stride and offset parameters are used to specify the used layout.

You might want to check out the documentation: http://www.opentk.com/doc/chapter/2/opengl/geometry

the Fiddler's picture

I don't think that is strictly necessary. You can keep your data on different arrays - GL.DrawElements() will use all of them, as long as they are enabled and bound (with GL.[Vertex|Normal|TexCoord|*]Pointer).

Inertia's picture

If you can get multiple ArrayBuffers to work at the same time you're my hero, I tried that and it didn't work at all. The second GL.BindBuffer(BufferTarget.ArrayBuffer, ..) call would simply replace the binding done in the first.

kvark's picture

Ha, Inertia, that was pretty naive to think that all vertex attributes have to be interleaved. Actually, interleaving is just an alternative to having separate vertex buffers for separate attributes. In my engine I preferred the second choice for it's simplicity and better modularity: there is no need to manager all buffers at once, you just need to bind some of them sequentially calling gl*Pointer function after each bind.

Check out the following discussion:
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Numb...

Briefly, you just need to have a proper array buffer bound on calling gl*Pointer. After that you can safely call glDraw* even if there's no buffer attached to the BufferTarget.ArrayBuffer at all.

remopini's picture

Actually, KVARK provided the solution:

I was missing a GL.EnableClientState(EnableCap.TextureCoordArray)

This now works using three completely different arrays (vertex, indices, texture coords), so it is obviously possible to use more than one array.

I guess that makes me Inertia's hero :)

Thank you all for helping me out here.

Cheers

Remo