Hecate's picture

A simple working VAO

OK. I got my VBO working, printed out cubed and grids i made, everything worked fine. Expect i always had to rebind VBO / EBO ( IBO ) and redo the call to either InterleavedArrayFormat or VertexPointer / ColorPointer. I heard that InterleavedArrayFormat was outdated and therefor went with the pointers, but also heard one more thing: VAO. The solution to my extra-call to the ***Pointer functions. So far everything went perfectly fine.

Generating a VAO, binding the VBO/EBO and doing the ***Pointer calls once, then only having to load the VAO and call DrawElements - or so i thought.
It didnt work, and after some trial and error debugging ( which is a real pain, but the manual is kind of scarce on the VAO subject and the only simple example that implements a VAO is the openGL 3.0 demo ) i found out that if i replace this:
GL.ColorPointer(4, ColorPointerType.UnsignedByte, BlittableValueType.StrideOf(VertexList), IntPtr.Zero);
GL.VertexPointer(3, VertexPointerType.Float, BlittableValueType.StrideOf(VertexList), new IntPtr(sizeof(uint)));
with the thing that i explicitly didnt use because i hear of it to be outdated as said:
GL.InterleavedArrays(InterleavedArrayFormat.C4ubV3f, 0, IntPtr.Zero);
it works.

The manual doesnt have much on this, the reference didnt help at all and from a few searches here i didnt find anything on this topic either. I noticed in the openGL 3.0 example EnableVertexAttribArray and VertexAttribPointer are used, but couldnt find any explanation about those two. Copying it into my program with the ( in my eyes correct ) parameters didnt fix anything either.

You can probably guess my question:
Can someone give me an if possible detailed explanation or a link to a tutorial / reference with a lot of explanation about this, maybe a working code snipped about a VAO without InterleavedArrayFormat and some tips on the general subject?

I have read the part in the manual and the http://www.opengl.org/wiki/Vertex_Array_Objects link.

Some notes:
Windows XP professional 32bit
MSVS 2008 professional
In my view unimportant info, but i'll attach it just in case:
2 nvidia 6600GT in SLI
Toledo AMD Athlon X2 64 4800+

The GenVertexArrays gave an EntryPointNotFound exception before, i updated my graphics driver today and now there is no exception anymore. Since it works with InterleavedArrayFormat, this probably is not the cause of the problem any longer.

Thanks in advance,
Hecate


Comments

Comment viewing options

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

In the following thread we elaborated a little about VAOs. Read it through though cause it has some errors at the beginning. It also contains some useful links to opengl forum on this topic.
http://www.opentk.com/node/1619
If you find that it suits what you want to do and you are still stuck I can post some code.

Hecate's picture

Thanks for the reply. I had already found that thread, it was interesting but mainly described the things from http://www.opengl.org/wiki/Vertex_Array_Objects which i already read. I described what works and what doesnt, my problem is why the way of manually settings things results in an empty screen.
Not even inserting

GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(0, 4, VertexAttribPointerType.UnsignedByte, true, BlittableValueType.StrideOf(VertexList), IntPtr.Zero);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, true, BlittableValueType.StrideOf(VertexList), new IntPtr(sizeof(uint)));

instead of the InterleavedArrayFormat works. An example might really be helpful. This is not about how VAO theoretically works, but rather about the practical "put this there and it blits your VBO" and how to use VertexAttribPointer / ***Pointer instead of the InterleavedArrayFormat.

edit:
I found another interesting thing. In the openGL3.0 example a part of the code looks like this:

// Create vertex buffer
            GL.GenVertexArrays(1, out vaoHandle);
            GL.BindVertexArray(vaoHandle);
 
            GL.GenBuffers(1, out positionVboHandle);
            GL.BindBuffer(BufferTarget.ArrayBuffer, positionVboHandle);
            GL.BufferData<Vector3>(BufferTarget.ArrayBuffer,
                new IntPtr(positionVboData.Length * Vector3.SizeInBytes),
                positionVboData, BufferUsageHint.StaticDraw);
 
            GL.GenBuffers(1, out normalVboHandle);
            GL.BindBuffer(BufferTarget.ArrayBuffer, normalVboHandle);
            GL.BufferData<Vector3>(BufferTarget.ArrayBuffer,
                new IntPtr(positionVboData.Length * Vector3.SizeInBytes),
                positionVboData, BufferUsageHint.StaticDraw);
 
            GL.EnableVertexAttribArray(0);
            GL.EnableVertexAttribArray(1);
 
            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, true, Vector3.SizeInBytes, 0);
            GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, true, Vector3.SizeInBytes, 0);

What sense does it make to generate a buffer, bind it, fill it with data and then generate another and bind that one and never bind the first one ever again? VertexAttribPointer is called twice but the buffer is not changed in between. I have a hard time understanding this without any explanation whatsoever. I always thought a buffer has no effect if it is not bound by GL.BindBuffer, somehow this seems to prove otherwise though.
Note that this might be an openGL related problem rather than openTK, but oh well.

PS: i just tried commenting out the entire first section. It had no effect on the program, as i would have expected. I am already so confused that i dont know what to believe where and why anymore...

the Fiddler's picture

You are correct, the 3.0 sample is buggy. I have fixed it in SVN to bind the correct VBOs when creating the VAO (it used to work by chance before and only because position and normals shared the same data). I have also modified it to store indicesVboData into a VBO as it should.

You can view the updated code online or download the code from SVN.

PS: you can use <code></code> tags to mark source code in your post (select the code with your mouse and click the C# button above the text control). Makes posts much easier to read.

Hecate's picture

Thanks for updating the example code. I also didnt know about code tags, sorry.

So far for me it look like VAO will not work with GL.VertexPointer nor GL.ColorPointer as they do not set the VAO's state. GL.InterleavedArrayFormat seems to set it however, making it work. Of course i cannot be certain since i dont see whats actually happening within the VAO.

Since i was told that InterleavedArrayFormat is outdated that kind of bugs me, seems my options are either to still use that function or to just skip to shaders without previous tests since GL.VertexAttribPointer will work then.

the Fiddler's picture
Quote:

So far for me it look like VAO will not work with GL.VertexPointer nor GL.ColorPointer as they do not set the VAO's state.

This sounds like a driver bug. From the vertex_array_object spec:

Quote:

Queries for VERTEX_ARRAY_POINTER, NORMAL_ARRAY_POINTER,
COLOR_ARRAY_POINTER, SECONDARY_COLOR_ARRAY_POINTER, INDEX_ARRAY_POINTER,
TEXTURE_COORD_ARRAY_POINTER, FOG_COORD_ARRAY_POINTER, or
EDGE_FLAG_ARRAY_POINTER return the value stored in the currently bound
vertex array object.

Which implies that all those are part of the VAO state.

Suggestion: make sure that a non-zero VBO id is bound when calling any ***Pointer method (because client-side buffers won't work with VAOs) and try calling GL.GetError() to see if anything comes up. Running your project with the debug version of OpenTK might also help, as it calls GL.GetError() automatically after each and every function call and converts its result into an exception.

Hecate's picture

The generation of the buffers:

GL.GenVertexArrays(1, out VAOName);
GL.GenBuffers(1, out VBOName);
GL.GenBuffers(1, out IBOName);
 
if (GL.GetError() != ErrorCode.NoError)
	throw new Exception("Could not generate Buffers");

Here is the code that loads the VBO:

private void LoadIntoGraphicsMemory(VertexC4ubV3f[] VertexList, ushort[] ElementsList)
{
	int size = 0;
	GL.BindVertexArray(VAOName);
 
	GL.BindBuffer(BufferTarget.ArrayBuffer, VBOName);
	GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(VertexList.Length * BlittableValueType.StrideOf(VertexList)), VertexList, BufferUsageHint.StaticDraw);
	GL.GetBufferParameter(BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out size);
	if (size != VertexList.Length * BlittableValueType.StrideOf(VertexList))
		throw new Exception("Vertex data not uploaded correctly");
 
	GL.BindBuffer(BufferTarget.ElementArrayBuffer, IBOName);
	GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(ElementsList.Length * sizeof(ushort)), ElementsList, BufferUsageHint.StaticDraw);
	GL.GetBufferParameter(BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out size);
	if (size != ElementsList.Length * sizeof(ushort))
		throw new Exception("Element data not uploaded correctly");
 
	GL.ColorPointer(4, ColorPointerType.UnsignedByte, BlittableValueType.StrideOf(VertexList), IntPtr.Zero);
	GL.VertexPointer(3, VertexPointerType.Float, BlittableValueType.StrideOf(VertexList), new IntPtr(sizeof(uint)));
	//Put that in right now, doesnt give any exception...
	if (GL.GetError() != ErrorCode.NoError)
		throw new Exception("ZOMGWTFBBQ");
 
	//Was told that InterleavedArray is outdated and to use gl*Pointer instead
	//GL.InterleavedArrays(InterleavedArrayFormat.C4ubV3f, 0, IntPtr.Zero);
}

And finally the blitting function:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref CameraMatrix);
 
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
GL.BindVertexArray(FloorGridVBO.VAOName);
GL.DrawElements(BeginMode.Lines, GridFloorVBO.NumberOfElements, DrawElementsType.UnsignedShort, IntPtr.Zero);

Note that if i EITHER:
- comment out the two VAO binds
- Exchange the gl*Pointer with the commented GL.InterleavedArrays
it will work and blit my object just fine.

EDIT:
If it really is an absurd error - Is there any place with a VAO example that uses gl*Pointers? I could load that and if it works search for the difference or if it doesnt check for another graphics driver.
As noted in my first post, my previous graphics driver gave an exception, probably too old, so i installed the newest i could find just yesterday - which made it compile but not show anything with VAO enabled but ( still ) works fine without.

the Fiddler's picture

Try using GL.EnableClientState to enable VertexArray and ColorArray prior to rendering. Other than that the code looks fine to me.

Edit: I modified the "Static Vertex Buffer Objects" sample in Examples.exe to use VAOs and it works fine. The code is almost identical to the one you posted - the only difference is that it calls GL.EnableClientState() prior to rendering. If I remove these calls, it renders nothing. Add them back, it renders correctly.

Relevant code:

           // Calls GenBuffers, BindBuffer, BufferData to create the VBO and EBO
           // Identical to your loading code
           vbo[0] = LoadVBO(CubeVertices, CubeElements);
 
            // Set up the VAO
            GL.GenVertexArrays(1, out vbo[0].VaoID);
            GL.BindVertexArray(vbo[0].VaoID);
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[0].VboID);
            GL.VertexPointer(3, VertexPointerType.Float, BlittableValueType.StrideOf(CubeVertices), new IntPtr(0));
            GL.ColorPointer(4, ColorPointerType.UnsignedByte, BlittableValueType.StrideOf(CubeVertices), new IntPtr(12));
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, vbo[0].EboID);
 
            // Render:
            GL.EnableClientState(EnableCap.ColorArray);
            GL.EnableClientState(EnableCap.VertexArray);
 
            GL.BindVertexArray(vbo[0].VaoID);
            GL.DrawElements(BeginMode.Triangles, handle.NumElements, DrawElementsType.UnsignedShort, IntPtr.Zero);

This is an unusual use-case, since it mixes a GL 3.0 feature (VAOs) with a 1.5 feature (fixed-function VBOs). Most VAO tutorials understandably focus on generic VBOs used in shaders (which makes sense as all VAO-capable cards support shaders).

Still, the feature works as expected. My hardware: Nvidia Quadro NVS135M, 197.16 drivers.

Hecate's picture

The problem is solved - and ( even though a bit frustrating it took me so long to figure it out ) luckily nothing but a simple mistake. I forgot the VAO will remember state changes when active and didnt realize it will actually deactivate the EnableCap.VertexArray if i do not explicitly activate it for the VAO. My assumption now is that GL.InterleavedArrays somehow does this and therefor worked - or GL.InterleavedArrays uses some form of newer implementations like generic attributes. Setting the caption in the renderer's constructor wasn't enough, as the VAO never noticed it and upon being bound set it to disabled.

Some explanation would be nice though, which captions does the VAO remember and delete upon binding? Is my assumption about GL.InterleavedArrays correct? If so, why doesnt the reference state this?

Maybe i need to research a bit more here.

Thanks for your patience, even though it took me relatively long i am still relieved it works now. There has a lot of work been piling up during the research, i definately need to read the openGL spec about what functions are deprecated by now. I also need to learn about shaders fast, as it seems the gl*Pointer way i use at the moment is exactly one of those deprecated things.