\$\begingroup\$

From a quick once-over it looks like all your stuff is correct conceptually. However, your vertex shader looks a bit suspect though. It seems like you are applying the world transform twice since it is already contained in the bone transforms. Or at least it seems that way in your code. This would be valid though if you bone transforms were relative to your overall object transform.

Also, I was initially confused by the fact that you were transforming by each bone influence and then adding the positions versus building a combined matrix first. I'm too lazy to work it out, but I think they are identical mathematically so this could just be a personal style thing.

The way I would usually do it is this:

// Declare the bone vectors mat4 worldSkinned = mat4(0); // If bone 0 is to be used if(in_boneIndex.x != -1) { worldSkinned += boneTransforms[int(in_boneIndex.x)] * in_boneWeight.x; } if(in_boneIndex.y != -1) { worldSkinned += boneTransforms[int(in_boneIndex.y)] * in_boneWeight.y; } if(in_boneIndex.z != -1) { worldSkinned += boneTransforms[int(in_boneIndex.z)] * in_boneWeight.z; } if(in_boneIndex.w != -1) { worldSkinned += boneTransforms[int(in_boneIndex.w)] * in_boneWeight.w; } gl_Position = projection * view * worldSkinned * vPos;

However, I prefer this way because you also need to transform the TBN basis and this way you can reuse the same matrix if you don't care about non-uniform scales

// Transform NBT to world tangentToWorld[0] = (worldSkinned * vec4(in_tangent, 0.0)).xyz; tangentToWorld[1] = (worldSkinned * vec4(in_binormal, 0.0)).xyz; tangentToWorld[2] = (worldSkinned * vec4(in_normal, 0.0)).xyz;

If you do need to care about non-uniform scales then you need to

mat3 worldSkinnedNormal = transpose(inverse(mat3(worldSkinned))); tangentToWorld[0] = (worldSkinnedNormal * in_tangent).xyz; tangentToWorld[1] = (worldSkinnedNormal * in_binorma).xyz; tangentToWorld[2] = (worldSkinnedNormal * in_normal).xyz;

Or just pass a second boneTransformsInverseTranspose[] to the shader, which could be faster depending on whether you are bottlenecked on uniforms or shader math.

EDIT: Also, another thing I noticed is that your index attribute seems weird. You have a float shader input but you are using glVertexAttribPointer with type GL_INT but with normalization turned on. So you are actually getting float(idx)/float(INT_MAX) in your shader. You should try passing false for the fourth argument or alternatively using an ivec4 in your shader and glVertexAttribIPointer directly.