threejs动画

个人博客地址: https://cxx001.gitee.io

前面我们所用的模型大都是静态的,没有动画,没有生命。这节我们将赋予它们生命。

动画本质是通过改变物体的旋转、缩放、位置、材质、顶点、面以及其它你所能想到的属性来实现的。这些其实在前面章节示例里或多或少已经使用了。

一、选择对象

在写示例前我们先了解一个重要操作,如何通过鼠标选中场景中的对象。

当我们在屏幕上点击鼠标时,会发生如下事情:

  1. 首先,基于屏幕上点击位置会创建一个THREE.Vecor3向量。

  2. 接着,使用vector.unproject方法将屏幕上的点击位置转换成Threejs场景中的坐标。

  3. 然后,创建THREE.Raycaster。使用它可以向场景中发射光线。

  4. 最后,我们使用raycaster.intersectObjects方法来判断指定的对象中哪些被该光线照射到了。其结果包含了所有被照射到的对象的信息,这些信息包括:

    distance: 50.66666666666   // 从摄像机到被点击对象的距离
    face: THREE.Face3  		   // 那个面被点击了
    faceIndex: 6               // 被点击面的下标
    object: THREE.Mesh         // 被点击的网格对象
    point: THREE.Vector3       // 网格对象上哪个点被点击了
    
<!-- chapter-09-01.html -->
<!DOCTYPE html>
<html>
<head><title>Selecting objects</title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><script type="text/javascript" src="../libs/Projector.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);var renderer = new THREE.WebGLRenderer();renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));renderer.setSize(window.innerWidth, window.innerHeight);var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});var plane = new THREE.Mesh(planeGeometry, planeMaterial);plane.rotation.x = -0.5 * Math.PI;plane.position.x = 15;plane.position.y = 0;plane.position.z = 0;scene.add(plane);var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);cube.position.x = -9;cube.position.y = 3;cube.position.z = 0;scene.add(cube);var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);sphere.position.x = 20;sphere.position.y = 0;sphere.position.z = 2;scene.add(sphere);var cylinderGeometry = new THREE.CylinderGeometry(2, 2, 20);var cylinderMaterial = new THREE.MeshLambertMaterial({color: 0x77ff77});var cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial);cylinder.position.set(0, 0, 1);scene.add(cylinder);camera.position.x = -30;camera.position.y = 40;camera.position.z = 30;camera.lookAt(scene.position);var ambientLight = new THREE.AmbientLight(0x0c0c0c);scene.add(ambientLight);var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(-40, 60, -10);scene.add(spotLight);document.getElementById("WebGL-output").appendChild(renderer.domElement);var tube;var controls = new function () {this.showRay = false;};var gui = new dat.GUI();gui.add(controls, 'showRay').onChange(function (e) {if (tube) scene.remove(tube)});function render() {stats.update();renderer.render(scene, camera);requestAnimationFrame(render);}render();// 监控鼠标按下和鼠标移动事件document.addEventListener('mousedown', onDocumentMouseDown, false);document.addEventListener('mousemove', onDocumentMouseMove, false);function onDocumentMouseDown(event) {// 1.鼠标点击位置var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);// 2.把鼠标点击位置转换为threejs场景中的坐标vector = vector.unproject(camera);// 3.从摄像机位置向点击位置发射一条光线var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());// 4. 获取指定数组里对象哪些被光线照射到了var intersects = raycaster.intersectObjects([sphere, cylinder, cube]);if (intersects.length > 0) {console.log(intersects[0]);intersects[0].object.material.transparent = true;intersects[0].object.material.opacity = 0.5;}}function onDocumentMouseMove(event) {// 是否显示HREE.Raycaster射线if (controls.showRay) {var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);vector = vector.unproject(camera);var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());var intersects = raycaster.intersectObjects([sphere, cylinder, cube]);if (intersects.length > 0) {var points = [];points.push(new THREE.Vector3(-30, 39.8, 30));points.push(intersects[0].point);var mat = new THREE.MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: 0.6});var tubeGeometry = new THREE.TubeGeometry(new THREE.SplineCurve3(points), 60, 0.001);if (tube) scene.remove(tube);tube = new THREE.Mesh(tubeGeometry, mat);scene.add(tube);}}}function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>

下面回归正题,我们重新回到动画上来。


二、使用Tween.js实现动画

Tween.js是一个轻量级的javascript库 https://github.com/tweenjs/tween.js,通过这个库可以很容易实现某个属性在两个值之间进行过渡,我们称这种为补间动画。官方提供了很多示例,自行去查看。

<!-- chapter-09-02.html -->
<!DOCTYPE html>
<html>
<head><title>Animation tween </title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/ColladaLoader.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><script type="text/javascript" src="../libs/tween.min.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);var webGLRenderer = new THREE.WebGLRenderer();webGLRenderer.setClearColor(new THREE.Color(0xcccccc, 1.0));webGLRenderer.setSize(window.innerWidth, window.innerHeight);webGLRenderer.shadowMapEnabled = true;camera.position.x = 150;camera.position.y = 150;camera.position.z = 150;camera.lookAt(new THREE.Vector3(0, 20, 0));var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(150, 150, 150);spotLight.intensity = 2;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);var mesh;var loader = new THREE.ColladaLoader();loader.load("../assets/models/dae/Truck_dae.dae", function (result) {mesh = result.scene.children[0].children[0].clone();//mesh.scale.set(4, 4, 4);scene.add(mesh);// 补间动画要在模型加载完后才能开始tween.start();  });// 使用补间动画, 在y轴上下缓动移动// http://sole.github.io/tween.js/examples/03_graphs.htmlvar posSrc = {pos: 10};var tween = new TWEEN.Tween(posSrc).to({pos: 0}, 1000);tween.easing(TWEEN.Easing.Sinusoidal.InOut);var tweenBack = new TWEEN.Tween(posSrc).to({pos: 10}, 1000);tweenBack.easing(TWEEN.Easing.Sinusoidal.InOut);// 衔接两个动作,循环执行tween.chain(tweenBack);tweenBack.chain(tween);// 执行后坐标更新var onUpdate = function () {var pos = this.pos;mesh.position.y = pos;};tween.onUpdate(onUpdate);tweenBack.onUpdate(onUpdate);function render() {stats.update();// 更新补间动画TWEEN.update();requestAnimationFrame(render);webGLRenderer.render(scene, camera);}render();function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>


三、使用摄像机实现动画

前面我们已经接触过了,可以通过移动摄像机的位置让整个场景动起来,我们是通过手动改变摄像机位置实现的。Threejs还提供了其它更新摄像机的控件,下面我们依次看看。

最常用的:

其它的本节不会介绍,使用和上面一样,了解下:

1. 轨迹球控制器

下面是使用这个控制器,通过鼠标左键拖动、滚轮、右键拖动分别实现场景的旋转、缩放、平移的示例。

<!-- chapter-09-03.html -->
<!DOCTYPE html>
<html>
<head><title>Trackball controls </title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/OBJLoader.js"></script><script type="text/javascript" src="../libs/MTLLoader.js"></script><script type="text/javascript" src="../libs/OBJMTLLoader.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><script type="text/javascript" src="../libs/chroma.js"></script><script type="text/javascript" src="../libs/TrackballControls.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);var webGLRenderer = new THREE.WebGLRenderer();webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));webGLRenderer.setSize(window.innerWidth, window.innerHeight);webGLRenderer.shadowMapEnabled = true;camera.position.x = 100;camera.position.y = 100;camera.position.z = 300;camera.lookAt(new THREE.Vector3(0, 0, 0));var ambientLight = new THREE.AmbientLight(0x383838);scene.add(ambientLight);var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(300, 300, 300);spotLight.intensity = 1;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);// 创建轨迹球控制器,并绑定摄像机var trackballControls = new THREE.TrackballControls(camera);// 想要全面了解这些属性可以参考rackballControls.js源文件trackballControls.rotateSpeed = 1.0;trackballControls.zoomSpeed = 1.0;trackballControls.panSpeed = 1.0;//trackballControls.noZoom=false;//trackballControls.noPan=false;trackballControls.staticMoving = true;//trackballControls.dynamicDampingFactor=0.3;// 加载OBJ/MTL模型var mesh;var loader = new THREE.OBJMTLLoader();var texture = THREE.ImageUtils.loadTexture('../assets/textures/Metro01.JPG');//texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;loader.load('../assets/models/city.obj', '../assets/models/city.mtl', function (object) {var scale = chroma.scale(['red', 'green', 'blue']);setRandomColors(object, scale);mesh = object;scene.add(mesh);});//texture.repeat.set( 1 , 1);function setRandomColors(object, scale) {var children = object.children;if (children && children.length > 0) {children.forEach(function (e) {setRandomColors(e, scale)});} else {if (object instanceof THREE.Mesh) {object.material.color = new THREE.Color(scale(Math.random()).hex());if (object.material.name.indexOf("building") == 0) {object.material.emissive = new THREE.Color(0x444444);object.material.transparent = true;object.material.opacity = 0.8;}}}}var clock = new THREE.Clock();function render() {stats.update();var delta = clock.getDelta();trackballControls.update(delta);  // delta距离上次调用时间间隔requestAnimationFrame(render);webGLRenderer.render(scene, camera)}render();function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>

2. 飞行控制器

可行像飞行视角一样在场景中飞行。

// 创建飞行控制器
var flyControls = new THREE.FlyControls(camera);
flyControls.movementSpeed = 25;
flyControls.domElement = document.querySelector("#WebGL-output");
flyControls.rollSpeed = Math.PI / 24;
flyControls.autoForward = true;
flyControls.dragToLook = false;// 更新
var clock = new THREE.Clock();
function render() {var delta = clock.getDelta();flyControls.update(delta);webGLRenderer.clear();requestAnimationFrame(render);webGLRenderer.render(scene, camera)
}

3. 翻滚控制器

翻滚场景(Q/E 左/右翻滚)。

// 创建翻滚控制器
var rollControls = new THREE.RollControls(camera);
rollControls.movementSpeed = 25;
rollControls.lookSpeed = 3;// 更新
var clock = new THREE.Clock();
function render() {var delta = clock.getDelta();rollControls.update(delta);webGLRenderer.clear();requestAnimationFrame(render);webGLRenderer.render(scene, camera)
}

4. 第一视角控制器

类似第一视角射击游戏那样控制摄像机。鼠标用于控制视角,键盘用于控制角色移动。

// 创建第一视角控制器
var camControls = new THREE.FirstPersonControls(camera);
camControls.lookSpeed = 0.4;
camControls.movementSpeed = 20;
camControls.noFly = true;
camControls.lookVertical = true;
camControls.constrainVertical = true;
camControls.verticalMin = 1.0;
camControls.verticalMax = 2.0;
camControls.lon = -150;
camControls.lat = 120;// 更新
var clock = new THREE.Clock();
function render() {var delta = clock.getDelta();rollControls.update(delta);webGLRenderer.clear();requestAnimationFrame(render);webGLRenderer.render(scene, camera)
}

5. 轨道控制器

和轨迹控制器类似,只是轨道控制器强制摄像头 up方向,而轨迹控制器允许相机倒置旋转。可以用于控制场景中的对象围绕场景中心旋转和平移。

<!-- chapter-09-04.html -->
<!DOCTYPE html>
<html>
<head><title>Orbit controls</title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><script type="text/javascript" src="../libs/OrbitControls.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);var webGLRenderer = new THREE.WebGLRenderer();webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));webGLRenderer.setSize(window.innerWidth, window.innerHeight);webGLRenderer.shadowMapEnabled = true;// 创建球体网格对象var sphere = createMesh(new THREE.SphereGeometry(20, 40, 40));scene.add(sphere);camera.position.x = -20;camera.position.y = 30;camera.position.z = 40;camera.lookAt(new THREE.Vector3(00, 0, 0));var ambiLight = new THREE.AmbientLight(0x111111);scene.add(ambiLight);var spotLight = new THREE.DirectionalLight(0xffffff);spotLight.position.set(-20, 30, 40);spotLight.intensity = 1.5;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);// 使用外部纹理材质创建球体网格function createMesh(geom) {var planetTexture = THREE.ImageUtils.loadTexture("../assets/textures/planets/mars_1k_color.jpg");var normalTexture = THREE.ImageUtils.loadTexture("../assets/textures/planets/mars_1k_normal.jpg");// 加载外部图片纹理创建Phong材质var planetMaterial = new THREE.MeshPhongMaterial({map: planetTexture, bumpMap: normalTexture});var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [planetMaterial]);return mesh;}// 创建轨道控制器var orbitControls = new THREE.OrbitControls(camera);orbitControls.autoRotate = true;var clock = new THREE.Clock();function render() {stats.update();var delta = clock.getDelta();orbitControls.update(delta);requestAnimationFrame(render);webGLRenderer.render(scene, camera);}render();function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>


四、高级动画

1. 关键帧动画

就是从动画开始到动画结束之间所有的顶点数据都会被存储下来,你需要做的就是定义好关键位置,并重复该过程。这种主要一个缺点就是对于大型网格和大型动画,模型文件会变得非常大。

Threejs库提供的使用关键字动画对象:THREE.MorphAnimMesh

<!-- chapter-09-05.html -->
<!DOCTYPE html>
<html>
<head><title>Working with morph targets</title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);var webGLRenderer = new THREE.WebGLRenderer();webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));webGLRenderer.setSize(window.innerWidth, window.innerHeight);webGLRenderer.shadowMapEnabled = true;camera.position.x = 250;camera.position.y = 250;camera.position.z = 350;camera.lookAt(new THREE.Vector3(100, 50, 0));var spotLight = new THREE.DirectionalLight(0xffffff);spotLight.position.set(300, 200, 300);spotLight.intensity = 1;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);var controls = new function () {this.keyframe = 0;  // 关键帧位置};var gui = new dat.GUI();gui.add(controls, "keyframe", 0, 15).step(1).onChange(function (e) {showFrame(e);});var meshAnim;var frames = [];var currentMesh;var loader = new THREE.JSONLoader();// 1. 加载帧动画模型loader.load('../assets/models/horse.js', function (geometry, mat) {// 创建左边静态马网格var mat = new THREE.MeshLambertMaterial({morphTargets: true,  // 设置为true网格才会动vertexColors: THREE.FaceColors});var mesh = new THREE.Mesh(geometry, mat);mesh.position.x = -100;frames.push(mesh);currentMesh = mesh;morphColorsToFaceColors(geometry);  // 修改各面颜色// 创建动画所有关键帧网格var mat2 = new THREE.MeshLambertMaterial({color: 0xffffff, vertexColors: THREE.FaceColors});// 遍历动画所有帧,并创建为网格对象添加到frames数值中mesh.geometry.morphTargets.forEach(function (e) {var geom = new THREE.Geometry();geom.vertices = e.vertices;geom.faces = geometry.faces;var morpMesh = new THREE.Mesh(geom, mat2);frames.push(morpMesh);morpMesh.position.x = -100;});// 确保运动时光照、阴影和颜色的准确性geometry.computeVertexNormals();geometry.computeFaceNormals();geometry.computeMorphNormals();// 2. THREE.MorphAnimMesh创建帧动画网格,右边运动马meshAnim = new THREE.MorphAnimMesh(geometry, mat);meshAnim.duration = 1000;meshAnim.position.x = 200;meshAnim.position.z = 0;scene.add(meshAnim);// 用第一帧创建右边的静态马showFrame(0);}, '../assets/models');// 手的改变帧播放function showFrame(idx) {scene.remove(currentMesh);scene.add(frames[idx]);currentMesh = frames[idx];console.log(currentMesh);}function morphColorsToFaceColors(geometry) {if (geometry.morphColors && geometry.morphColors.length) {var colorMap = geometry.morphColors[0];for (var i = 0; i < colorMap.colors.length; i++) {geometry.faces[i].color = colorMap.colors[i];geometry.faces[i].color.offsetHSL(0, 0.3, 0);}}}var clock = new THREE.Clock();function render() {stats.update();var delta = clock.getDelta();webGLRenderer.clear();if (meshAnim) {// 3. 帧动画更新meshAnim.updateAnimation(delta * 1000);meshAnim.rotation.y += 0.01;  }requestAnimationFrame(render);webGLRenderer.render(scene, camera);}render();function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>

在这里插入图片描述

2. 骨骼动画

就是定义好网格的骨骼,并将顶点绑定到特定的骨骼上。当骨骼运动时,相连骨骼也会随着一起变换,同时骨骼上绑定的顶点也会随之移动。

Threejs库提供的创建骨骼网格的对象:THREE.SkinnedMesh。下面是一个手动变换骨骼对象的示例:

<!-- chapter-09-06.html -->
<!DOCTYPE html>
<html>
<head><title>Load blender model </title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><script type="text/javascript" src="../libs/tween.min.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);var webGLRenderer = new THREE.WebGLRenderer();webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));webGLRenderer.setSize(window.innerWidth, window.innerHeight);webGLRenderer.shadowMapEnabled = true;camera.position.x = 0;camera.position.y = 0;camera.position.z = 4;camera.lookAt(new THREE.Vector3(0, 0, 0));var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(0, 50, 30);spotLight.intensity = 2;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);var mesh;var loader = new THREE.JSONLoader();// 1. 加载Blender导出的骨骼模型loader.load('../assets/models/hand-1.js', function (geometry, mat) {var mat = new THREE.MeshLambertMaterial({color: 0xF0C8C9, skinning: true}); // skinning要设置为true,否则骨骼不会动// 2. 创建骨骼模型网格mesh = new THREE.SkinnedMesh(geometry, mat);mesh.rotation.x = 0.5 * Math.PI;mesh.rotation.z = 0.7 * Math.PI;scene.add(mesh);// 开始运行tween.start();}, '../assets/models');// 3. 手动改变骨骼变换var onUpdate = function () {var pos = this.pos;console.log(mesh.skeleton);mesh.skeleton.bones[5].rotation.set(0, 0, pos);mesh.skeleton.bones[6].rotation.set(0, 0, pos);mesh.skeleton.bones[10].rotation.set(0, 0, pos);mesh.skeleton.bones[11].rotation.set(0, 0, pos);mesh.skeleton.bones[15].rotation.set(0, 0, pos);mesh.skeleton.bones[16].rotation.set(0, 0, pos);mesh.skeleton.bones[20].rotation.set(0, 0, pos);mesh.skeleton.bones[21].rotation.set(0, 0, pos);mesh.skeleton.bones[1].rotation.set(pos, 0, 0);};var tween = new TWEEN.Tween({pos: -1}).to({pos: 0}, 3000).easing(TWEEN.Easing.Cubic.InOut) // 缓动.yoyo(true)   		// 设置为true,使得动画运动完后反着运行.repeat(Infinity)  // 重复运行.onUpdate(onUpdate);var clock = new THREE.Clock();function render() {stats.update();TWEEN.update();var delta = clock.getDelta();requestAnimationFrame(render);webGLRenderer.render(scene, camera);}render();function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>


五、使用外部模型创建动画

1. 使用Blender创建骨骼动画

Blender使用threejs导出器,把.blend动画导出为.json,然后加载并播放动画。

播放外部骨骼动画使用:THREE.Animation

<!-- chapter-09-07.html -->
<!DOCTYPE html>
<html>
<head><title>Animation from blender</title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);var webGLRenderer = new THREE.WebGLRenderer();webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));webGLRenderer.setSize(window.innerWidth, window.innerHeight);webGLRenderer.shadowMapEnabled = true;camera.position.x = 0;camera.position.y = 0;camera.position.z = 4;camera.lookAt(new THREE.Vector3(0, 0, 0));var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(0, 50, 30);spotLight.intensity = 2;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);var mesh;var helper;var controls = new function () {this.showHelper = false;  // 是否显示骨骼骨架};var gui = new dat.GUI();gui.add(controls, 'showHelper').onChange(function (state) {helper.visible = state;});var loader = new THREE.JSONLoader();// 加载骨骼动画模型loader.load('../assets/models/hand-2.js', function (model, mat) {var mat = new THREE.MeshLambertMaterial({color: 0xF0C8C9, skinning: true});mesh = new THREE.SkinnedMesh(model, mat);mesh.rotation.x = 0.5 * Math.PI;mesh.rotation.z = 0.7 * Math.PI;scene.add(mesh);// 创建骨骼动画对象,并开始播放var animation = new THREE.Animation(mesh, model.animation);animation.play();// 创建骨骼骨架helper = new THREE.SkeletonHelper(mesh);helper.material.linewidth = 2;helper.visible = false;scene.add(helper);}, '../assets/models');var clock = new THREE.Clock();function render() {stats.update();var delta = clock.getDelta();if (mesh) {// 更新骨骼动画helper.update();THREE.AnimationHandler.update(delta);}requestAnimationFrame(render);webGLRenderer.render(scene, camera);}render();function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>

2. 从Collada模型加载动画

使用方式和上面一样,只是加载模型文件格式变了。

<!-- chapter-09-08.html -->
<!DOCTYPE html>
<html>
<head><title>Animation from collada</title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><script type="text/javascript" src="../libs/ColladaLoader.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head>
<body><div id="Stats-output">
</div>
<div id="WebGL-output">
</div><script type="text/javascript">function init() {var stats = initStats();var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);var webGLRenderer = new THREE.WebGLRenderer();webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));webGLRenderer.setSize(window.innerWidth, window.innerHeight);webGLRenderer.shadowMapEnabled = true;camera.position.x = 400;camera.position.y = 50;camera.position.z = 150;camera.lookAt(new THREE.Vector3(0, 0, 0));var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(300, 500, 100);spotLight.intensity = 3;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);var meshAnim;// 加载.dae模型var loader = new THREE.ColladaLoader();loader.load('../assets/models/monster.dae', function (collada) {// 模型第一帧网格添加进场景var child = collada.skins[0];scene.add(child);child.scale.set(0.15, 0.15, 0.15);child.rotation.x = -0.5 * Math.PI;child.position.x = -100;child.position.y = -60;// 创建动画并开始播放var animation = new THREE.Animation(child, child.geometry.animation);animation.play();});var clock = new THREE.Clock();function render() {stats.update();var delta = clock.getDelta();// 动画更新THREE.AnimationHandler.update(delta);requestAnimationFrame(render);webGLRenderer.render(scene, camera);}render();function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}window.onload = init;
</script>
</body>
</html>

在这里插入图片描述

3. 其它

整的来说动画就两类:帧动画和骨骼动画,使用方法都差不多。其它动画文件格式加载和使用可以查看官方示例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/4211.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

win11,win10睡眠自动被唤醒部分总结

网上查了很多的解决方法&#xff0c;试了关闭 启用快速启动&#xff0c;大致有几点 1. powercfg /lastwake cmd命令行输入powercfg /lastwake&#xff0c;可以查询最后一次被哪个设备唤醒 2. PowerCfg -DEVICEQUERY wake_armed 也是命令行输入&#xff0c;查询所有可以唤醒电…

基于Thinkphp6框架全新UI的AI网址导航系统源码

2023全新UI的AI网址导航系统源码&#xff0c;基于thinkphp6框架开发的 AI 网址导航是一个非常实用的工具&#xff0c;它能够帮助用户方便地浏览和管理自己喜欢的网站。 相比于其他的 AI 网址导航&#xff0c;这个项目使用了更加友好和易用的 ThinkPHP 框架进行搭建&#xff0c…

学习系统编程No.25【核心转储实战】

引言&#xff1a; 北京时间&#xff1a;2023/6/16/8:39&#xff0c;实训课中&#xff0c;大一下学期最后有课的一天&#xff0c;还有两天就要期末考啦&#xff01;目前什么都还没有复习&#xff0c;不到星期天晚上&#xff0c;咱不慌&#xff0c;小小挂科&#xff0c;岂能拦得…

系列二、Maven下载安装配置

一、下载 链接&#xff1a;https://pan.baidu.com/s/1BvwLzAk9kRSP-daxSYe4Vw?pwdyyds 提取码&#xff1a;yyds 二、安装 第一步&#xff1a;下载安装包 第二步&#xff1a;解压至安装目录&#xff0c;例如 第三步&#xff1a;配置settings.xml&#xff08;主要配置maven本…

STM32F407 滴答定时器

介绍STM32F407滴答定时器配置方法、使用方式&#xff0c;封装延时函数得到精确的时间。 【1】介绍滴答定时器的章节 STM32F407参考手册中第10章介绍了滴答定时器的校准值。 M4权威指南介绍滴答定时器的章节&#xff0c;M3权威指南中与M4权威指南中的介绍一样。 【2】滴答定时…

Linux--打印内容或者插入内容:echo

语法&#xff1a; echo 内容 作用&#xff1a;打印内容到显示器echo 内容 > 不存在的文件 作用&#xff1a;文件创建&#xff0c;并将内容插入新创建的文件中echo 内容 > 存在的文件 作用&#xff1a;覆盖文件原有的内容 echo 内容 >> 存在的文件 作用&#xff1a…

Tdengine 时序数据库-安装与客户端连接

使用 TDengine 时序数据库的版本是 2.4.0.0 使用的安装RPM的安装方便安装 TDengine-server-2.4.0.0-Linux-x64.rpm 1. 安装指令: rpm -ivh TDengine-server-2.4.0.0-Linux-x64.rpm [rootnode3 server]# rpm -ivh TDengine-server-2.4.0.0-Linux-x64.rpm Verifying... …

Java对称与非对称加密解密(AES与RSA)

尽可能预想所有残酷的可能性、因为现实永远让你无法预警,而且又吝于给人慈悲。——富坚义博 今天我们讨论一下秘钥这个东西 一、对称加密技术与非对称加密技术简述 加密技术可以分为对称与非对称两种。 对称加密、解密即加密与解密用的是同一把秘钥,常用的对称加密技术有DES,A…

微服务springcloud 10.config配置中心框架和rabbitmq的安装

config配置中心的作用&#xff1a;项目的yml 配置文件保存到 git 服务器&#xff0c;例如 github.com 或 gitee.com 微服务启动时&#xff0c;从服务器获取配置文件 1.新建 “Project”,命名为 config。注意这里的不是maven项目&#xff0c;而是project 2.将sp02,sp03,sp04,s…

Docker: 改变容器化世界的革命性技术

目录 1.1什么是虚拟化 1.2什么是Docker 1.3容器与虚拟机的比较 1.4Docker组建 2、Docker安装 2.2设置ustc的镜像 2.3Docker的启动与停止 3、docker常用命令 3.1镜像 3.2容器相关命令 1.1什么是虚拟化 在计算机中&#xff0c;虚拟化&#xff08;Vitualization&#x…

netty学习(1):1个客户端与服务器通信

1. 新建maven工程&#xff0c;添加netty依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"…

Spring MVC

Spring MVC &#x1f50e;什么是 Spring MVCMVC对比 MVC 与 Spring MVC &#x1f50e;Spring MVC—连接RequestMapping 默认情况下支持的请求类型RequestMapping 指定请求类型 &#x1f50e;Spring MVC—获取参数获取单个参数获取两个参数参数重命名传递对象传递 JSON 对象获取…