In my pursuit to write code that matches todays OpenGL standards I have found that I am completely clueless about interleaving arrays. I've tried and debugged just about everywhere I can think of but I can't get my model to render using interleaved arrays (It worked when it was configuered to use multiple arrays) Now I know that all the data is properly being parsed from an obj file and information is being copied properly copied into the Vertex object array, but I still can't seem to get anything to render. Below is the code for initializing a model and drawing it (along with the Vertex struct for reference.)


struct Vertex {
    glm::vec3 position;
    glm::vec3 normal;
    glm::vec2 uv;
    glm::vec3 tangent;
    glm::vec3 bitangent;

Model Constructor:

Model::Model(const char* filename) {
    bool result = loadObj(filename, vertices, indices);

    glGenVertexArrays(1, &vertexArrayID);

    glGenBuffers(1, &vertexbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);

    glGenBuffers(1, &elementbuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);

Draw Model:

Model::Draw(ICamera camera) {
    GLuint matrixID = glGetUniformLocation(programID, "mvp");
    GLuint positionID = glGetAttribLocation(programID, "position_modelspace");
    GLuint uvID = glGetAttribLocation(programID, "uv");
    GLuint normalID = glGetAttribLocation(programID, "normal_modelspace");
    GLuint tangentID = glGetAttribLocation(programID, "tangent_modelspace");
    GLuint bitangentID = glGetAttribLocation(programID, "bitangent_modelspace");

    glm::mat4 projection = camera->GetProjectionMatrix(); 
    glm::mat4 view = camera->GetViewMatrix();
    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 mvp = projection * view * model;

    glUniformMatrix4fv(matrixID, 1, GL_FALSE, &mvp[0][0]);


    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glVertexAttribPointer(positionID, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].position);

    glVertexAttribPointer(uvID, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].uv);

    glVertexAttribPointer(normalID, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].normal);

    glVertexAttribPointer(tangentID, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].tangent);

    glVertexAttribPointer(bitangentID, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].bitangent);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, (void*)0);

  • \$\begingroup\$ Why are you trying to interleave arrays? You shouldn't complicate your code unless you have a clear reason for it. I assume you're doing it for some potential speed improvements; you should only do things like this if you can benchmark that it actually speed things up.. In fact, in certain environments, interleaved arrays actually make things slower. \$\endgroup\$ – Jari Komppa Oct 12 '12 at 17:17
  • 1
    \$\begingroup\$ @JariKomppa well I can't exactly bench mark this because it is not working to begin with. I realize this can be inefficient in some cases, but the goal is just to get it working for now and worry about optimization later. \$\endgroup\$ – Benjamin Danger Johnson Oct 12 '12 at 17:19
  • \$\begingroup\$ Did you have it working without interleaving first? \$\endgroup\$ – Jari Komppa Oct 12 '12 at 17:21
  • \$\begingroup\$ Have you tried gDEBugger? It's fairly nice for debugging openGL issues. \$\endgroup\$ – MichaelHouse Oct 12 '12 at 17:21
  • \$\begingroup\$ @JariKomppa yep, I mentioned in the question that it was working fine with multiarrays (aside from a small lighting issue) but when I modified the code to what I think would work for interleaving, I'm coming up blank. The only modifications are to the code shown. \$\endgroup\$ – Benjamin Danger Johnson Oct 12 '12 at 17:23

I think you want to change your attribute pointers, specifically the offsets you have listed. &vertices[0].position should be 0, &vertices[0].normal should be 12, &vertices[0].uv should be 24, and so on.

Since getting the address of a struct member gives you the absolute address instead of the address relative to the beginning of the struct. glVertexAttribPointer is looking for the offset into your interleaved set.

struct Vertex {          //previousfloats * sizeof(float) = offset
    glm::vec3 position;  // 0 * 4 = 0
    glm::vec3 normal;    // 3 * 4 = 12
    glm::vec2 uv;        // 6 * 4 = 24
    glm::vec3 tangent;   // 8 * 4 = 32
    glm::vec3 bitangent; // 11 * 4 = 44

See the documentation for the glVertexAttribPointer here. The big hint being:


Specifies a pointer to the first component of the first generic vertex attribute in the array. The initial value is 0.

  • \$\begingroup\$ Ah, well done, I feel a little dumb about the error being something that small (although slightly proud for just guessing at what the code should be and coming that close.) My hat is off to you and your amazing debugging skills sir. \$\endgroup\$ – Benjamin Danger Johnson Oct 12 '12 at 17:44
  • \$\begingroup\$ No problem, did it actually solve the problem? Pretty nice if that was the single little thing that was wrong. \$\endgroup\$ – MichaelHouse Oct 12 '12 at 17:47
  • \$\begingroup\$ That was the exact issue. I replaced the actually (void*)int pointers with just offsetof(...) to make it a bit simpler to read, but that was my exact problem. Thanks again for your help. \$\endgroup\$ – Benjamin Danger Johnson Oct 12 '12 at 17:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.