2021SC@SDUSC
文章目录
骨骼决定了模型整体在世界坐标系中的位置和方位。 在渲染静态模型时, 由于模型的顶点都是定义在模型坐标系中的,所以只要将各顶点模型坐标系变换到世界坐标系即可。而对于骨骼动画,我们设置模型的位置和方位, 实际是在设置根骨骼的位置和方位,然后根据骨骼层次结构中父子骨骼之间的变换信息计算出各个骨骼的位置和方位。
骨骼数据结构
任何类型角色无论是人、直立动物还是爬行动物的骨骼,都可以描述成一个以某个关节为根节点逐 步扩展生成的树形结构。整个骨骼是一个蕴含父子关系的树形结构描述。
Dust3D为(哺乳)动物提供了统一的骨骼组织形式,分为Neck、Limb、Tail、Joint标记,用户可以在使用时选择套用“Animal”的骨骼类型。对于非生物或其他不那么规则的骨骼,可以使用Joint对关节部分进行自定义标记。
//骨骼标记
enum class BoneMark
{
None = 0,
Neck,
Limb,
Tail,
Joint,
Count
};
骨骼类,用于定义一根可控制的骨骼。
class RigBone
{
public:
QString name;
int index = -1;
int parent = -1;
//骨骼头部的位置
QVector3D headPosition;
//骨骼尾部的位置
QVector3D tailPosition;
//骨骼头部节点半径
float headRadius = 0.0;
//骨骼尾部节点半径
float tailRadius = 0.0;
QColor color;
std::map<QString, QString> attributes;
//指向子骨骼的向量
std::vector<int> children;
};
关节节点类,存储父节点索引、子节点索引、当前节点位置及变换信息。
bindMatrix :表示骨骼基本变换的Matrix4(一般为4x4矩阵)。将骨架绑定到一个蒙皮网格上,其逆矩阵inverseBindMatrix 也会被计算出来。
JointNodeTree存储RigBone和JointNode序列。
struct JointNode
{
//父节点索引
int parentIndex;
QString name;
//节点位置
QVector3D position;
//变换信息
QVector3D bindTranslation;
QVector3D translation;
QQuaternion rotation;
QMatrix4x4 bindMatrix;
QMatrix4x4 inverseBindMatrix;
//子节点索引
std::vector<int> children;
};
class JointNodeTree
{
public:
const std::vector<JointNode> &nodes() const;
JointNodeTree(const std::vector<RigBone> *resultRigBones);
void updateRotation(int index, const QQuaternion &rotation);
void updateTranslation(int index, const QVector3D &translation);
void updateMatrix(int index, const QMatrix4x4 &matrix);
private:
std::vector<JointNode> m_boneNodes;
};
updateMatrix
我们从一个顶点vertex出发,假设vertex受到bone2的影响,骨骼B具有一个父节点骨骼A,已知骨骼B到其父节点骨骼A的bindMatrix矩阵,同时还知道骨骼B到世界坐标系的变换矩阵,这个矩阵会帮我们把处于世界坐标下的mesh顶点转化到骨骼B的坐标系下,以及骨骼A到世界坐标的变换矩阵,那么一个顶点的最终位置=骨骼A到世界坐标系的变换矩阵×bindMatrix×骨骼A到世界坐标系的变换矩阵×原始位置。
void JointNodeTree::updateMatrix(int index, const QMatrix4x4 &matrix)
{
const QMatrix4x4 &localMatrix = matrix;
updateTranslation(index,
QVector3D(localMatrix(0, 3), localMatrix(1, 3), localMatrix(2, 3)));
//将本地坐标转换为世界坐标
float scalar = std::sqrt(std::max(0.0f