1. 引言
三维动画是计算机图形技术领域中一个重要的研究方向,它可以清晰、直观、逼真地展现角色的动作和行为过程,广泛应用于医学、教育、军事、娱乐等领域。VEGA PRIME是MULTIGEN-PARADIGM公司(后被PRESAGIS公司收购)开发的实时视景仿真软件,它集成了海洋、大气、星空、地形、特效等各种通用模块,并且可以通过扩展插件支持虚拟仪表、红外成像、雷达成像等专业模块,广泛应用于城市规划、海洋仿真、建筑设计、飞行仿真等各个领域,是目前最流行的视景仿真工具之一 [1] [2] 。
VEGA PIRME本身并不支持目前主流的三维动画模型格式,而类似的扩展插件基本上都是商业发布的,其价格居高不下,限制了VEGA PRIME的推广应用。如果能够通过插件扩展的方式使得VEGA PRIME能够实时的加载、渲染三维动画,那么在飞行仿真、舰船仿真训练时,就可以以三维动画的形式全方位地展示仿真过程中虚拟角色的动作和行为,从而提高VEGA PRIME视景仿真引擎的适用性并增强仿真训练效果。
2. 三维动画技术分类简介
三维动画的基本原理是让模型中各个顶点的位置随时间变化,其主要技术有三种:变形动画、关节动画和蒙皮动画。
1) 变形动画中的角色模型由一系列的渐变网格模型构成,变形动画要达到模型细致的效果,必然需要大量的帧数,最理想的情况是渲染程序中的每一帧都对应于模型中一帧,但这样模型数据量会非常大,因此只能存储关键时刻模型顶点的位置,其过度时刻的顶点数据由两个相邻的关键时刻插值获得,这使得变形动画灵活性差,存储开销大;
2) 关节动画的角色模型由若干独立的关节点组成。每一关节点是一个独立的网格模型,对应于人体的一个关节,不同的关节按照角色的特征组织成一个层次结构,通过改变不同关节的位置和旋转,就可以实现各种所需的动画效果,关节动画的优点是存储空间小,缺点是在关节的连接处会有明显接缝,影响真实感;
3) 蒙皮动画是在骨骼系统基础上发展而来的,其基本原理为:在骨骼系统的控制下,模型网格的顶点可以被几块骨骼所影响,每块骨头对顶点影响的权重不同,骨骼对顶点的作用之和就是该顶点的最终效果。蒙皮动画数据内容包括骨骼层次数据、模型网格数据、骨骼蒙皮数据和动画关键帧数据,其中骨骼的运动由动画关键帧数据驱动,蒙皮动画同时兼有关节动画的灵活和渐变动画的逼真,现代主流的三维动画格式均采用了蒙皮动画技术 [3] [4] 。
3. MD5模型动画原理及文件格式
MD5三维动画模型格式是由ID SOFTWARE公司推出的世界上首款真正意义上的蒙皮动画格式,在海拔2004年随着DOOM3一起面世,经过几个版本的更新,现在在蒙皮动画格式中依然有着其重要地位。MD5是在骨骼动画基础上发展来的,其中骨骼的概念与人体骨骼类似,可以把人体看作一堆骨骼,然后外面蒙上一层肌肉、皮肤,在骨骼运动的过程中这些肌肉、皮肤就跟随骨骼的运动而变化。在三维动画中,骨骼与骨骼之间通过关节连接,一根骨骼的一端或两端连着两个关节,而一个关节可能连着数条骨骼,骨骼模型的描述也分以骨骼为主和以关节为主,其中MD5格式属于后者。在MD5格式中,关节的集合可以用一个树型的数据结构描述,首先定义一个总的关节根节点,其下连着一个或者多个子关节,这些子关节本身也作为父关节连着一个或多个子关节。在骨骼变化时,父关节的移动首先传递到子关节上,再叠加上子关节本身的移动,于是就可以建立一个前向的驱动模式,每个关节的运动信息可以抽象成一个三维变换矩阵M,这样这个驱动模型可以看作是每个时刻给予每个节点一个变换矩阵,变换节点的位置和旋转以驱动整个骨骼模型的运转。
MD5蒙皮动画模型由两个文件组成:MD5MESH文件和MD5ANIM文件,前者描述了关节(即骨骼)和拓扑,组成网格的顶点的权重、纹理等信息,后者描述了关节变化的关键帧信息 [5] [6] 。
3.1. MD5MESH文件结构分析
其中,MD5MESH文件的结构和格式如图1所示。

Figure 1. Structure of MD5MESH file
图1. MD5MESH文件的结构
MD5MESH文件由基本信息块、关节信息块和多个网格信息块三部分组成,它是一个文本文件,其中每一行都有特定的格式。
1) 基本信息块中描述了三维动画模型文件的版本号、命令行、关节数量、网格数量等信息;
2) 关节信息块以joints关键字开头,在一个封闭的大括号中描述了组成骨骼系统的各个关节信息,其中的每一行数据都描述一个关节,典型的关节信息示例如下;
“origin” −1(0.480.00.0)(0.70.00.0)
“bip01” 0(9.520.00.35)(0.00.00.0)
…
其中“bip01”是关节名称,其后的数字是父关节的索引序号,−1表示没有父关节,0指向名称为“origin”的关节,紧跟的一个小括号中的三个值是bindpose状态(即骨骼的初始姿态)下关节的位置(px, py, pz),最后一个小括号中的三个值是bindpose状态下关节的旋转,用四元数表示,文件中给出了四元数三个分量(qx, qy, qz),需要按公式1自行计算第四个分量qw。
获取关节的位置和旋转信息后,可以构造表示关节位置和旋转的局部矩阵ML,然后叠加父关节的位置和旋转,即可以得到关节的全局矩阵MG,对应的计算公式如公式2所示。
(公式1)
(公式3)
公式2中MG-Parent是父关节的全局矩阵,如果父关节仍然存在父节点,则可以继续通过上述公式迭代计算。
3) 网格信息块以mesh关键字开头,其中包含四类数据:纹理信息、顶点信息、图元信息和权重信息。其中,shader后的字符串表示网格对应的纹理图片路径;numverts表示此网格包含的顶点数量;vert表示顶点,顶点并不是直接通过坐标的形式给出,而是给出了顶点对应权重信息的索引起始序号和权重数量,此外为了实现纹理贴图还给出了顶点对应的纹理坐标;numtris表示网格所包含的三角面片的数量;tri开头的行表示三角面片,其后的四个数字分别表示面片序号和组成三角面片的三个顶点的索引;numweights表示权重信息的数量;weight开头的行表示权重信息,每个权重信息都给出了序号、对应的关节序号、比率值(即权重值)和位置等信息。
上述信息读取完毕后,就可以进行蒙皮计算,也就是计算顶点的实际位置,计算公式如下:
(公式3)
其中,s是指顶点对应的权重信息的索引起始序号,n表示顶点对应的权重数据的数量,Pi指的是第i条权重数据对应的位置,MGi表示第i条权重数据对应的关节的全局矩阵,用行向量形式的齐次坐标[px, py, pz, 1]表示,bi指的是第i条权重数据对应的比率值(即权重值),计算结果V就是顶点的坐标。
3.2. MD5ANIM文件格式分析
MD5ANIM文件也是一个文本文件,如图2所示,它由基本信息块、节点集成信息块、包围盒信息块、基础帧信息块和多个关键帧信息块组成。
1) 基本信息块中描述了三维动画模型文件版本号、命令行、关键帧数量、关节数量、播放帧率和骨骼动画变化数据数量等信息。
2) 节点继承信息块以hierarchy关键字开头,大括号中的每一行都包括了关节名称,父关节序号,关节信息更新掩码FLAG和关节信息更新起始位置,其中关节名称和父关节序号等内容与MD5MESH文件中关节信息块的对应内容完全一致。
3) 包围盒信息块以bounds关键字开头,每一行对应一个关键帧,描述了在对应的关键帧下模型对象的最小轴对齐包围盒,由一个最小点的坐标和最大点坐标组成。

Figure 2. Structure of MD5ANIM file
图2. MD5ANIM文件的结构
4) 基础帧信息块由baseframe关键字开头,每一行都表示对应关节位置和旋转,其中前一个括号中的三个值是关节的默认位置,后一个括号中的三个值是关节的默认旋转(四元数表示,需自行计算qw)。
5) 关键帧信息块由frame关键字+帧序号开头,大括号中包含描述骨骼位置和姿态的数值,其中每一行对应一个关节,一行数据最多包含六个值,至少包含一个值,这些数据构成一个Animated Components数组,其长度等于基本信息块中定义的num Animated Components的值,关节的实际的位置和姿态需要根据关节的更新掩码FLAG和更新起始位置,使用关键帧数据替换对应的基础帧数据得到,其替换规则如图3所示。

Figure 3. Rules for replacing base frame data
图3. 替换基础帧数据的规则
更新掩码FLAG的值从低到高六个比特分别表示基础帧数据中位置的三个值(px, py, pz)和旋转(qx, qy, qz)是否应该被替换,执行替换时首先得到关节对应的基础帧数据和关节的更新起始位置,然后从Animated Components数组的关节更新起始位置开始,从低到高依次检查更新掩码FLAG,如果对应比特位为1则使用关键帧数据替换对应的基础帧数据,如对应图中的更新掩码值,则px、py、qx、qy、qz将被Animated Components数组中由关节信息更新起始位置开始标记的5个数据依次替换。替换完成后即可得到关键帧状态下此关节的实际位置和旋转,然后就可以根据公式1、2、3计算对应的骨骼和蒙皮。
4. VEGA PRIME动画插件的设计
按照VEGA PRIME的插件开发规范,一个完整的VEGA PRIME插件包含三部分内容:GCF文件、XML SCHEMA文件、可执行文件和库(包括头文件、动态库和链接库) [7] (图4)。

Figure 4. Composition of VEGA PRIME 3D animation plug-in
图4. VEGA PRIME三维动画插件组成
GCF文件用于描述在LYNX PRIME环境下插件的图形交互配置界面,它通过一组LYNX PRIME内置控件将插件位置、姿态、缩放等配置参数以页面的形式显示在LYNX PRIME程序中;XML SCHEMA文件用于连接GCF文件和插件可执行程序和库,将图形界面下的参数调整行为转化成对应的插件API调用,以便在预览时可以直观的观察到调整的效果;插件的可执行程序和库采用编程实现插件的功能并提供API接口,其功能实现主要分成两部分。
4.1. 模型的加载和绘制
模型的加载和绘制在AnimMD5类中实现,为实现模型文件读取、解析加载、骨骼计算、蒙皮计算的相关功能,需要首先与MD5蒙皮动画中的概念相对应,分别定义Vertex、Triangle、Weight、Joint、Frame、Hierarchy等结构体,分别用于描述顶点、三角面片、权重、关节、帧(包含基础帧和关键帧),节点继承关系等,然后定义Mesh类和Anim类分别表示MD5MESH文件和MD5ANIM文件的内容,最后在loadMesh和loadAnim函数中实现文件的加载功能,其过程为:逐行读取文件,根据文件格式解析内容并构造Mesh对象或者Anim对象,过程中出现错误则加载失败。成功则依次进行关节全局矩阵的计算和蒙皮计算。
蒙皮动画的更新、渲染在updateAndDraw中实现,其流程如图5所示。

Figure 5. Updating and rendering process of animation
图5. 动画的更新和渲染流程
1) 在播放开始时记录动画开始播放的时刻,然后在每一帧的更新渲染时获取当前时刻,并且计算动画的播放时长。
2) 比较播放时长和动画时长(即帧率x帧数−1)判断动画是否结束,如果已经结束则获取最后一个关键帧。
3) 如果没有结束,则根据帧率、播放时长找到两个相邻的关键帧,然后依次对关键帧各个关节的位置进行线性插值,选择进行球面线性插值,得到当前帧。
4) 利用步骤2或3获取的帧信息进行蒙皮计算各个网格的顶点,然后逐顶点计算法线。
5) 绑定纹理,构造顶点数组,使用OpenGL渲染当前时刻的模型网格。
4.2. 集成插件设计实现
按照VEGA PRIME插件规范,首先必须从vpModule派生一个类vpGearModule负责插件的初始化、关闭清理等;然后从vpTransform派生子类vpAnimMD5,其实例将作为节点加入到场景图中,并具备调整模型位置、姿态、缩放等功能;vpAnimMD5Geom (从vsGeometryBase派生)表示几何体,其实例将作为子节点插入到vpAnimMD5中,同时模型的视景裁切在其cull函数中实现,过程为:首先获取模型当前时刻的包围盒,然后根据包围盒进行视景裁切计算,如果模型处于当前摄像机的可见范围内,则将对应的vpAnimMD5GCR实例加入到筛选结果。动画的更新和渲染在vpAnimMD5GCR的processDraw函数中完成,在其中调用AnimMD5: updateAndDraw即可,代码如下:


Figure 6. Animation models rendered by plug-ins in VEGA PRIME
图6. 在VEGA PRIME中使用插件渲染的动画模型
5. 结束语
在深入理解MD5三维动画模型文件格式和动画原理的前提下,通过C++编程基于VEGA PRIME设计实现了MD5三维动画插件,基于该插件的三维动画如图6所示,其中图6(a)是插件GCF文件对应的配置界面,图6(b)是bindpose状态下飞行员的模型,图6(c)~(e)分别是行走状态下飞行员各个时刻的渲染效果。插件具备在仿真程序中控制动画的加载、播放,以及位置、姿态和缩放调整等功能,并且渲染效率能够稳定维持在60赫兹以上。实践证明能够满足虚拟角色运动展示的要求。