Woodfighter
Woodfighter

Reputation: 45

GLM mat4x4 to layout qualifier

I am currently learning OpenGL for a hobby project and I reached the point where I want to do some particle effects with instancing.

To give each of my particles its own transformation, I am building a vector with transformation matrices and use the values for the buffer:

    glBindBuffer(GL_ARRAY_BUFFER, particleEffect->getTransformationBufferID());
    std::vector<glm::mat4x4> particleTransformations;
    const std::vector<Particle>& particles = particleEffect->getParticles();
    for(std::vector<Particle>::const_iterator particleIt = particles.cbegin();
        particleIt != particles.cend();
        ++ particleIt)
    {
        particleTransformations.push_back(particleIt->getTransformation());
    }
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(particleTransformations[0]) * particles.size(),  particleTransformations.data());
    glEnableVertexAttribArray(4);
    glVertexAttribPointer(4, 16, GL_FLOAT, GL_FALSE, 0, 0);

I am initializing my transformation buffer this way:

        glGenBuffers(1, &transformationBufferID_);
        glBindBuffer(GL_ARRAY_BUFFER, transformationBufferID_);
        glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4x4) * particles_.size(), nullptr, GL_STREAM_DRAW);

And I use this simple draw code:

    glVertexAttribDivisor(0, 0);
    glVertexAttribDivisor(1, 0);
    glVertexAttribDivisor(2, 0);
    glVertexAttribDivisor(3, 0);
    glVertexAttribDivisor(4, 1); //(4, 1), because each particle has its own transformation matrix at (layout location = 4)?!
    glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, particleEffect->getVertices().size(), particleEffect->getParticles().size());//getVertices() are the same vertices for each particle
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glDisableVertexAttribArray(3);
    glDisableVertexAttribArray(4);

My vertex shader header looks like this:

    #version 430 core
    layout(location = 0) in vec3 vertexPosition;
    layout(location = 1) in vec3 normals;
    layout(location = 2) in vec4 colors;
    layout(location = 3) in vec2 uvCoordinates;
    layout(location = 4) in mat4 transformation;

This leads to a crash with the glError 1281, but only if I use glEnableVertexAttribArray(4). Otherwise this doesn't crash. I am also able to draw my simple particle mesh when I use an identity matrix instead of my transformation matrix.

I used glGetAttribLocation(programID, "transformation") to find out if the location is correct. Yep, it is correct.

Upvotes: 2

Views: 2063

Answers (1)

Andon M. Coleman
Andon M. Coleman

Reputation: 43319

Your problem is not actually the layout qualifier at all, you need to understand some nuances of vertex attributes.

On GPUs, every vertex attribute is a 4-component vector, whether you declare it float, vec2, vec3 or vec4. That works fine for all of the types I just mentioned, the unused parts of that 4-component vector are assigned 0,0,1 respectively.

For mat4, however, the story is very different. A mat4 is effectively an array of four vec4s, and this means that layout(location = 4) in mat4 transformation; actually occupies 4 different locations (all sequential).

transformation is composed of four 4-component vectors assigned to locations 4, 5, 6 and 7. glVertexAttribPointer(4, 16, GL_FLOAT, GL_FALSE, 0, 0); is wrong, and this needs to be split into multiple calls like so:

// Setup 4 different vertex attributes (one for each column of your matrix).

// Each matrix has a stride of 64-bytes and each column of the matrix advances 16-bytes
glVertexAttribPointer (4, 4, GL_FLOAT, GL_FALSE, 64, 0);
glVertexAttribPointer (5, 4, GL_FLOAT, GL_FALSE, 64, 16);
glVertexAttribPointer (6, 4, GL_FLOAT, GL_FALSE, 64, 32);
glVertexAttribPointer (7, 4, GL_FLOAT, GL_FALSE, 64, 48);

You will also need to enable, disable and setup a vertex attribute divisor for all four of those attribute locations to make your code work correctly.

Upvotes: 6

Related Questions