Hello community,
<Skin VertexID="2" Type="Rigid">
<Bone BoneID="22" Weight="1,000000"></Bone>
</Skin>
...
<Skin VertexID="14" Type="Blended">
<Bone BoneID="22" Weight="0,986472"></Bone>
<Bone BoneID="14" Weight="0,013015"></Bone>
<Bone BoneID="23" Weight="0,000513"></Bone>
</Skin>
...
<Skin VertexID="31" Type="Blended">
<Bone BoneID="22" Weight="0,948354"></Bone>
<Bone BoneID="23" Weight="0,030462"></Bone>
<Bone BoneID="23" Weight="0,011265"></Bone>
<Bone BoneID="14" Weight="0,009909"></Bone>
<Bone BoneID="23" Weight="0,000011"></Bone>
</Skin>
My shader assumes that there should be always 4 bones, which influence a single vertex, and as you can see, sometimes there is sulprus/insufficient number of them, so I decided to:
- Sum up weights of the same bone - take a look at vertex 31 - there are five entries, but after summing them up, we're left with three.
- Pad the remaining number of weights with zero.
const
MAX_BONES_PER_VERTEX = 4;
type
TVertexWeight = record
BoneID: Integer;
Weight: Single; // standard float
end;
...
var
L: TList<TVertexWeight>;
...
glGenBuffers(1, @FBonesVBO);
glBindBuffer(GL_ARRAY_BUFFER, FBonesVBO);
glBufferData(GL_ARRAY_BUFFER, L.ListSize, L.ListPointer, GL_STATIC_DRAW);
glEnableVertexAttribArray(FBoneIDsAttr);
glVertexAttribIPointer(FBoneIDsAttr, MAX_BONES_PER_VERTEX, GL_INT,
SizeOf(TVertexWeight), PInteger(0));
glEnableVertexAttribArray(FWeightsAttr);
glVertexAttribPointer(FWeightsAttr, MAX_BONES_PER_VERTEX, GL_FLOAT,
False, SizeOf(TVertexWeight), PInteger(4));
I think this part is working fine, because when I hardcode an identity matrix in the shader, the model is rendered in its bind pose.
procedure CalculateBones(const SequenceName: String; const FrameID: Integer;
const ABone: TAnimatedMeshBone; const ParentTransform: TBrainMatrix);
var
Samples: TBrainList<TAnimatedMeshAnimationSample>;
BoneSample: TAnimatedMeshAnimationSample;
I: Integer;
BoneTransform, GlobalTransform: TBrainMatrix;
Children: TBonesList;
begin
Samples := TBrainList<TAnimatedMeshAnimationSample>.Create();
Children := TBonesList.Create();
try
FAnims[SequenceName].SamplesByFrame(FrameID, Samples);
BoneSample := nil;
for I := 0 to Samples.Count -1 do
if (Samples[I].Bone.ID = ABone.ID) then
begin
BoneSample := Samples[I];
break;
end;
BoneTransform := ABone.OffsetMatrix;
if (BoneSample <> nil) then
BoneTransform := BoneSample.Matrix;
GlobalTransform := Mat4Multiply(ParentTransform, BoneTransform);
BoneMatrices.Add(GlobalTransform);
FBones.BonesByParentID(ABone.ID, Children);
for I := 0 to Children.Count -1 do
CalculateBones(SequenceName, FrameID, Children[I], GlobalTransform);
finally
Samples.Free();
Children.Free();
end;
end;
...
BoneMatrices := TBrainList<TBrainMatrix>.Create();
try
// A bone's offset matrix is the bind pose.
CalculateBones('zhuxian2', 49, FBones.RootBone, FBones.RootBone.OffsetMatrix);
glUniformMatrix4fv(FShader.GetUniformLocation('boneMatrices'),
BoneMatrices.Count, False, BoneMatrices.ListPointer);
finally
BoneMatrices.Free();
end;
#version 150
#define MAX_BONES_PER_VERTEX 4
#define MAX_BONES 32
uniform mat4 proj;
uniform mat4 modelview;
uniform mat4 boneMatrices[MAX_BONES];
in vec3 vertex;
in vec3 normal;
in vec2 texCoord;
in ivec4 boneIDs;
in vec4 boneWeights;
out vec4 fragmentColor;
float phong_weightCalc(in vec3 light_pos, in vec3 frag_normal) {
return max(0.0, dot(frag_normal, light_pos));
}
mat4 getBoneMatrix(int boneIndex) {
mat4 retMat = mat4(1.0);
if (boneIDs[boneIndex] != 0) {
retMat = boneMatrices[boneIDs[boneIndex]];
}
return retMat;
}
float getWeight(int boneIndex) {
float res = 1.0;
if (boneIDs[boneIndex] != 0) {
res = boneWeights[boneIndex];
}
return res;
}
void main() {
vec3 EC_Light_location = vec3(0.0, 1.0, 1.0);
float diffuse_weight = phong_weightCalc(
normalize(EC_Light_location),
normalize(normal)
);
fragmentColor = clamp(
(
(vec4(0.2, 0.2, 0.2, 1.0))
+ (vec4(0.8, 0.8, 0.8, 1.0) * diffuse_weight)
),
0.0, 1.0);
float totalWeight = 0.0;
vec4 PosL = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < MAX_BONES_PER_VERTEX; i++) {
float w = getWeight(i);
PosL += w * (vec4(vertex, 1.0) * getBoneMatrix(i));
totalWeight += w;
}
PosL /= totalWeight;
PosL.w = 1.0;
gl_Position = proj * modelview * PosL;
}
The question is, can someone point out what is wrong with the code?