上个章节通过加载PMD模型和VMD的动作播放MMD的动画,这节通过js控制让模型实现眨眼,说话。我们还是拿上个模型来操作,首先是创建好Threejs的场景,包括灯光,相机,渲染器等。
initScene(){
this.scene = new THREE.Scene();
this.clock = new THREE.Clock();
const gridHelper = new THREE.PolarGridHelper( 30, 0 );
this.scene.add(gridHelper)
},
initCamera(){
this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
this.camera.position.set(100,100,100);
this.camera.lookAt(0,0,0);
this.listener = new THREE.AudioListener();
this.camera.add( this.listener );
this.scene.add( this.camera );
},
initLight(){
//添加两个平行光
const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);
directionalLight1.position.set(-300,-300,600)
this.scene.add(directionalLight1);
const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.5);
directionalLight2.position.set(600,200,600)
this.scene.add(directionalLight2);
},
initRenderer(){
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.container = document.getElementById("container")
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
this.renderer.setClearColor('#FFFFFF', 1.0);
this.container.appendChild(this.renderer.domElement);
},
initControl(){
this.controls = new OrbitControls(this.camera, this.renderer.domElement);//创建控制器
this.controls.enableDamping = true;
this.controls.maxPolarAngle = Math.PI / 2.2; // // 最大角度
this.controls.target = new THREE.Vector3(0, 0, 0);
this.camera.position.set(100, 100, 100);
this.camera.lookAt(0, 0, 0);
},
initAnimate() {
requestAnimationFrame(this.initAnimate);
this.renderer.render(this.scene, this.camera);
},
然后添加模型,这里不用通过loadWidthAnimation来加载,因为这里不用播放动画,而是直接用load的方法加载,加载好模型后添加到scene场景中。
initBox(){
this.helper = new MMDAnimationHelper();
const loader = new MMDLoader();
let _this = this
loader.load( '/static/animal/miku_v2.pmd',function(object){
let mesh = object;
_this.scene.add(mesh)
},null,null)
},
此时场景中的模型如图:
下面需要添加说话和眨眼的动画了,在模型中有个morphTargetInfluences属性morphTargetInfluences 是一个数组,用于控制模型的形态目标(morph targets)或称为“混合形状”的权重或影响。简单的说就是可以动态控制点的移动形成动画效果,所以我们在加载模型后需要获取到这个数组,不过大家在实践的时候要注意下自己用的模型有没有这个属性以及属性值分别控制的是什么,如果没有合适的模型可以私信我,我把我用的这个模型给你。
_this.face = mesh.morphTargetInfluences
获取到数组后,在动画函数中不断调整值来实现动画效果,比如实现眨眼,那就是要修改一个参数,不断变大,当眼睛闭上时,再延迟一点时间不不断变小把眼睛睁开,这里我直接放代码。
smellEyeAction(){
if(this.smellEye.show) {
if (this.face[8] > 1) {
if (this.smellEye.duration > 20) {
this.smellEye.duration = 0
this.face[8] = 0
} else {
this.smellEye.duration = this.smellEye.duration + 0.1;
}
} else {
this.face[8] = this.face[8] + 0.02;//笑闭眼
}
}
},
说话的效果和眨眼类似,不过说话嘴型是变化较快,且没有太多规则的,所以这里用随机数,让控制嘴巴的点随机变化,
talkAction(){
if(this.talk.show) {
if(this.talk.duration >20){
this.talk.duration = 0;
this.face[24] = Math.random() * 0.6
this.face[25] = Math.random() * 0.6
this.face[26] = Math.random() * 0.6
}
this.talk.duration = this.talk.duration + 1;
}
},
最终要在渲染动画中调用它,为了防止模型没加载出来之前执行,这里加上一点判断,确定数组存在了再执行。
这里不支持上传视频,如果想看动态效果可以私我,我发给你视频