因为公司没有人会用blender,所以动画都是用max做的,动画师一般都是会把多个动画做在一个模型上,然后程序中需要使用的话,调用指定的帧数。unity可以把动画切割成animationclip。最近公司使用了需要在three.js中添加一个模型,模型带了一段长动画,所以我这边就是需要把模型的指定帧数之间的动画切割出来。
接下来演示部分:
1.准备素材一段带动画的素材
初始化,加入相机,灯光模型等元素
function init() {
var canHeight = window.innerHeight;
var canWidth = window.innerWidth;
container = document.createElement('div');
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(0, 150, 300);
scene = new THREE.Scene();
// ground
var mesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(2000, 2000), new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false }));
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
scene.add(mesh);
var grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000);
grid.material.opacity = 0.2;
grid.material.transparent = true;
scene.add(grid);
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(0, 100, 0);
controls.update();
LoadModel();
LoadLightModel();
document.getElementById("threejsContainer").appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
}
function LoadLightModel() {
scene.background = new THREE.Color(0xa0a0a0);
light = new THREE.HemisphereLight(0xffffff, 0x444444);
light.position.set(0, 200, 0);
scene.add(light);
light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 200, 100);
light.castShadow = true;
light.shadow.camera.top = 180;
light.shadow.camera.bottom = - 100;
light.shadow.camera.left = - 120;
light.shadow.camera.right = 120;
scene.add(light);
}
function LoadModel() {
var femaleModel = new THREE.FBXLoader();
femaleModel.load('../Model/Female_Match_Animation_H5.FBX', (object) => {
object.scale.set(100, 100, 100);
object.traverse(function (child) {
if (child.isMesh) {
if (child.name === 'head_base') {
child.visible = false;
}
if (child.name === 'female_body_gai001') {
child.material = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('../Model/map/female_body.jpg'),
skinning: true,
color: 0xffffff,
side: THREE.DoubleSide
});
}
if (child.name === 'female_head_26') {
for (var i = 0; i < child.material.length; i++) {
if (child.material[i].name === 'head_bozi_female_26_small_Std_Skin_Head') {
child.material[i] = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load(
'../Model/map/female_head_26.png'
),
skinning: true,
color: 0xffffff,
morphTargets: true,
side: THREE.DoubleSide
});
}
if (child.material[i].name === 'Eyelash') {
child.material[i] = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load(
'../Model/map/female_Eyelash.png'
),
skinning: true,
color: 0xffffff,
transparent: true,
});
}
if (child.material[i].name === 'Eye') {
child.material[i] = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load(
'../Model/map/female_eye.png'
),
skinning: true,
color: 0xffffff,
});
}
}
}
}
});
model = object;
scene.add(object);
});
}
导入模型之后如上图所示,这个时候并没有执行模型的动画,所以他就是没有动画的状态
2.切割three.js 需要使用的是THREE.AnimationUtils.subclip这个方法
我这边需要的是动作每一个动作的姿势,所以我只切割了一帧。如果大家需要的是一个动作的话就可以根据需要来切割
function SetModelAnimationClip(action) {
console.log(action)
mixer = new THREE.AnimationMixer(model);
var modelaction = model.animations[0];
if (actionList.length == 0) {
actionList.actionA = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'A', 59, 60));
actionList.actionB = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'B', 129, 130));
actionList.actionC = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'C', 199, 200));
actionList.actionD = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'D', 270, 271));
actionList.actionE = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'E', 399, 400));
actionList.actionF = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'F', 529, 530));
actionList.actionG = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'G', 599, 600));
actionList.actionH = mixer.clipAction(THREE.AnimationUtils.subclip(modelaction, 'H', 668, 669));
}
actionClip = actionList['action' + action];
actionClip.play();
}
这里我把这些切割玩的动画放在了一个actionList中,以后需要调用的时候直接使用就可以了。
3.在界面中添加一些按钮这样就可以直接切换动作了
切换不同的动作。
遇到的问题:一开始我只引用了three.js这个脚本(单独的),在使用THREE.AnimationUtils.subclip这个方法的时候无法执行,代码也不会报错。可能是脚本插件不齐全吧,所以我用了我在vue中下载的比较完整的three的插件。