Reputation: 45
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
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 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.
Upvotes: 6