0
\$\begingroup\$

A time has come when i had to implement an Animation to my Engine. I started with standart MD5 mesh/anim format since i find it easy to understand. I managed to implement a simple MD5 loader and i can load MD5 meshes along with their animations. Animations work fine , i am using GPU skinning and CPU interpolation. Here comes my Problem: Performance (you can say i am obsessed with it). Currently i use this:http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html Tutorial as an example. As i said animations work fine but performance is horrible. With one Single MD5 animated Model in my scene i get ~60-80 FPS. If i add more models (~3-4 models) FPS drops to ~12-20. I know what causes that FPS drop , since i am doing a lot of computations CPU side every frame: Interpolating, filling Bone Matrices etc. Here is The Hefty CPU side part which runs every frame to compute Each Bone Transformation Matrices , from there i upload this Bone transformation Matrices to The shader and apply the skinning.

void SkinnedMesh::BoneTransform(float TimeInSeconds, vector<Matrix4f>& Transforms)
{
Matrix4f Identity;
Identity.InitIdentity();

float TicksPerSecond = (float)(m_pScene->mAnimations[0]->mTicksPerSecond != 0 ? m_pScene->mAnimations[0]->mTicksPerSecond : 25.0f);
float TimeInTicks = TimeInSeconds * TicksPerSecond;
float AnimationTime = fmod(TimeInTicks, (float)m_pScene->mAnimations[0]->mDuration);

ReadNodeHeirarchy(AnimationTime, m_pScene->mRootNode, Identity);

Transforms.resize(m_NumBones);

for (uint i = 0 ; i < m_NumBones ; i++) {
    Transforms[i] = m_BoneInfo[i].FinalTransformation;
}

}

Function Above takes vector of transformation matrices for each bone (i am working with a model that has ~71 Bones)

This function calculates interpolations etc..

void SkinnedMesh::ReadNodeHeirarchy(float AnimationTime, const aiNode* pNode, const Matrix4f& ParentTransform)
{    
string NodeName(pNode->mName.data);

const aiAnimation* pAnimation = m_pScene->mAnimations[0];

Matrix4f NodeTransformation(pNode->mTransformation);

const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnimation, NodeName);

if (pNodeAnim) {
    // Interpolate scaling and generate scaling transformation matrix
    aiVector3D Scaling;
    CalcInterpolatedScaling(Scaling, AnimationTime, pNodeAnim);
    Matrix4f ScalingM;
    ScalingM.InitScaleTransform(Scaling.x, Scaling.y, Scaling.z);

    // Interpolate rotation and generate rotation transformation matrix
    aiQuaternion RotationQ;
    CalcInterpolatedRotation(RotationQ, AnimationTime, pNodeAnim);        
    Matrix4f RotationM = Matrix4f(RotationQ.GetMatrix());

    // Interpolate translation and generate translation transformation matrix
    aiVector3D Translation;
    CalcInterpolatedPosition(Translation, AnimationTime, pNodeAnim);
    Matrix4f TranslationM;
    TranslationM.InitTranslationTransform(Translation.x, Translation.y, Translation.z);

    // Combine the above transformations
    NodeTransformation = TranslationM * RotationM * ScalingM;
}

Matrix4f GlobalTransformation = ParentTransform * NodeTransformation;

if (m_BoneMapping.find(NodeName) != m_BoneMapping.end()) {
    uint BoneIndex = m_BoneMapping[NodeName];
    m_BoneInfo[BoneIndex].FinalTransformation = m_GlobalInverseTransform * GlobalTransformation * m_BoneInfo[BoneIndex].BoneOffset;
}

for (uint i = 0 ; i < pNode->mNumChildren ; i++) {
    ReadNodeHeirarchy(AnimationTime, pNode->mChildren[i], GlobalTransformation);
}
}

I wont upload the following functions which are being called in ReadNodeHeirarchy , but you get the idea . The CPU calculations per frame are A LOT. Thus i get terrible performance. I am Quite new at animation and skinning. Somewhere i was reading about using Quaternions insted of Bone Transformation matrices. I am open to any suggestions on how i may improve or even change my animating technique.

PS: Vertex Skinning Shader Code:

#version 330                                                                        

layout (location = 0) in vec3 Position;                                             
layout (location = 1) in vec2 TexCoord;                                             
layout (location = 2) in vec3 Normal;                                               
layout (location = 3) in ivec4 BoneIDs;
layout (location = 4) in vec4 Weights;

out vec2 TexCoord0;
out vec3 Normal0;                                                                   
out vec3 WorldPos0;                                                                 

const int MAX_BONES = 120;

uniform mat4 gWVP;
uniform mat4 gWorld;
uniform mat4 gBones[MAX_BONES];

void main()
{       
mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
BoneTransform     += gBones[BoneIDs[1]] * Weights[1];
BoneTransform     += gBones[BoneIDs[2]] * Weights[2];
BoneTransform     += gBones[BoneIDs[3]] * Weights[3];

vec4 PosL    = BoneTransform * vec4(Position, 1.0);
gl_Position  = gWVP * PosL;
TexCoord0    = TexCoord;
vec4 NormalL = BoneTransform * vec4(Normal, 0.0);
Normal0      = (gWorld * NormalL).xyz;
WorldPos0    = (gWorld * PosL).xyz;                                
}
\$\endgroup\$
  • \$\begingroup\$ Quaternions will probably give you a bit of a speed up for stacking up rotations. \$\endgroup\$ – Eugene Marcotte Apr 26 '15 at 4:02
  • \$\begingroup\$ That is what i was thinking. Is it possible to move most of the calculations straight on the Shader instead of the CPU. I assume i will have to create methods in the shader to handle Quaternion math(mult,slerp etc.) ? But my question is: Is it a good practice to do that in the shader? I have heard that shaders do better operating with matrices \$\endgroup\$ – user2742982 Apr 26 '15 at 7:09
  • \$\begingroup\$ While I've not done it, it should be possible... You may have to scratch your head a bit to deal with all the data uploads (you'll need more buffers to contain either matrices or quats) to go with your vertex data etc. You wouldn't just make uniforms or attributes or whatever for this data \$\endgroup\$ – Eugene Marcotte Apr 26 '15 at 13:15
  • \$\begingroup\$ So i should implement Animation calculations purely on GPU ? Recently i tried to run in release mode and i grained nearly 1700% (x17) boost in performance as posed to debug. But that does not seem right. Previously i was rendering my scene for ~17-18 Millisecs now its 1 Millsec. Is it possible for the release mode to optimize CPU calculations that much ? \$\endgroup\$ – user2742982 Apr 26 '15 at 14:18
  • \$\begingroup\$ Absolutely possible. Safety checks and things depending on your matrix implementation could be very dramatic. \$\endgroup\$ – Eugene Marcotte Apr 26 '15 at 14:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.