ThreeJS 官方案例学习(webgl_animation_skinning_blending)
1.效果图
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c1baf25dd0b543abb41def465b8dcb48.jpeg#pic_center)
2.源码
<template>
<div>
<div id="container"></div>
</div>
</template>
<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
import Stats from 'three/examples/jsm/libs/stats.module.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import gsap from 'gsap';
let idleAction, walkAction, runAction;
export default {
data() {
return {
container: null,
scene: null,
camera: null,
renderer: null,
controller: null,
stats: null,
mixer: null,
model: null,
skeleton: null,
settings: {},
actions: null,
panel: null,
singleStepMode: false,
clock: new THREE.Clock()
};
},
mounted() {
this.init()
window.addEventListener("resize", this.onWindowSize)
},
beforeUnmount() {
console.log('beforeUnmount===============');
this.container = null
this.scene = null
this.camera = null
this.renderer = null
this.controller = null
this.stats = null
this.mixer = null
this.model = null
this.skeleton = null
this.settings = {}
this.actions = null
this.panel = null
},
methods: {
init() {
this.container = document.getElementById('container')
this.setScene()
this.setCamera()
this.setRenderer()
this.setController()
this.addHelper()
this.setLight()
this.addGround()
this.setGltfLoader()
this.addStatus()
},
setScene() {
this.scene = new THREE.Scene()
this.scene.background = new THREE.Color(0xa0a0a0);
this.scene.fog = new THREE.Fog(0xa0a0a0, 10, 50);
},
setCamera() {
this.camera = new THREE.PerspectiveCamera(45, this.container.clientWidth / this.container.clientHeight, 1, 100)
this.camera.position.set(1, 2, -3)
this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
this.camera.updateProjectionMatrix();
this.camera.lookAt(0, 1, 0)
this.scene.add(this.camera)
},
setRenderer() {
this.renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true,
})
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.shadowMap.enabled = true;
this.container.appendChild(this.renderer.domElement);
},
setController() {
this.controller = new OrbitControls(this.camera, this.renderer.domElement);
this.controller.enableDamping = true;
this.controller.dampingFactor = 0.04;
this.controller.target.set(0, 1, 0);
},
setLight() {
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3);
hemiLight.position.set(0, 20, 0);
this.scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 3);
dirLight.position.set(- 3, 10, - 10);
dirLight.castShadow = true;
dirLight.shadow.camera.top = 2;
dirLight.shadow.camera.bottom = - 2;
dirLight.shadow.camera.left = - 2;
dirLight.shadow.camera.right = 2;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 40;
this.scene.add(dirLight);
},
addGround() {
const mesh = new THREE.Mesh(
new THREE.PlaneGeometry(100, 100),
new THREE.MeshPhongMaterial({
color: 0xcbcbcb,
depthWrite: false
})
)
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
this.scene.add(mesh)
},
setGltfLoader() {
let that = this
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
loader.load('/model/gltf/Soldier.glb', (gltf) => {
that.model = gltf.scene
that.scene.add(that.model)
that.model.traverse(function (object) {
if (object.isMesh) {
object.castShadow = true
}
})
that.skeleton = new THREE.SkeletonHelper(that.model);
that.skeleton.visible = false;
that.scene.add(that.skeleton);
that.createPanel()
const animations = gltf.animations
that.mixer = new THREE.AnimationMixer(that.model)
idleAction = that.mixer.clipAction(animations[0])
walkAction = that.mixer.clipAction(animations[3]);
runAction = that.mixer.clipAction(animations[1]);
that.actions = [idleAction, walkAction, runAction];
that.activateAllActions();
that.animate();
}, undefined, (err => {
console.error(err)
}))
},
createPanel() {
this.panel = new GUI({ width: 310 })
const floder1 = this.panel.addFolder('Visibility')
const floder2 = this.panel.addFolder('Activation/Deactivation')
const folder3 = this.panel.addFolder('Pausing/Stepping');
const folder4 = this.panel.addFolder('Crossfading');
const folder5 = this.panel.addFolder('Blend Weights');
const folder6 = this.panel.addFolder('General Speed');
this.settings = {
'show model': true,
'show skeleton': false,
'deactive all': this.deactivateAllActions,
'activate all': this.activateAllActions,
'pause/continue': this.pauseContinue,
'modify idle weight': 0.0,
'modify walk weight': 1.0,
'modify run weight': 0.0,
}
floder1.add(this.settings, 'show model').onChange(this.showModel)
floder1.add(this.settings, 'show skeleton').onChange(this.showSkeleton)
floder2.add(this.settings, 'deactive all')
floder2.add(this.settings, 'activate all')
folder5.add(this.settings, 'modify idle weight', 0.0, 1.0, 0.01).listen().onChange((weight) => {
this.setWeight(idleAction, weight)
})
folder5.add(this.settings, 'modify walk weight', 0.0, 1.0, 0.01).listen().onChange((weight) => {
this.setWeight(walkAction, weight)
})
folder5.add(this.settings, 'modify run weight', 0.0, 1.0, 0.01).listen().onChange((weight) => {
this.setWeight(runAction, weight)
})
},
showModel(visibility) {
this.model.visible = visibility;
},
showSkeleton(visibility) {
this.skeleton.visible = visibility
},
deactivateAllActions() {
this.actions.forEach(action => {
action.stop()
});
},
activateAllActions() {
this.setWeight(idleAction, this.settings['modify idle weight']);
this.setWeight(walkAction, this.settings['modify walk weight']);
this.setWeight(runAction, this.settings['modify run weight']);
this.actions.forEach(function (action) {
action.play();
});
},
pauseContinue() {
},
setWeight(action, weight) {
action.enabled = true
action.setEffectiveTimeScale(1);
action.setEffectiveWeight(weight);
},
addHelper() {
let helper = new THREE.CameraHelper(this.camera);
let axisHelper = new THREE.AxesHelper(150);
this.scene.add(axisHelper);
let gridHelper = new THREE.GridHelper(100, 30, 0x2C2C2C, 0x888888);
},
setPMREMGenerator() {
const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
this.scene.background = new THREE.Color(0xbfe3dd);
this.scene.environment = pmremGenerator.fromScene(new RoomEnvironment(this.renderer), 0.04).texture;
},
addStatus() {
this.stats = new Stats();
this.container.appendChild(this.stats.dom);
},
onWindowSize() {
this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
this.renderer.setPixelRatio(window.devicePixelRatio)
},
animate() {
const delta = this.clock.getDelta();
if (this.mixer) {
this.mixer.update(delta);
}
requestAnimationFrame(this.animate);
this.controller.update(delta);
this.stats.update();
this.renderer.render(this.scene, this.camera);
},
},
};
</script>
<style>
#container {
position: absolute;
width: 100%;
height: 100%;
}
</style>