Whats with with my VBO code?

I agree this is pretty boiled down but this is where something I believe is a miss. In here I have three calls. One to build a VBO object, one to render the VBO object and one to render using Vertex Arrays. The application calls either RenderVBO() or RenderArrays() based on the state of a key passing the same arguments to each method, BuildVBO is always called.

The RenderArrays renders properly with geometry, lighting and texels using the activated shader. The RenderVBO call renders the geometry and lighting correctly but the texels are random. See attached screen shots. Not sure whats wrong. Do I need to pass the buffer offsets along somewhere for texels? Why would lighting work if that’s the case?

For anyone who may ask the code branch is…

if (blnUseVBO) {
VBO.Instance.RenderVBO(objModel.arySegments[0], a_vVertex, a_vNormal, a_vTexel);
} else {
VBO.Instance.RenderArrays(objModel.arySegments[0], a_vVertex, a_vNormal, a_vTexel);
}

BTW…This is some C# code using OpenTK and TAO.Compatibility in a Windows form for rapid development.

Any insight is greatly appreciated.


using System;

using Tao.OpenGl;

using Engine;
using Engine.DataTypes;
using Engine.MathLib;

namespace VBOShaders
{
  class VBO : Singleton<VBO>
  {
    public void BuildVBO(Segment objSegment)
    {
      // Calculate sizes
      int tri_buf_size = objSegment.intTriangleCount * Vector3s.SizeInBytes;  // Size of triangle array in bytes
      int vrt_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         vertex
      int nrm_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         normal
      int txl_buf_size = objSegment.intVertexCount   * Vector2f.SizeInBytes;  //         texel
      int geo_buf_size = vrt_buf_size + nrm_buf_size + txl_buf_size;          // all geometry arrays in bytes
      int offset = 0;                                                          // current buffer starting location

      // Setup geometry array and allocate space
      Gl.glGenBuffers(1, out objSegment.geometryVBOId);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.geometryVBOId);
      Gl.glBufferData(Gl.GL_ARRAY_BUFFER, (IntPtr)geo_buf_size, IntPtr.Zero, Gl.GL_STATIC_DRAW);
      
      // Copy geometry data to the hardware buffer starting at offset
      Gl.glBufferSubData(Gl.GL_ARRAY_BUFFER, (IntPtr)offset, (IntPtr)vrt_buf_size, objSegment.aryVertexes);
      offset += vrt_buf_size;
      Gl.glBufferSubData(Gl.GL_ARRAY_BUFFER, (IntPtr)offset, (IntPtr)nrm_buf_size, objSegment.aryVNormals);
      offset += nrm_buf_size;
      Gl.glBufferSubData(Gl.GL_ARRAY_BUFFER, (IntPtr)offset, (IntPtr)txl_buf_size, objSegment.aryVertexes);

      // Setup indices array and allocate space
      Gl.glGenBuffers(1, out objSegment.indicesVBOId);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, objSegment.indicesVBOId);
      Gl.glBufferData(Gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)tri_buf_size, IntPtr.Zero, Gl.GL_STATIC_DRAW);
      
      // Copy index data to the hardware buffer starting at offset 0
      Gl.glBufferSubData(Gl.GL_ELEMENT_ARRAY_BUFFER, IntPtr.Zero, (IntPtr)tri_buf_size, objSegment.aryTriangles);

      // Unbind any buffers so remaining GL stays happy
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void RenderVBO(Segment objSegment, int a_vVertex, int a_vNormal, int a_vTexel)
    {
      // Step 1 
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.geometryVBOId);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, objSegment.indicesVBOId);

      // Step 2 
      Gl.glEnableVertexAttribArray(a_vVertex);
      Gl.glVertexAttribPointer(a_vVertex, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, IntPtr.Zero);
      Gl.glEnableVertexAttribArray(a_vNormal);
      Gl.glVertexAttribPointer(a_vNormal, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, IntPtr.Zero);
      Gl.glEnableVertexAttribArray(a_vTexel);
      Gl.glVertexAttribPointer(a_vTexel, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, IntPtr.Zero);

      // Step 3 
      int tcount = objSegment.intTriangleCount * 3;
      Gl.glDrawElements(Gl.GL_TRIANGLES, tcount, Gl.GL_UNSIGNED_SHORT, IntPtr.Zero);

      // Step 4 
      Gl.glDisableVertexAttribArray(a_vVertex);
      Gl.glDisableVertexAttribArray(a_vNormal);
      Gl.glDisableVertexAttribArray(a_vTexel);

      // Step 5 
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void RenderArrays(Segment objSegment, int a_vVertex, int a_vNormal, int a_vTexel)
    {
      // Step 2 
      Gl.glEnableVertexAttribArray(a_vVertex);
      Gl.glVertexAttribPointer(a_vVertex, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVertexes);
      Gl.glEnableVertexAttribArray(a_vNormal);
      Gl.glVertexAttribPointer(a_vNormal, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVNormals);
      Gl.glEnableVertexAttribArray(a_vTexel);
      Gl.glVertexAttribPointer(a_vTexel, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVTexels);

      // Step 3 
      int tcount = objSegment.intTriangleCount * 3;
      Gl.glDrawElements(Gl.GL_TRIANGLES, tcount, Gl.GL_UNSIGNED_SHORT, objSegment.aryTriangles);

      // Step 4 
      Gl.glDisableVertexAttribArray(a_vVertex);
      Gl.glDisableVertexAttribArray(a_vNormal);
      Gl.glDisableVertexAttribArray(a_vTexel);
    }
  }
}

The offsets you provide in RenderVBO() should be the same as the offsets you loaded the data to in BuildVBO():

offset =0;
glVertexAttribPointer(..., (IntPtr)offset);
offset+=vrt_buf_size;
glVertexAttribPointer(..., (IntPtr)offset);
offset+=nrm_buf_size;
glVertexAttribPointer(..., (IntPtr)offset);

You know I actually tried this prior to posting but with no change. I am actually tracking the offsets in the structure with the VBO Ids. I added logic as suggested and no change.

See below.

What gets me though is the normal appear to be correct. My diffuse and specular lighting is correct. I only see this affect when adding texture coordinates. I still need to add tangents and the like so I can only suspect the problem to get worse.

I’m beginning to suspect the TAO framework/driver implementation. I may need to port to C++ and see if its a .Net/Managed issue or an actual problem with the logic. It’s either something really simple that I am overlooking or in an area I cannot address.

Anyone have an thoughts?


using System;

using Tao.OpenGl;

using Engine;
using Engine.DataTypes;
using Engine.MathLib;

namespace VBOShaders
{
  class VBO : Singleton<VBO>
  {
    public void BuildVBO(Segment objSegment)
    {
      // Calculate sizes
      int tri_buf_size = objSegment.intTriangleCount * Vector3s.SizeInBytes;  // Size of triangle array in bytes
      int vrt_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         vertex
      int nrm_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         normal
      int txl_buf_size = objSegment.intVertexCount   * Vector2f.SizeInBytes;  //         texel
      int geo_buf_size = vrt_buf_size + nrm_buf_size + txl_buf_size;          // all geometry arrays in bytes
      int offset = 0;                                                          // current buffer starting location

      // Setup geometry array and allocate space
      Gl.glGenBuffers(1, out objSegment.geometryVBOId);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.geometryVBOId);
      Gl.glBufferData(Gl.GL_ARRAY_BUFFER, (IntPtr)geo_buf_size, IntPtr.Zero, Gl.GL_STATIC_DRAW);
      
      // Copy geometry data to the hardware buffer starting at offset
      Gl.glBufferSubData(Gl.GL_ARRAY_BUFFER, (IntPtr)offset, (IntPtr)vrt_buf_size, objSegment.aryVertexes);
      offset += vrt_buf_size;
      Gl.glBufferSubData(Gl.GL_ARRAY_BUFFER, (IntPtr)offset, (IntPtr)nrm_buf_size, objSegment.aryVNormals);
      offset += nrm_buf_size;
      Gl.glBufferSubData(Gl.GL_ARRAY_BUFFER, (IntPtr)offset, (IntPtr)txl_buf_size, objSegment.aryVertexes);

      // Setup indices array and allocate space
      Gl.glGenBuffers(1, out objSegment.indicesVBOId);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, objSegment.indicesVBOId);
      Gl.glBufferData(Gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)tri_buf_size, IntPtr.Zero, Gl.GL_STATIC_DRAW);
      
      // Copy index data to the hardware buffer starting at offset 0
      Gl.glBufferSubData(Gl.GL_ELEMENT_ARRAY_BUFFER, IntPtr.Zero, (IntPtr)tri_buf_size, objSegment.aryTriangles);

      // Unbind any buffers so remaining GL stays happy
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void RenderVBO(Segment objSegment, int a_vVertex, int a_vNormal, int a_vTexel)
    {
      int vrt_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         vertex
      int nrm_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         normal
      int offset = 0;                                                          // current buffer starting location

      // Step 1 
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.geometryVBOId);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, objSegment.indicesVBOId);

      // Step 2 
      Gl.glEnableVertexAttribArray(a_vVertex);
      Gl.glVertexAttribPointer(a_vVertex, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, (IntPtr)offset);
      offset += vrt_buf_size;
      Gl.glEnableVertexAttribArray(a_vNormal);
      Gl.glVertexAttribPointer(a_vNormal, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, (IntPtr)offset);
      offset += nrm_buf_size;
      Gl.glEnableVertexAttribArray(a_vTexel);
      Gl.glVertexAttribPointer(a_vTexel,  2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, (IntPtr)offset);

      // Step 3 
      int tcount = objSegment.intTriangleCount * 3;
      Gl.glDrawElements(Gl.GL_TRIANGLES, tcount, Gl.GL_UNSIGNED_SHORT, IntPtr.Zero);

      // Step 4 
      Gl.glDisableVertexAttribArray(a_vVertex);
      Gl.glDisableVertexAttribArray(a_vNormal);
      Gl.glDisableVertexAttribArray(a_vTexel);

      // Step 5 
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void RenderArrays(Segment objSegment, int a_vVertex, int a_vNormal, int a_vTexel)
    {
      // Step 2 
      Gl.glEnableVertexAttribArray(a_vVertex);
      Gl.glVertexAttribPointer(a_vVertex, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVertexes);
      Gl.glEnableVertexAttribArray(a_vNormal);
      Gl.glVertexAttribPointer(a_vNormal, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVNormals);
      Gl.glEnableVertexAttribArray(a_vTexel);
      Gl.glVertexAttribPointer(a_vTexel, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVTexels);

      // Step 3 
      int tcount = objSegment.intTriangleCount * 3;
      Gl.glDrawElements(Gl.GL_TRIANGLES, tcount, Gl.GL_UNSIGNED_SHORT, objSegment.aryTriangles);

      // Step 4 
      Gl.glDisableVertexAttribArray(a_vVertex);
      Gl.glDisableVertexAttribArray(a_vNormal);
      Gl.glDisableVertexAttribArray(a_vTexel);
    }
  }
}

Solved…Kinda…

OK…Found another post where new buffers were created for each array of information. So…created a new VBO buffer ID for vertex, normal and texel data. Buffered the data during the initial glBufferData call. During render I bind each buffer just prior to glVertexAttribPointer but not sure if order here really matter, need to test that. But it works.

Problem with glBufferSubData ??? Not sure. Was I doing something wrong?

Anyway, any disadvantage to creating a new VBO buffer ID for each array? For many VBOs to create it would mean many handles. Any concern with overloading GL if too many handles are created?


using System;

using Tao.OpenGl;

using Engine;
using Engine.DataTypes;
using Engine.MathLib;

namespace VBOShaders
{
  class VBO : Singleton<VBO>
  {
    public void BuildVBO(Segment objSegment)
    {
      // Calculate sizes
      int tri_buf_size = objSegment.intTriangleCount * Vector3s.SizeInBytes;  // Size of triangle array in bytes
      int vrt_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         vertex
      int nrm_buf_size = objSegment.intVertexCount   * Vector3f.SizeInBytes;  //         normal
      int txl_buf_size = objSegment.intVertexCount   * Vector2f.SizeInBytes;  //         texel

      // Setup geometry array and allocate space
      Gl.glGenBuffers(1, out objSegment.vertexVBOId);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.vertexVBOId);
      Gl.glBufferData(Gl.GL_ARRAY_BUFFER, (IntPtr)vrt_buf_size, objSegment.aryVertexes, Gl.GL_STATIC_DRAW);

      Gl.glGenBuffers(1, out objSegment.normalVBOId);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.normalVBOId);
      Gl.glBufferData(Gl.GL_ARRAY_BUFFER, (IntPtr)vrt_buf_size, objSegment.aryVNormals, Gl.GL_STATIC_DRAW);

      Gl.glGenBuffers(1, out objSegment.texelVBOId);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.texelVBOId);
      Gl.glBufferData(Gl.GL_ARRAY_BUFFER, (IntPtr)vrt_buf_size, objSegment.aryVTexels, Gl.GL_STATIC_DRAW);

      // Setup indices array and allocate space
      Gl.glGenBuffers(1, out objSegment.indicesVBOId);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, objSegment.indicesVBOId);
      Gl.glBufferData(Gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)tri_buf_size, objSegment.aryTriangles, Gl.GL_STATIC_DRAW);
      
      // Unbind any buffers so remaining GL stays happy
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void RenderVBO(Segment objSegment, int a_vVertex, int a_vNormal, int a_vTexel)
    {
      // Step 1 
      Gl.glEnableVertexAttribArray(a_vVertex);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.vertexVBOId);
      Gl.glVertexAttribPointer(a_vVertex, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, IntPtr.Zero);

      Gl.glEnableVertexAttribArray(a_vNormal);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.normalVBOId);
      Gl.glVertexAttribPointer(a_vNormal, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, IntPtr.Zero);

      Gl.glEnableVertexAttribArray(a_vTexel);
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, objSegment.texelVBOId);
      Gl.glVertexAttribPointer(a_vTexel,  2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, IntPtr.Zero);

      // Step 2 
      int tcount = objSegment.intTriangleCount * 3;
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, objSegment.indicesVBOId);
      Gl.glDrawElements(Gl.GL_TRIANGLES, tcount, Gl.GL_UNSIGNED_SHORT, IntPtr.Zero);

      // Step 3 
      Gl.glDisableVertexAttribArray(a_vVertex);
      Gl.glDisableVertexAttribArray(a_vNormal);
      Gl.glDisableVertexAttribArray(a_vTexel);

      // Step 4 
      Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
      Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void RenderArrays(Segment objSegment, int a_vVertex, int a_vNormal, int a_vTexel)
    {
      // Step 1 
      Gl.glEnableVertexAttribArray(a_vVertex);
      Gl.glVertexAttribPointer(a_vVertex, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVertexes);

      Gl.glEnableVertexAttribArray(a_vNormal);
      Gl.glVertexAttribPointer(a_vNormal, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVNormals);

      Gl.glEnableVertexAttribArray(a_vTexel);
      Gl.glVertexAttribPointer(a_vTexel, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, objSegment.aryVTexels);

      // Step 2 
      int tcount = objSegment.intTriangleCount * 3;
      Gl.glDrawElements(Gl.GL_TRIANGLES, tcount, Gl.GL_UNSIGNED_SHORT, objSegment.aryTriangles);

      // Step 3 
      Gl.glDisableVertexAttribArray(a_vVertex);
      Gl.glDisableVertexAttribArray(a_vNormal);
      Gl.glDisableVertexAttribArray(a_vTexel);
    }
  }
}