• Three.js——骨骼动画


    个人简介

    👀个人主页: 前端杂货铺
    开源项目: rich-vue3 (基于 Vue3 + TS + Pinia + Element Plus + Spring全家桶 + MySQL)
    🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
    📃个人状态: 研发工程师,现效力于中国工业软件事业
    🚀人生格言: 积跬步至千里,积小流成江海
    🥇推荐学习:🍖开源 rich-vue3 🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js

    🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

    前言

    大家好,这里是前端杂货铺。

    上篇文章我们学习了 tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器。接下来,我们继续我们 three.js 的学习!

    在学习的过程中,如若需要深入了解或扩展某些知识,可以自行查阅 => three.js官方文档。


    骨骼动画

    three.js骨骼动画

    要完成上面视频的效果,我们需要先了解一下下面的新内容:

    骨骼

    Bone 骨骼是 Skeleton(骨架)的一部分。骨架是由 SkinnedMesh(蒙皮网格)依次来使用的。 骨骼几乎和空白 Object3D 相同。

    new THREE.Bone()
    

    骨架

    Skeleton,使用一个 bones 数组来创建一个可以由 SkinnedMesh 使用的骨架。

    new Skeleton( bones : Array, boneInverses : Array )
    
    参数名称描述
    bones包含有一组 bone 的数组,默认值是一个空数组
    boneInverses(可选) 包含 Matrix4 的数组

    蒙皮网格

    SkinnedMesh,具有 Skeleton(骨架)和 bones(骨骼)的网格,可用于给几何体上的顶点添加动画。

    new THREE.SkinnedMesh( geometry : BufferGeometry, material : Material )
    
    参数名称描述
    geometry一个 BufferGeometry 实例
    material(可选)一个Material实例,默认值是一个新的 MeshBasicMaterial

    实现骨骼动画

    1. 创建圆柱体和 Phong 材质,通过蒙皮网格 SkinnedMesh 把构建好的圆柱添加到场景中
    2. 创建骨骼系统,以 b1 为基准,连接其他骨骼
    3. 创建骨架,把骨骼连接起来,添加到 mesh 中,并把 mesh 绑定 skeleton
    4. 通过顶点位置数据的原始数组构建 索引index 和 权重weight,并设置到几何体中
    <!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>
        <style>*{ margin: 0; padding: 0;}</style>
        <script src="../lib/three/three2.js"></script>
    </head>
    
    <body>
    <script type="module">
        // 创建场景
        const scene = new THREE.Scene();
        // 创建相机 视野角度FOV、长宽比、近截面、远截面
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
        // 设置相机位置
        camera.position.set(100, 100, 0);
        camera.lookAt(new THREE.Vector3(0, 0, 0));
    
        // 创建渲染器
        const renderer = new THREE.WebGLRenderer();
        // 设置渲染器尺寸
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
    
        // 添加灯光
        const spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(2000, 8000, 4000);
        scene.add(spotLight);
    
        // 圆柱体,圆柱的顶部半径 | 圆柱的底部半径 | 圆柱的高度 | 圆柱侧面周围的分段数 | 圆柱侧面沿着其高度的分段数 | 圆锥的底面是开放的还是封顶的
        const geometry = new THREE.CylinderGeometry(2, 2, 40, 20, 12, false);
        const material = new THREE.MeshPhongMaterial();
    
        // 蒙皮网格
        const mesh = new THREE.SkinnedMesh(geometry, material);
        scene.add(mesh);
    
        // 创建骨骼系统
        let b1 = new THREE.Bone();
        b1.position.set(0, -20, 0);
    
        let b2 = new THREE.Bone();
        b1.add(b2);
        b2.position.set(0, 10, 0);
    
        let b3 = new THREE.Bone();
        b2.add(b3);
        b3.position.set(0, 10, 0);
    
        let b4 = new THREE.Bone();
        b3.add(b4);
        b4.position.set(0, 10, 0);
    
        let b5 = new THREE.Bone();
        b4.add(b5);
        b5.position.set(0, 10, 0);
    
        // 创建骨架
        const skeleton = new THREE.Skeleton([b1, b2, b3, b4]);
        mesh.add(b1);
        mesh.bind(skeleton);
    
        // 顶点蒙皮的索引
        const index = [];
        // 设置蒙皮的权重
        const weight = [];
    
        // 顶点位置数据的原始数组 [x1, y1, z1, x2, y2, z2, ...]
        const arr = geometry.attributes.position.array;
    
        // 每三个值是一个索引
        for (let i = 0; i < arr.length; i += 3) {
            // 获取 y 坐标
            const y = arr[i + 1] + 20;
    
            // const index = Math.floor(y / 10);
            // y 坐标上每一个骨骼都相同权重
            const weightValue = (y % 10) / 10;
    
            // 索引
            index.push(Math.floor(y / 10), Math.floor(y / 10) + 1, 0, 0);
            // 权重
            weight.push(1 - weightValue, weightValue, 0, 0);
        }
    
        // 设置索引和权重(4个数据为一个顶点)
        geometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(index, 4));
        geometry.setAttribute('skinWeight', new THREE.Float32BufferAttribute(weight, 4));
    
        let step = 0.1;
        const animation = () => {
            // 渲染
            renderer.render(scene, camera);
    
            // 添加边界,往返动画
            if (mesh.skeleton.bones[0].rotation.x > 0.3
                || mesh.skeleton.bones[0].rotation.x < -0.3
            ) {
                step = -step;
            }
            // 控制每个骨骼的旋转
            for (let i = 0; i < mesh.skeleton.bones.length; i++) {
                mesh.skeleton.bones[i].rotation.x += step * Math.PI / 180;
            }
    
            requestAnimationFrame(animation);
        }
    
        animation();
    </script>
    </body>
    
    </html>
    

    总结

    本篇文章我们讲解了如何实现骨骼动画,并了解到了相关知识点。

    更多内容扩展请大家自行查阅 => three.js官方文档,真心推荐读一读!!

    好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


    参考资料:

    1. Three.js 官方文档
    2. WebGL+Three.js 入门与实战【作者:慕课网_yancy】

    在这里插入图片描述


  • 相关阅读:
    使用Spring AI让你的Spring Boot应用快速拥有生成式AI能力
    报错记录:AES加密报错:Illegal key size or default parameters解决方案
    面试理论篇三
    MBR30300VCT-ASEMI肖特基二极管有什么用
    使用Docker部署debezium来监控MySQL数据库
    基于前馈式模糊控制的公路隧道通风系统研究
    深度学习基础知识 学习率调度器的用法解析
    后端八股笔记------框架篇
    【LeetCode】78. 子集
    Vue06/Vue中this.$nextTick( ) 的用法及详细介绍
  • 原文地址:https://blog.csdn.net/qq_45902692/article/details/139395478