Fix animated model skinning precision issues

#2460
This commit is contained in:
Wojtek Figat
2024-04-22 13:18:52 +02:00
parent 1072b90c5b
commit 568a69081d
5 changed files with 24 additions and 26 deletions

View File

@@ -430,7 +430,9 @@ float3x4 GetPrevBoneMatrix(int index)
float3 SkinPrevPosition(ModelInput_Skinned input)
{
float4 position = float4(input.Position.xyz, 1);
float3x4 boneMatrix = input.BlendWeights.x * GetPrevBoneMatrix(input.BlendIndices.x);
float weightsSum = input.BlendWeights.x + input.BlendWeights.y + input.BlendWeights.z + input.BlendWeights.w;
float mainWeight = input.BlendWeights.x + (1.0f - weightsSum); // Re-normalize to account for 16-bit weights encoding erros
float3x4 boneMatrix = mainWeight * GetPrevBoneMatrix(input.BlendIndices.x);
boneMatrix += input.BlendWeights.y * GetPrevBoneMatrix(input.BlendIndices.y);
boneMatrix += input.BlendWeights.z * GetPrevBoneMatrix(input.BlendIndices.z);
boneMatrix += input.BlendWeights.w * GetPrevBoneMatrix(input.BlendIndices.w);
@@ -439,12 +441,6 @@ float3 SkinPrevPosition(ModelInput_Skinned input)
#endif
// Cached skinning data to avoid multiple calculation
struct SkinningData
{
float3x4 BlendMatrix;
};
// Calculates the transposed transform matrix for the given bone index
float3x4 GetBoneMatrix(int index)
{
@@ -457,7 +453,9 @@ float3x4 GetBoneMatrix(int index)
// Calculates the transposed transform matrix for the given vertex (uses blending)
float3x4 GetBoneMatrix(ModelInput_Skinned input)
{
float3x4 boneMatrix = input.BlendWeights.x * GetBoneMatrix(input.BlendIndices.x);
float weightsSum = input.BlendWeights.x + input.BlendWeights.y + input.BlendWeights.z + input.BlendWeights.w;
float mainWeight = input.BlendWeights.x + (1.0f - weightsSum); // Re-normalize to account for 16-bit weights encoding erros
float3x4 boneMatrix = mainWeight * GetBoneMatrix(input.BlendIndices.x);
boneMatrix += input.BlendWeights.y * GetBoneMatrix(input.BlendIndices.y);
boneMatrix += input.BlendWeights.z * GetBoneMatrix(input.BlendIndices.z);
boneMatrix += input.BlendWeights.w * GetBoneMatrix(input.BlendIndices.w);
@@ -465,13 +463,13 @@ float3x4 GetBoneMatrix(ModelInput_Skinned input)
}
// Transforms the vertex position by weighted sum of the skinning matrices
float3 SkinPosition(ModelInput_Skinned input, SkinningData data)
float3 SkinPosition(ModelInput_Skinned input, float3x4 boneMatrix)
{
return mul(data.BlendMatrix, float4(input.Position.xyz, 1));
return mul(boneMatrix, float4(input.Position.xyz, 1));
}
// Transforms the vertex position by weighted sum of the skinning matrices
float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data)
float3x3 SkinTangents(ModelInput_Skinned input, float3x4 boneMatrix)
{
// Unpack vertex tangent frame
float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
@@ -479,10 +477,10 @@ float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data)
float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
// Apply skinning
tangent = mul(data.BlendMatrix, float4(tangent, 0));
normal = mul(data.BlendMatrix, float4(normal, 0));
tangent = normalize(mul(boneMatrix, float4(tangent, 0)));
normal = normalize(mul(boneMatrix, float4(normal, 0)));
float3 bitangent = cross(normal, tangent) * bitangentSign;
float3 bitangent = normalize(cross(normal, tangent) * bitangentSign);
return float3x3(tangent, bitangent, normal);
}
@@ -501,10 +499,9 @@ VertexOutput VS_Skinned(ModelInput_Skinned input)
VertexOutput output;
// Perform skinning
SkinningData data;
data.BlendMatrix = GetBoneMatrix(input);
float3 position = SkinPosition(input, data);
float3x3 tangentToLocal = SkinTangents(input, data);
float3x4 boneMatrix = GetBoneMatrix(input);
float3 position = SkinPosition(input, boneMatrix);
float3x3 tangentToLocal = SkinTangents(input, boneMatrix);
// Compute world space vertex position
CalculateInstanceTransform(input);

View File

@@ -10,7 +10,7 @@
/// <summary>
/// Current materials shader version.
/// </summary>
#define MATERIAL_GRAPH_VERSION 161
#define MATERIAL_GRAPH_VERSION 162
class Material;
class GPUShader;

View File

@@ -563,9 +563,10 @@ void MeshData::NormalizeBlendWeights()
ASSERT(Positions.Count() == BlendWeights.Count());
for (int32 i = 0; i < Positions.Count(); i++)
{
const float sum = BlendWeights[i].SumValues();
Float4& weights = BlendWeights.Get()[i];
const float sum = weights.SumValues();
const float invSum = sum > ZeroTolerance ? 1.0f / sum : 0.0f;
BlendWeights[i] *= invSum;
weights *= invSum;
}
}

View File

@@ -113,7 +113,7 @@ void AnimatedModel::PreInitSkinningData()
for (int32 boneIndex = 0; boneIndex < bonesCount; boneIndex++)
{
auto& bone = skeleton.Bones[boneIndex];
identityMatrices[boneIndex] = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex];
identityMatrices.Get()[boneIndex] = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex];
}
_skinningData.SetData(identityMatrices.Get(), true);

View File

@@ -720,23 +720,23 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
{
int vtxIndex = clusterIndices[j] - firstVertexOffset;
float vtxWeight = (float)clusterWeights[j];
if (vtxWeight <= 0 || vtxIndex < 0 || vtxIndex >= vertexCount)
continue;
auto& indices = mesh.BlendIndices[vtxIndex];
auto& weights = mesh.BlendWeights[vtxIndex];
Int4& indices = mesh.BlendIndices.Get()[vtxIndex];
Float4& weights = mesh.BlendWeights.Get()[vtxIndex];
for (int32 k = 0; k < 4; k++)
{
if (vtxWeight >= weights.Raw[k])
{
// Move lower weights by one down
for (int32 l = 2; l >= k; l--)
{
indices.Raw[l + 1] = indices.Raw[l];
weights.Raw[l + 1] = weights.Raw[l];
}
// Set bone influence
indices.Raw[k] = boneIndex;
weights.Raw[k] = vtxWeight;
break;