Скелетная анимация с OpenGL 4 и FBX SDK; В чем моя проблема?
Прежде всего, посмотрите мой потрясающий результат:
Эта модель без шкуры:
Проблема в том, что что-то не так, но я не могу понять, что это такое. Это код импорта анимации с помощью FBX SDK:
void FbxLoader::processAnimation(FbxNode* fbxNode, std::string_view animationName, f64 frameRate, f64 stopTime)
{
auto bone = mModel->getSkeleton()->getHierarchy(StringHash(fbxNode->GetName()));
if (!bone) {
return;
}
auto animationSet = new KeyFrameSet{animationName};
f64 invFrameRate = 1.0 / frameRate;
FbxTime animationTime{};
f64 currTime = 0.0;
while (currTime <= stopTime) {
animationTime.SetSecondDouble(currTime);
auto localTransform = fbxNode->EvaluateLocalTransform(animationTime);
animationSet->addKeyFrame(ToMat4(localTransform));
currTime += invFrameRate;
}
bone->addAnimationSet(animationSet);
}
Честно говоря, я новичок в FBX SDK, поэтому я очень озадачен тем, что это правильный код для получения позы связывания.
При расчете матриц Sking гарантируется, что root -> parent -> child order, поэтому я подумал, что это может сработать. Это расчет кода скининга матриц:
void Skeleton::update(AnimationController* animationController)
{
auto currAnimationClip = animationController->getCurrAnimationClip();
std::vector<glm::mat4> boneTransforms(mHierarchy.size());
u32 currKeyFrameIndex = animationController->getCurrKeyFrameIndex();
u32 nextKeyFrameIndex = animationController->getNextKeyFrameIndex();
f32 delta = animationController->getDelta();
glm::mat4 invBindPose{};
glm::mat4 pose{}; // local pose
glm::mat4 parentPose{}; // local pose of parent
for (usize i = 0; i < mHierarchy.size(); ++i) {
auto bone = mHierarchy[i];
invBindPose = bone->getInvBindPose();
auto animationSet = bone->getKeyFrameSet(currAnimationClip->getName());
auto currKeyFrame = animationSet->getKeyFrameAt(currKeyFrameIndex);
auto nextKeyFrame = animationSet->getKeyFrameAt(nextKeyFrameIndex);
auto currRotation = currKeyFrame.getRotation();
auto nextRotation = nextKeyFrame.getRotation();
auto rotation = glm::slerp(currRotation, nextRotation, delta);
pose = glm::mat4_cast(rotation);
auto currTranslation = currKeyFrame.getTranslation();
auto nextTransaltion = nextKeyFrame.getTranslation();
auto translaion = glm::mix(currTranslation, nextTransaltion, delta);
pose[3] = glm::vec4{translaion, 1.0f};
// if root, identity matrix
i32 parentIndex = bone->getParentIndex();
parentPose = parentIndex < 0 ? glm::mat4{1.0f} : boneTransforms[parentIndex];
boneTransforms[i] = pose * parentPose;
// I think skin transfrom will be global inverse bind pose, because boneTransforms[i] is global pose matrix...
mSkinTransforms[i] = invBindPose * boneTransforms[i];
}
}
Затем я отправляю скининговые матрицы в вершинный шейдер:
#version 450 core
struct Phong {
vec3 n;
vec3 l;
vec3 v;
};
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
layout(location = 2) in vec3 a_Normal;
layout(location = 10) in uvec4 a_SkinIndex;
layout(location = 11) in vec4 a_SkinWeight;
uniform mat4 u_ModelViewMatrix;
uniform mat4 u_NormalMatrix;
uniform mat4 u_Projection;
uniform mat4 u_SkinMatrices[100];
uniform vec3 u_LightPosition;
out Phong v_Phong;
void main(void) {
vec4 position = u_ModelViewMatrix * vec4(a_Position, 1.0);
position
= (u_SkinMatrices[a_SkinIndex.x] * position) * a_SkinWeight.x
+ (u_SkinMatrices[a_SkinIndex.y] * position) * a_SkinWeight.y
+ (u_SkinMatrices[a_SkinIndex.z] * position) * a_SkinWeight.z
+ (u_SkinMatrices[a_SkinIndex.w] * position) * a_SkinWeight.w;
v_Phong.n = mat3(u_NormalMatrix) * a_Normal;
v_Phong.l = u_LightPosition - a_Position.xyz;
v_Phong.v = -a_Position.xyz;
gl_Position = u_Projection * position;
}
Я полностью потерян. Ребята, можете ли вы дать мне совет?