1

I'm writing a simple scene graph, and I have a mesh class that keeps track of a vertex array object, vertex buffer object and an index buffer object.

When I initialize the mesh objects and populate them with data, it seems that the last created vertex buffer is the one being used when drawing, even when I bind a different vertex array object.

When rendering, if I only bind the vertex array, any meshes other than the last initialized one have the vertices of the last initialized mesh (but the correct indices, making them look wrong).

The only way I got it to work is to bind the vertex buffer object again right after I bind the vertex array object. This contradicts every documentation or comment I read anywhere.

// Geometry class
class Geometry 
{
    public override void Render()
    {
        this.Mesh.bind(); // bind mesh
        this.Material.bind(); // bind shader. This also uses attribute locations stored in the shader object to enable vertex attributes

        this.Data.Render();
        this.Mesh.unbind(); // bind 0 to vertex array

        this.Material.unbind(); // use program 0
    }
}

// base mesh class
class Mesh 
{
    public override void init()
    {
        // ... truncated code for creating vertices ..

        vertexArray = GL.GenVertexArray();
        GL.BindVertexArray(vertexArray);

        GL.GenBuffers(2, buffers);

        GL.BindBuffer(BufferTarget.ArrayBuffer, buffers[0]);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * Vertex.SizeInBytes), vertices, BufferUsageHint.StaticDraw);

        GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffers[1]);
        GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Count * sizeof(uint)), indices.ToArray(), BufferUsageHint.StaticDraw);

        numPoints = vertices.Length;
        numIndices = indices.Count;
        GL.BindVertexArray(0);
    }

    public void bind()
    {
        GL.BindVertexArray(vertexArray);

        // I had to add this line to make meshes render correctly
        GL.BindBuffer(BufferTarget.ArrayBuffer, buffers[0]);
    }

    public void unbind()
    {
        GL.BindVertexArray(0);
    }
    public override void Render()
    {
        GL.DrawElements(PrimitiveType.Triangles, numIndices, DrawElementsType.UnsignedInt, IntPtr.Zero);
    }

    public override void Update(float t, float deltaT, int xDelta, int yDelta, int zDelta, MouseState mouse, KeyboardState keyboard)
    {
    }
}
Elias
  • 175
  • 8

1 Answers1

1

Your mesh initialization method is missing something rather important. Namely, calls to glVertexAttribPointer. This method sets where and how your VAO will fetch vertex data. You create buffers and submit data to them, you never set the attribute bindings for the buffers.

It's the attribute bindings that is saved with VAO state and automatically used when binding a VAO, information on buffers being bound to buffer targets like Element array buffer and array buffer are not saved.

Edit:

You seem to be rebinding attributes every time you render, which is not what you're suppose to do, and it's whats causing you problems.

Bind the attributes once in initialization. Then just bind the VAO and render. Simple.

The reason it worked how it did is because you were setting attribute bindings in the material.bind. If the relevant buffers were not bound when you did that, you would get problems. So, it makes sense that it would only work if your buffer is bound, hence why it works when you add the binding line in mesh.bind, which is the call that happens before. But again, you shouldn't be doing this every render call.

Thomas
  • 6,032
  • 6
  • 41
  • 79
  • Sorry I should have mentioned that I am doing that in Material.bind(), because I was trying to avoid using layout (location = x) in my shaders. So when the shader is bound, it is using the attribute locations stored in it to enable the attributes. Thank you for pointing that out. I updated the code with a comment to clarify that. So if only the attribute bindings are saved in the VAO, does this mean that I have to call BindBuffer every time I bind the VAO as well? All the documentation and tutorials I saw implied I only need to bind the VAO. Did I misunderstand them? – Elias Mar 26 '15 at 21:12
  • @Elias There's something wrong going on involving this still. Your Material.bind() is being called in a render method. You don't set attribute bindings in render calls! They are expensive! – Thomas Mar 26 '15 at 21:15
  • oh I see. I guess I should use hardcoded locations then and do that in the initialization. I'll let you know how that goes. Thank you very much! – Elias Mar 26 '15 at 21:18
  • That solved it. I really need to understand how this VAO really works but I'm not getting it from everything I read lol. Thank you very much. Marked as an answer. – Elias Mar 26 '15 at 21:44
  • @Elias There should be plenty of documentation and tutorials on how VAOs work. There are also lots of answers to previous questions. Here is one of my own older answers that explain VAOs in a fairly generic way: http://stackoverflow.com/a/26229019. – Reto Koradi Mar 27 '15 at 03:04
  • @RetoKoradi thank you. That post really answered my questions. I appreciated it. – Elias Mar 27 '15 at 15:38