3D模型格式详解
OBJ 格式
OBJ 格式适用于 3D 软件模型之间的互导。几乎所有的 3D 软件都支持 OBJ 格式的读取,本质上它是一种文本文件。 OBJ 格式只是 3D 模型文件,它不包含动画、材质特性、贴图路径等 3D 模型特质。
FBX 格式
FBX 格式可以视为 OBJ 格式的升级版,它不仅是一个 3D 模型格式,更可以视为一个通用的模型格式,它包含动画、材质特性、贴图、骨骼动画、灯光、摄像机等信息。FBX 格式支持多边形游戏模型、曲线、表面、点组材质。
FBX 格式经常用于游戏,是一种比较成熟的 3D 模型格式。
FBX 缺点也很明显,它是一个封闭格式,除开创者 Autodesk 外,其余很难对它进行更新。
STL 格式
STL 不能算是一种 3D 模型格式,它不支持颜色、材质、光照、纹理等 3D 模型信息,只能描述三维物体的几何信息,因此在 Three 导入时,通常不会把它视为第一序列的选择。
GLTF(推荐)
GLTF 格式几乎所有的框架都可以支持,也是官方极力推荐的格式。可以说,它就是为 3D 模型格式而诞生的,GLTF 跨平台格式已成为 Web 上的 3D 对象标准及 Web 导出的通用标准。 GLTF 文件可以包含模型、动画、几何体、材质、灯光、相机等 3D 模型属性,甚至它还可以包含整个场景。也就是说,你可以在外部创建整个模型的场景,然后直接加载到 Three 中。
GLTF 格式有两种扩展名
gltf: gltf 格式文件没有被压缩,会附带额外的 bin 数据文件,在文本编辑器中容易阅读,方便进行调试。
glb: 所有数据都包含在二进制文件中,文件小,推荐使用
GLTF格式包含的数据
{
animations: [AnimationClip],
asset: {version: '2.0', generator: 'THREE.GLTFExporter'},
cameras: [],
parser: GLTFParser {json: {…}, extensions: {…}, plugins: {…}, options: {…}, cache: {…}, …},
scene: Group {isObject3D: true, uuid: '06006dfc-5ae0-49b3-ba2e-cd32ae0dd256', name: 'AuxScene', type: 'Group', parent: null, …},
scenes: [Group],
userData: {},
}
{
animations: [AnimationClip],
asset: {version: '2.0', generator: 'THREE.GLTFExporter'},
cameras: [],
parser: GLTFParser {json: {…}, extensions: {…}, plugins: {…}, options: {…}, cache: {…}, …},
scene: Group {isObject3D: true, uuid: '06006dfc-5ae0-49b3-ba2e-cd32ae0dd256', name: 'AuxScene', type: 'Group', parent: null, …},
scenes: [Group],
userData: {},
}
- animations: 动画剪辑数组,GLTF 格式是可以包含动画效果的,本文使用的三只小鸟就包含了简单的飞行动画,后面我们来启用它。
- asset: GLTF 文件的元数据,由 Blender 导出时创建
- cameras: 一组相机
- paser: GLTFLoader 的技术细节
- scene: 模型存放的位置
- scenes: GLTF 可以将多个场景存储在一个文件中
- userData: 额外的一些非标准数据
模型光源
模型导入threejs里通常是黑色的,需要添加光源
DirectionalLight: 直射光,可以理解为手电筒,从放置处发出平行光。缺点也比较明显,只能照向一个位置,而且直射光很浪费性能,不建议使用太多。
AmbientLight: 环境光,从各个方向向场景中的每个对象添加恒定数量的光照,与现实中光的工作模式完全不同。用起来很简单,通常来配
DirectionalLight 来使用。但环境光由于各方向相同,无法显示出深度信息。
HemisphereLight: 半球光,光源在场景顶部的天空颜色和场景底部的地面颜色之间渐变,比较接近现实的光。半球光性能非常高,但其不从某一特定方向照射,因此通常和直射光配合使用实现某区域的高光效果。
模型动画
animations 属性存储了模型的动画,我们需要按照 Three 的模式来启动动画帧。
首先使用 AnimationMixer 混合器将静态对象转化为动态对象,然后利用 AnimationAction 将动画连接至混合器,为每个模型添加 tick 方法,在帧更新时同时更新鸟的混合器。
AnimationMixer 混合器
Three 场景中的动画对象对需要使用混合器,混合器控制模型的移动,因此后续通过更新混合器来实现动画帧的切换效果。
AnimationAction 动画的控制模块
动画效果通常由多个动作组成,该组件负责将每个动作进行动画剪辑,每个动画剪辑对应一个动作,该组件来控制动作的开启与暂停等控制功能。
tick 动画剪辑更新
AnimationAction 只是启动了动画,但动画真正开始播放需要更新渲染循环中的混合器。AnimationMixer 提供了 update 方法,允许根据时间参数来进行更新,通常使用帧——渲染循环更新时进行更新。
function setupModel(data) {
const model = data.scene.children[0];
// 获取到动画
const clip = data.animations[0];
// 声明混合器
const mixer = new AnimationMixer(model);
// 将动画按照动作进行动画剪辑
const action = mixer.clipAction(clip);
action.play();
// 更新混合器
model.tick = (delta) => mixer.update(delta);
return model;
}
function setupModel(data) {
const model = data.scene.children[0];
// 获取到动画
const clip = data.animations[0];
// 声明混合器
const mixer = new AnimationMixer(model);
// 将动画按照动作进行动画剪辑
const action = mixer.clipAction(clip);
action.play();
// 更新混合器
model.tick = (delta) => mixer.update(delta);
return model;
}
modelList = [];
// 获取模型后,放置到 birds 中
const model = await loadModel();
modelList.push(model);
onst clock = new Clock();
const render = () => {
renderer.render(scene, camera);
// 获取时间参数
const delta = clock.getDelta();
// 每一帧更新时更新混合器
modelList.forEach((model) => {
model.tick(delta);
});
controls && controls.update();
requestAnimationFrame(render);
};
modelList = [];
// 获取模型后,放置到 birds 中
const model = await loadModel();
modelList.push(model);
onst clock = new Clock();
const render = () => {
renderer.render(scene, camera);
// 获取时间参数
const delta = clock.getDelta();
// 每一帧更新时更新混合器
modelList.forEach((model) => {
model.tick(delta);
});
controls && controls.update();
requestAnimationFrame(render);
};