three.js(31)-帧动画

2023-01-09  本文已影响0人  姜治宇

帧动画

之前用的gsap动画库可以帮我们做补间动画。也就是说,我们只需定义开始和末尾的状态,这之间的过渡效果动画库会帮我们填充上。
但是,有时我们需要播放一系列自定义的动作,比如我们想做一个小人做体操的动画,我们需要绘制小人的不同动作,也就是说,我们需要自定义帧去存储物体的运动数据。
以一个普通物体为例,我们可以先绘制出这个物体的运动轨迹,然后定义出每一帧的动作,然后连贯起来就可以了。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="http://www.yanhuangxueyuan.com/threejs/build/three.min.js"></script>
    <script src="http://www.yanhuangxueyuan.com/threejs/examples/js/controls/OrbitControls.js"></script>

</head>


<body>
    <div id="webgl"></div>
</body>

</html>
<script>
    //场景
    var scene = new THREE.Scene();

    var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 0, 100);
    scene.add(camera);//添加相机

    //添加坐标轴
    var axes = new THREE.AxesHelper(500);//500表示xyz轴的长度,红:x,绿:y,蓝:z
    scene.add(axes);
    //===========================================================================//
    const box = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshBasicMaterial({ color: 0x0000ff }));
    scene.add(box);
    box.position.set(-10, -50, -50);
    const curve = new THREE.CatmullRomCurve3([
        new THREE.Vector3(-10, -50, -50),
        new THREE.Vector3(10, 0, 0),
        new THREE.Vector3(8, 50, 50),
        new THREE.Vector3(-5, 0, 100)
    ]);
    const points = curve.getPoints(100);//将曲线切分100份,总共是101个顶点
    console.log(points);//101个点
    const geometry = new THREE.BufferGeometry().setFromPoints(points);//创建一个空几何体,用来存储顶点

    const line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0xff00ff }));
    scene.add(line);
    // 声明一个数组用于存储时间序列
    let arr = [];
    for (let i = 0; i < 101; i++) {
        arr.push(i);
    }
    // 生成一个时间序列
    var times = new Float32Array(arr);

    var posArr = [];
    points.forEach(elem => {
        posArr.push(elem.x, elem.y, elem.z);
    });
    // 创建一个和时间序列相对应的位置坐标系列
    var values = new Float32Array(posArr);
    // 创建一个帧动画的关键帧数据,曲线上的位置序列对应一个时间序列
    var posTrack = new THREE.KeyframeTrack('.position', times, values);
    let duration = 101;
    let clip = new THREE.AnimationClip("default", duration, [posTrack]);
    var mixer = new THREE.AnimationMixer(box);

    let AnimationAction = mixer.clipAction(clip);
    AnimationAction.timeScale = 20;
    AnimationAction.play();
    //===========================================================================//
    var renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
    });//画布
    renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染区域尺寸
    renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色

    document.getElementById('webgl').appendChild(renderer.domElement);



    var controls = new THREE.OrbitControls(camera, renderer.domElement);


    window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;

        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);



    });
    var clock = new THREE.Clock();//声明一个时钟对象

    function animate() {

        renderer.render(scene, camera);//开始渲染

        requestAnimationFrame(animate);

        if (mixer) {

            mixer.update(clock.getDelta());
        }
    }

    animate();
</script>
GIF 2023-1-10 11-22-22.gif

模型的帧动画

在枪战小游戏中,敌人中弹倒地到消失,这是一个完整的动作,需要自定义完成。
不过这个工作一般在做模型时就会完成,我们只需播放这个动画即可。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="http://www.yanhuangxueyuan.com/threejs/build/three.min.js"></script>
    <script src="http://www.yanhuangxueyuan.com/threejs/examples/js/controls/OrbitControls.js"></script>
    <script src="http://www.yanhuangxueyuan.com/threejs/examples/js/loaders/GLTFLoader.js"></script>
    <script src="http://www.yanhuangxueyuan.com/threejs/examples/js/loaders/DRACOLoader.js"></script>
</head>

<body>
    <div id="webgl"></div>

</body>

</html>
<script>
    //场景
    var scene = new THREE.Scene();

    var camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 0, 100);
    scene.add(camera);//添加相机

    //添加坐标轴
    var axes = new THREE.AxesHelper(500);//500表示xyz轴的长度,红:x,绿:y,蓝:z
    scene.add(axes);



    addGlb();
    var mixer;//更新帧要用到
    async function addGlb() {
        const loader = new THREE.GLTFLoader();
        const dracoloader = getDracoLoader();
        loader.setDRACOLoader(dracoloader);//注入loader

        const city = await loadGlb('./model/city.glb', loader);

        scene.add(city.scene);
        city.scene.traverse(child => {
            if (child.name === '热气球') {
                
                // 混合动画
                mixer = new THREE.AnimationMixer(child);//初始化
                // 裁剪动画
                const clip = city.animations[0];
                const action = mixer.clipAction(clip);
                action.play();
            }

        });

    }

    function getDracoLoader() {
        //对模型解压
        const dracoloader = new THREE.DRACOLoader();
        dracoloader.setDecoderPath("./draco/");//把examples\jsm\libs\draco这个文件夹复制过来
        dracoloader.setDecoderConfig({ type: "js" })
        dracoloader.preload();
        return dracoloader;
    }
    function loadGlb(filepath, loader) {
        return new Promise((resolve, reject) => {

            loader.setCrossOrigin('Anonymous');//跨域问题
            loader.load(filepath, (gltf) => {
                console.log('gltf>>>', gltf);
                //处理材质丢失的情况
                gltf.scene.traverse(child => {
                    if (child.isMesh) {
                        child.material.emissive = child.material.color;
                        child.material.emissiveMap = child.material.map;
                    }
                });
                resolve(gltf);
            }, undefined, (error) => {

                console.error(error);
                reject(error);

            });
        });
    }
    var renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
    });//画布
    renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染区域尺寸
    renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色

    document.getElementById('webgl').appendChild(renderer.domElement);



    var controls = new THREE.OrbitControls(camera, renderer.domElement);


    window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;

        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);



    });

   

    var clock = new THREE.Clock();
    function animate() {

        renderer.render(scene, camera);//开始渲染
        if(mixer) {
            mixer.update(clock.getDelta());
        }
        requestAnimationFrame(animate);

    }
   animate();

</script>
GIF 2023-1-10.gif
上一篇 下一篇

猜你喜欢

热点阅读