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 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.
Related
Currently I have a system that contains 2 buffers, one created CPU side and set to a buffer. and then one from a ssbo and populated from another shader.
Here is the struct for the ssbo data:
struct ssbo_data_t
{
int maxcount = 1024;
float baz[1024];
} ssbo_data;
Then the vao is build like:
//Build buffer
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, arraysize * sizeof(DATATYPE), dataarr, GL_DYNAMIC_DRAW);
//Build vao
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
//index,size,type,norm,stride,pointerToFirst
glEnableVertexAttribArray(0); //Position of light
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1); //Color
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(DATATYPE) * 2));
glEnableVertexAttribArray(2); //Intensity
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(DATATYPE) * 5));
glEnableVertexAttribArray(3); //Layer
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(DATATYPE) * 6));
//Try to bind ssbo for lighting data.
glBindBuffer(GL_ARRAY_BUFFER, ssbo);
glEnableVertexAttribArray(4); //MaxSize
glVertexAttribPointer(4, 1, GL_INT, GL_FALSE, 0, (void*)offsetof(ssbo_data_t,maxcount));
glEnableVertexAttribArray(5); //Corner Data
glVertexAttribPointer(5, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)offsetof(ssbo_data_t, baz));
My question is that if I set no stride for the one buffer, and then a stride for another part of the buffer it should work right? Or is there something i'm missing?
Cause when I actually preform the draw call, the first line is drawn, but all others have a position of -1,-1 Like the first buffer bound to the VAO was reset.
I am however receiving the correct potions from the other buffer, which shows it is working. Its like the first buffer is being unbound on the next draw call.
Due to how large the project is, I cant really post a working example.
As you can see here, I place the primitive to the left and the other object to the right.
The primitive is drawn, and sets its corner positions to the SSBO.
The second object is then drawn and creates 4 line segments from its position.
The first vertex, works as expected, and creates a line segment from its position and terminates on the corner of the box, specified by the VAO.
The next draws don't read the position correctly. But do take the correct information from the ssbo. So... ?
Might be helpful if I posted the vertex shader:
#version 430 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in float intensity;
layout(location = 3) in float layer;
layout(location = 4) in int maxcount;
layout(location = 5) in vec2 loc;
uniform vec2 screensize;
out VS_OUT{
vec3 color;
float intensity;
vec4 targ;
} vs_out;
void main()
{
vec2 npos = ((aPos - (screensize / 2.0)) / screensize) * 2; //Convert mouse chords to screen chords.
gl_Position = vec4(npos, layer, 1.0);
vs_out.targ = vec4(loc, layer, 1.0);
vs_out.color = aColor;
vs_out.intensity = intensity;
}
To answer your question, yes you can mix and match strides and offsets in the same buffer and share vertex attribute data with an SSBO (Shader Storage Buffer Object).
Vertex stride
I'm not sure how you're trying to use the contents of the SSBO there. However, the binding of vertex attributes #4 and #5 looks fishy.
glVertexAttribPointer(4, 1, GL_INT, GL_FALSE, 0, (void*)offsetof(ssbo_data_t,maxcount));
The first vertex will have maxcount as expected in the x component. However, a stride of 0 means that consecutive vertex attributes are packed. The second vertex will therefore read 32 bits from baz[0] and treat that as an integer. The third will read baz[1] and so on. In short, only the first vertex will have the correct maxcount value and the rest will have bogus values.
To fix this, use instanced arrays (also known as vertex divisor). The function to use is glVertexAttribDivisor(). Another option is to bind your SSBO directly and read it as an SSBO (buffer type in GLSL). See the SSBO OpenGL wiki page for an example.
Finally:
glVertexAttribPointer(5, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*)offsetof(ssbo_data_t, baz));
You can use a stride of 0 here because your attribute values are tightly packed. sizeof(float) * 2 gives the same result.
The (wrong) solution
I wrongly stated that BindVertexArray() resets the current GL_ARRAY_BUFFER binding. This is not the case. It does reset the indexed vertex buffer binding though but you already set those.
I'm attempting to render Suzanne (from Blender) in OpenGL 3.3, but the buffer data doesn't seem to be correct. I get this:
https://gyazo.com/ab82f9acb6854a49fccc527ed96cc4e8
I also tried to render a sphere with a simple texture:
https://gyazo.com/85c1e87fcc4eab128ca37b1a0cb1deaa
My importer inserts the vertex data into a std::vector as single floats:
if(line.substr(0,2) == "v ")
{
/** Vertex position */
std::istringstream s(line.substr(2));
float v[3];
s >> v[0]; s >> v[1]; s >> v[2];
this->vertices.push_back(v[0]);
this->vertices.push_back(v[1]);
this->vertices.push_back(v[2]);
}
I setup the array buffer as follows:
glGenBuffers(1, &this->vbo);
glBindBuffer(GL_ARRAY_BUFFER, this->vbo);
glBufferData(GL_ARRAY_BUFFER,
sizeof(float)*(this->vertices.size()+this->textures.size()+this->normals.size()),
NULL,
GL_STATIC_DRAW);
And then I insert the actual data using glBufferSubData
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*this->vertices.size(), this->vertices.data());
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*this->vertices.size(), sizeof(float)*this->textures.size(), this->textures.data());
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*(this->vertices.size()+this->textures.size()), sizeof(float)*this->normals.size(), this->normals.data());
I also insert the indices in the same way (GL_ELEMENT_ARRAY_BUFFER of course).
I then point to the information:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (GLvoid*)0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (GLvoid*)(sizeof(float)*this->v.size()));
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (GLvoid*)(sizeof(float)*this->v.size()+this->vt.size()));
My vertex shader takes in the data like this:
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;
layout(location = 2) in vec3 normals;
Am I screwing up the offsets?
EDIT:
I figured out the biggest issue! I wrote an external Lua program to convert the obj files to a format that was easier to import, but it ended up messing with the data and duplicating the indices on the "f #/#/# #/#/# #/#/#" so the file looked like this (x->y->x) instead of (x->y->z)
Also fixed a few other errors thanks to the responses below!
Without seeing your shaders I can't be 100% sure if your calls to glVertexAttribPointer are legit. I also can't tell if you want interleaved vertex data in a single VBO or not. What you currently have packs all of the vertex positions in first, then all of the texture coordinates, and finally all of the normals.
To interleave the data you would first want to put all of it into a single array (or vector) so that each vertex repeats the PPPTTNNN pattern. Where PPP are the three position floats, TT are the two texcoord floats, and NNN are the three normal floats.
It would look something like this (using bogus types, values, and spacing to help illustrate the pattern):
float[] vertices = {
/* pX, pY, pZ, tX, tY, nX, nY, nZ */
1, 1, 1, 0, 0, 1, 1, 1, // vertex 1
0, 0, 0, 1, 1, 0, 0, 0, // vertex 2
1, 1, 1, 0, 0, 1, 1, 1, // vertex 3
...
};
Let's say you put it all into a single vector called vertices, then you can upload it with a single command:
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * this->vertices.size(), this->vertices.data(), GL_STATIC_DRAW);
You could also put each attribute into its own VBO. How you decide to store the data on the GPU is ultimately up to you. If you are purposely storing the data the way you have it, let me know in the comments and I'll update the answer.
Ok, now the shader bits.
Let's say you've got a vertex shader that looks like this:
in vec3 position;
in vec2 texcoord;
in vec3 normal;
out vec2 uv;
void main() {
gl_Position = vec4(position, 1);
uv = texcoord;
}
And a fragment shader that looks like this:
in vec2 uv;
uniform sampler2D image;
out vec4 color;
void main() {
color = texture(image, uv);
}
Then you would want the following glVertexAttribPointer calls:
int stride = (3 + 2 + 3) * sizeof(float);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)3);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)5);
Notice how the first parameter to each call is a different number. This corresponds to position, texcoord, normal respectively in the vertex shader.
Also texture coordinates are typically only a pair of floats (e.g. vec2 texcoord) so I changed the second parameter to 2 for the texcoord call.
Finally the last parameter, when using an interleaved array, only needs to specify the offset per vertex. Thus we get 0, 3, and 5 for position offset, texcoord offset, and normal offset respectively.
Hopefully this gets you where you want to be.
Check out the docs.gl page on glVertexAttribPointer for more info.
I am working on a game, and trying to implement the instancized CPU-Particle System programmed on http://www.opengl-tutorial.org/intermediate-tutorials/billboards-particles/particles-instancing/
i managed to get it working in my code structure, but i am trying to draw other objects in the same window, which i can't, i have tested it, and it only allows me to draw one, either draw the particle system or draw the object i want.
The problem happens specifically at this code part :
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Use our shader
glUseProgram(particleprogramID->programHandle);
unit2 +=1;
glActiveTexture(GL_TEXTURE0 + unit2);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(TextureID, unit2);
glm::mat4 ViewMatrix = camera->getViewMatrix();
// Same as the billboards tutorial
glUniform3f(CameraRight_worldspace_ID, ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
glUniform3f(CameraUp_worldspace_ID , ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);
glUniformMatrix4fv(ViewProjMatrixID, 1, GL_FALSE, &mvp[0][0]);
//glUniformMatrix4fv(modviewprojID, 1, GL_FALSE, &mvp[0][0]);
//1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);
// 2nd attribute buffer : positions of particles' centers
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
glVertexAttribPointer(
1,
4,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);
// 3rd attribute buffer : particles' colors
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
glVertexAttribPointer(
2,
4,
GL_UNSIGNED_BYTE,
GL_TRUE,
0,
(void*)0
);
glVertexAttribDivisor(0, 0);
glVertexAttribDivisor(1, 1);
glVertexAttribDivisor(2, 1);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
then i try to draw my star:
unit2 += 1;
starTexture->Bind(unit2);
shaderObject ->useShader();
glUniform1i(glGetUniformLocation(shaderObject->programHandle, "colorTexture"), unit2);
glUniformMatrix4fv(glGetUniformLocation(shaderObject->programHandle, "modelMatrix"), 1, GL_FALSE, glm::value_ptr(star1->getModelMatrix()));
glUniformMatrix4fv(glGetUniformLocation(shaderObject->programHandle, "projectionMatrix"), 1, GL_FALSE, glm::value_ptr(projectionViewMatrix));
star1->draw();
the vertex and fragment shader for the particle system:
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 squareVertices;
layout(location = 1) in vec4 xyzs; // Position of the center of the particule and size of the square
layout(location = 2) in vec4 color; // Position of the center of the particule and size of the square
// Output data ; will be interpolated for each fragment.
out vec2 UV;
out vec4 particlecolor;
// Values that stay constant for the whole mesh.
uniform vec3 CameraRight_worldspace;
uniform vec3 CameraUp_worldspace;
uniform mat4 VP; // Model-View-Projection matrix, but without the Model (the position is in BillboardPos; the orientation depends on the camera)
void main()
{
float particleSize = xyzs.w; // because we encoded it this way.
vec3 particleCenter_wordspace = xyzs.xyz;
vec3 vertexPosition_worldspace =
particleCenter_wordspace
+ CameraRight_worldspace * squareVertices.x * particleSize
+ CameraUp_worldspace * squareVertices.y * particleSize;
// Output position of the vertex
gl_Position = VP * vec4(vertexPosition_worldspace, 1.0f);
// UV of the vertex. No special space for this one.
UV = squareVertices.xy + vec2(0.5, 0.5);
particlecolor = color;
}
frragment shader:
#version 330 core
// Interpolated values from the vertex shaders
in vec2 UV;
in vec4 particlecolor;
// Ouput data
out vec4 color;
uniform sampler2D myTexture;
void main(){
// Output color = color of the texture at the specified UV
color = texture2D( myTexture, UV ) * particlecolor;
}
and it only displays the particle system:
worth mentioning is:
the object i want to draw is a star modelled in blender and is displayed correctly when drawn alone or with other objects other than the particle system. and has its own class having buffers for psitions, UVs, indices and normals...
it seems like the star data are being swallowed by the buffer...
i appreciate every help...
I'm trying to color single vertices of quads that are drawn through glDrawElements, I'm working with cocos2d libray, so I've been able to scavenge the source code to understand exactly what is happening, code is the following:
glBindVertexArray( VAOname_ );
glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
glBindVertexArray(0);
So vertex array objects are used. I'm trying to modify single vertices color of the objects that are passed and it seems to work but with a glitch which is described by the following image:
Here I tried to change the color of the lower left and right vertex. The result is different, I guess this is because the quad is rendered as a couple of triangles with shared hypotenuse which resides on the diagonal which goes from lower left vertex to higher right vertex. So this could cause the different result.
Now I would like to have the second result also for the first case. Is there a way to obtain it?
Your guess is right. The OpenGL driver tesselates your quad into two triangles, in which the vertex colours are interpolated barycentrically, which results in what you see.
The usual approach to solve this, is by performing the interpolation "manually" in a fragment shader, that takes into account the target topology, in your case a quad. Or in short you have to perform barycentric interpolation not based on a triangle but on a quad. You might also want to apply perspective correction.
I don't have ready to read resources at hand right now, but I'll update this answer as soon as I have (might actually mean, I'll have to write it myself).
Update
First we must understand the problem: Most OpenGL implementations break down higher primitives into triangles and render them localized, i.e. without further knowledge about the rest of the primitive, e.g. a quad. So we have to do this ourself.
This is how I'd do it.
#version 330 // vertex shader
Of course we also need the usual uniforms
uniform mat4x4 MV;
uniform mat4x4 P;
First we need the position of the vertex processed by this shader execution instance
layout (location=0) in vec3 pos;
Next we need some vertex attributes which we use to describe the quad itself. This means its corner positions
layout (location=1) in vec3 qp0;
layout (location=2) in vec3 qp1;
layout (location=3) in vec3 qp2;
layout (location=4) in vec3 qp3;
and colors
layout (location=5) in vec3 qc0;
layout (location=6) in vec3 qc1;
layout (location=7) in vec3 qc2;
layout (location=8) in vec3 qc3;
We put those into varyings for the fragment shader to process.
out vec3 position;
out vec3 qpos[4];
out vec3 qcolor[4];
void main()
{
qpos[0] = qp0;
qpos[1] = qp1;
qpos[2] = qp2;
qpos[3] = qp3;
qcolor[0] = qc0;
qcolor[1] = qc1;
qcolor[2] = qc2;
qcolor[3] = qc3;
gl_Position = P * MV * position;
}
In the fragment shader we use this to implement a distance weighting for the color components:
#version 330 // fragment shader
in vec3 position;
in vec3 qpos[4];
in vec3 qcolor[4];
void main()
{
vec3 color = vec3(0);
The following can be simplified combinatorical, but for sake of clarity I write it out:
For each corner point of the vertex mix with the colors of all corner points with the projection of the position on the edge between them as mix factor.
for(int i=0; i < 4; i++) {
vec3 p = position - qpos[i];
for(int j=0; j < 4; j++) {
vec3 edge = qpos[i] - qpos[j];
float edge_length = length(edge);
edge = normalize(edge);
float tau = dot(edge_length, p) / edge_length;
color += mix(qcolor[i], qcolor[j], tau);
}
}
Since we looked at each corner point 4 times, scale down by 1/4
color *= 0.25;
gl_FragColor = color; // and maybe other things.
}
We're almost done. On the client side we need to pass the additional information. Of course we don't want to duplicate data. For this we use glVertexBindingDivisor so that a vertex attribute advances only every 4 vertices (i.e. a quad), on the qp… and qc… locations, i.e. location 1 to 8
typedef float vec3[3];
extern vec3 *quad_position;
extern vec3 *quad_color;
glVertexAttribute(0, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[0]);
glVertexBindingDivisor(1, 4);
glVertexAttribute (1, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[0]);
glVertexBindingDivisor(2, 4);
glVertexAttribute (2, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[1]);
glVertexBindingDivisor(3, 4);
glVertexAttribute (3, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[2]);
glVertexBindingDivisor(4, 4);
glVertexAttribute (4, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[3]);
glVertexBindingDivisor(5, 4);
glVertexAttribute (5, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[0]);
glVertexBindingDivisor(6, 4);
glVertexAttribute (6, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[1]);
glVertexBindingDivisor(7, 4);
glVertexAttribute (7, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[2]);
glVertexBindingDivisor(8, 4);
glVertexAttribute (8, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[3]);
It makes sense to put the above into a Vertex Array Object. Also using a VBO would make sense, but then you must calculate the offset sizes manually; due to the typedef float vec3 the compiler does the math for us ATM.
With all this being set you can finally tesselation independently draw your quad.
I'm currently trying to set up a GPU skinning (with glsl) but it's not working the way I would :) Actually it's not working at all. My mesh disappear when I try this glsl code :
layout(location = 0) in vec3 vertexPos;
layout(location = 1) in vec2 vertexUv;
layout(location = 2) in vec3 vertexNor;
layout(location = 5) in ivec4 joints_influences;
layout(location = 6) in vec4 weights_influences;
uniform mat4 ViewProj, View, Proj, Model;
out vec3 vertexPosEye;
out vec3 vertexNorEye;
const int MAX_INFLUENCES = 4;
const int MAX_BONES = 50;
uniform mat4 animation_matrices[MAX_BONES];
uniform mat4 inv_bind_matrices[MAX_BONES];
void main()
{
vertexPosEye = (View * Model * vec4(vertexPos, 1)).xyz; // Position
vertexNorEye = (View * Model * vec4(vertexNor, 0)).xyz; // Normal matrix
vec4 final_v = vec4(0, 0, 0, 0);
for (int i = 0; i < MAX_INFLUENCES; i++)
{
vec4 v = vec4(vertexPos, 1)
* inv_bind_matrices[joints_influences[i]]
* animation_matrices[joints_influences[i]]
* weights_influences[i];
final_v += v;
}
gl_Position = ViewProj * Model * final_v;
}
when I try this :
gl_Position = ViewProj * Model * vertexPos;
My mesh is back :) but no animations anymore of course...
Here's my application (c++) code when I set VBO attributes :
// Vertex position
glGenBuffers(1, &buffer[0]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
glBufferData(GL_ARRAY_BUFFER, vertices.pos.size() * sizeof(bVector3), &vertices.pos[0], GL_STATIC_DRAW);
// Ibid for uv, normals, tangents and bitangents.
// Skinning : joints index
glGenBuffers(1, &buffer[5]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[5]);
glBufferData(GL_ARRAY_BUFFER, vertices.joints.size() * sizeof(SkinningJoints), &vertices.joints[0], GL_STATIC_DRAW);
// Skinning : weights
glGenBuffers(1, &buffer[6]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[6]);
glBufferData(GL_ARRAY_BUFFER, vertices.weights.size() * sizeof(SkinningWeights), &vertices.weights[0], GL_STATIC_DRAW);
// Indices
glGenBuffers(1, &buffer[7]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[7]);
glBufferData(GL_ARRAY_BUFFER, vertices.indices.size() * sizeof(bUshort), &vertices.indices[0], GL_STATIC_DRAW);
In the main loop :
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m->GetBuffer(0));
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(for uv, normals, tangents and bitangents)...
glEnableVertexAttribArray(5);
glBindBuffer(GL_ARRAY_BUFFER, m->GetBuffer(5));
glVertexAttribPointer(5, 4, GL_INT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(6);
glBindBuffer(GL_ARRAY_BUFFER, m->GetBuffer(6));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->GetBuffer(7));
glDrawElements(GL_TRIANGLES, m->vertices.indices.size(), GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
Here is my RenderingVertices struct (after Barr's recomendations):
struct RenderingVertices
{
// std::vector<Vec3>
vVec3 pos, nor, tang, btan;
vVec2 uv;
vUshort indices;
vector<SkinningJoints> joints;
vector<SkinningWeights> weights;
};
And here is my SkinningJoints struct :
struct SkinningJoints
{
int j[MAX_BONES_PER_VERT];
SkinningJoints(Vertex::Weights weights[MAX_BONES_PER_VERT])
{
for (bUint i = 0; i < MAX_BONES_PER_VERT; i++)
j[i] = weights[i].jid;
}
};
My SkinningWeights struct is almost the same, with an array of float instead of int.
Now when I try to debug the joints index, weights values and final vertex as colors, here is what I get :
// Shader
color_debug = joints_influences;
http://www.images-host.fr/view.php?img=00021800pop.jpg
color_debug = weights_influences;
http://www.images-host.fr/view.php?img=00021800pop2.jpg
Another interesting thing, when I try this :
vec4 pop = vec4(vertexPos, 1) * animation_matrices[1] * inv_bind_matrices[1] * 1.0;
gl_Position = ViewProj * Model * pop;
My all mesh is actually rotating, which means that my uniform animation_matrices is good.
Anyone can see what i'm doing wrong here ?
I finally got it working. For those who may be interested, here is what I was doing wrong :
When I send joints indices array to Glsl, instead of doing this:
glEnableVertexAttribArray(5);
glBindBuffer(GL_ARRAY_BUFFER, m->GetBuffer(5));
glVertexAttribPointer(5, 4, GL_INT, GL_FALSE, 0, BUFFER_OFFSET(0));
I needed to do this:
glEnableVertexAttribArray(5);
glBindBuffer(GL_ARRAY_BUFFER, m->GetBuffer(5));
glVertexAttribIPointer(5, 4, GL_INT, 0, BUFFER_OFFSET(0));
You have to look closely to find the difference. Instead of calling glVertexAttribPointer(), I needed to call glVertexAttribIPointer() because joints indices are int.
Hope this will help someone someday.
Did you try debugging your skinning attributes? Output the vertex weight as colors so that you can confirm you have meaningful values? If everything is black you'll know where to look.
From a quick glance at your RenderingVertices I can spot a first problem. You are passing a Vector of pointers to GL which I don't think is what you want to do.
Most of the time you will limit skinning influences to 4 joint/weight pairs per vertex. So you can get away with a simple array (ie. SkinningJoints joints[4];).