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.
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 vec4
s, 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.