ThreeJS教程:变形动画(定制人物胖瘦)
推荐:将NSDT场景编辑器加入你的3D工具链
3D工具集:NSDT简石数字孪生
变形动画(定制人物胖瘦)
下面给大家演示一个变形动画的应用例子,先通过三维建模生成几何体的变形目标顶点数据,不用创建关键帧动画,然后通过拖动条,控制变形目标权重系数,控制人物形象的胖瘦。
模型几何体变形相关信息
查看Blender软件中几何体变形信息,可以通过形态键调节查看测试效果。
课件代码浏览器控制打印人物模型,可以看到,有几何体的变形目标顶点数据,但是没有通过关键帧动画设置模型的变形动画效果。
loader.load("../人.glb", function (gltf) {
console.log('控制台查看gltf对象结构', gltf);
model.add(gltf.scene);
// 访问人体网格模型
const mesh = gltf.scene.children[0];
// 获取所有变形目标的顶点数据
const tArr = mesh.geometry.morphAttributes.position
console.log('所有变形目标', tArr);
console.log('所有权重', mesh.morphTargetInfluences);
//每个变形目标对应的含义(注意和变形目标对应起来)
const nameArr = ['变胖', '丰乳肥臀', '增肌', '年龄', '变瘦'];
})
UI界面定制人胖瘦体型
人物模型有多个变形目标,下面随机选择了两个设置拖动条控制变形目标对应的权重系数。
import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
const gui = new GUI();
const obj = {
t0: 0,
t1: 0,
}
gui.add(obj, 't0', 0, 1).name('变胖').onChange(function (v) {
mesh.morphTargetInfluences[0] = v;
});
gui.add(obj, 't1', 0, 1).name('丰乳肥臀').onChange(function (v) {
mesh.morphTargetInfluences[1] = v;
});
批量设置所有变形目标的拖动条
import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
const mesh = gltf.scene.children[0]; // 访问人体网格模型
// 获取所有变形目标的顶点数据
const tArr = mesh.geometry.morphAttributes.position
// 每个变形目标对应的含义(注意和变形目标对应起来)
const nameArr = ['变胖', '丰乳肥臀', '增肌', '年龄', '变瘦'];
// GUI拖动条可视化改变变形目标权重系数
const obj = {};
const gui = new GUI();
for (let i = 0; i < tArr.length; i++) {
obj['t' + i] = 0;//obj批量定义一个属性表示变性目标的权重系数
// 批量设置要改变的obj属性,对应name名字,和对应权重
gui.add(obj, 't' + i, 0, 1).name(nameArr[i]).onChange(function (v) {
mesh.morphTargetInfluences[i] = v;
});
}
外部模型变形数据生成动画
loader.load("../人.glb", function (gltf) {
const mesh = gltf.scene.children[0];
// 创建变形动画权重系数的关键帧数据
mesh.name = "per"; //关键帧动画控制的模型对象命名
// 设置变形目标1对应权重随着时间的变化
const KF1 = new THREE.KeyframeTrack('per.morphTargetInfluences[0]', [0, 5], [0, 1]);
// 生成关键帧动画
const clip = new THREE.AnimationClip("t", 5, [KF1]);
//包含关键帧动画的模型作为参数创建一个播放器
const mixer = new THREE.AnimationMixer(gltf.scene);
const clipAction = mixer.clipAction(clip);
clipAction.play();
const clock = new THREE.Clock();
function loop() {
requestAnimationFrame(loop);
const frameT = clock.getDelta();
// 更新播放器相关的时间
mixer.update(frameT);
}
loop();
})