个人简介
👀个人主页: 前端杂货铺
⚡开源项目: rich-vue3 (基于 Vue3 + TS + Pinia + Element Plus + Spring全家桶 + MySQL)
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍖开源 rich-vue3 🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
内容 | 参考链接 |
---|---|
WebGL专栏 | WebGL 入门 |
Three.js(一) | 创建场景、渲染三维对象、添加灯光、添加阴影、添加雾化 |
Three.js(二) | scene场景、几何体位置旋转缩放、正射投影相机、透视投影相机 |
Three.js(三) | 聚光灯、环境光、点光源、平行光、半球光 |
Three.js(四) | 基础材质、深度材质、法向材质、面材质、朗伯材质、Phong材质、着色器材质、直线和虚线、联合材质 |
Three.js(五) | Three.js——二维平面、二维圆、自定义二维图形、立方体、球体、圆柱体、圆环、扭结、多面体、文字 |
Three.js(六) | Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器 |
文章目录
- 前言
- 骨骼动画
- 骨骼
- 骨架
- 蒙皮网格
- 实现骨骼动画
- 总结
前言
大家好,这里是前端杂货铺。
上篇文章我们学习了 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 |
实现骨骼动画
- 创建圆柱体和 Phong 材质,通过蒙皮网格 SkinnedMesh 把构建好的圆柱添加到场景中
- 创建骨骼系统,以 b1 为基准,连接其他骨骼
- 创建骨架,把骨骼连接起来,添加到 mesh 中,并把 mesh 绑定 skeleton
- 通过顶点位置数据的原始数组构建 索引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官方文档,真心推荐读一读!!
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- Three.js 官方文档
- WebGL+Three.js 入门与实战【作者:慕课网_yancy】