Three.js学习五——让模型沿着轨迹移动
目录
- 流程
- 搭建场景环境
- 添加模型
- 增加运动轨迹
- 让模型沿轨迹运动
- 完整代码和效果
流程
基本流程
1、添加模型
2、增加运动轨迹
3、让模型沿轨迹运动
工程文件结构如下图:
static:存放静态资源文件
three.js-master:为官网下载的代码包,包含所有需要用到的资源包,链接:https://github.com/mrdoob/three.js/archive/master.zip
index.html:页面代码
模型使用的是官方示例中的Soldier模型,文件位置:three.js-master\examples\models\gltf\Soldier.glb
为了方便操作我们将文件拷出来放在上图static\3dmod\gltf文件夹下,static与three.js-master同级
index.html单页代码组成
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>My first three.js app</title><style>body {margin: 0;}</style>
</head><body><script type="importmap">{"imports": {"three": "./three.js-master/build/three.module.js"}}</script><script type="module">// 下文JS代码位置// ...</script>
</body></html>
参照官网例子:https://threejs.org/examples/#webgl_animation_skinning_blending中的场景和模型
搭建场景环境
import * as THREE from "three";
import { OrbitControls } from "./three.js-master/examples/jsm/controls/OrbitControls.js";let scene, camera, renderer;// 渲染器开启阴影渲染:renderer.shadowMapEnabled = true;
// 灯光需要开启“引起阴影”:light.castShadow = true;
// 物体需要开启“引起阴影”和“接收阴影”:mesh.castShadow = mesh.receiveShadow = true;function init() {scene = new THREE.Scene();camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);renderer = new THREE.WebGLRenderer();// position and point the camera to the center of the scenecamera.position.set(5, 5, 5);camera.lookAt(scene.position);// 增加坐标系红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.// 添加坐标系到场景中const axes = new THREE.AxesHelper(20);scene.add(axes);// 调整背景颜色,边界雾化scene.background = new THREE.Color(0xa0a0a0);scene.fog = new THREE.Fog(0xa0a0a0, 10, 30);// 半球形光源const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);hemiLight.position.set(0, 10, 0);scene.add(hemiLight);// 创建一个虚拟的球形网格 Mesh 的辅助对象来模拟 半球形光源 HemisphereLight.const hemiLighthelper = new THREE.HemisphereLightHelper(hemiLight, 5);scene.add(hemiLighthelper);// 地面const mesh = new THREE.Mesh(new THREE.PlaneGeometry(100, 100), new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false }));mesh.rotation.x = - Math.PI / 2;mesh.receiveShadow = true;scene.add(mesh);// 平行光const directionalLight = new THREE.DirectionalLight(0xFFFFFF);directionalLight.castShadow = true;directionalLight.shadow.camera.near = 0.5;directionalLight.shadow.camera.far = 50;directionalLight.shadow.camera.left = -10;directionalLight.shadow.camera.right = 10;directionalLight.shadow.camera.top = 10;directionalLight.shadow.camera.bottom = -10;directionalLight.position.set(0, 5, 5);scene.add(directionalLight);// 用于模拟场景中平行光 DirectionalLight 的辅助对象. 其中包含了表示光位置的平面和表示光方向的线段.const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5);scene.add(directionalLightHelper);renderer.shadowMap.enabled = true;renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 控制器const controls = new OrbitControls(camera, renderer.domElement);
}
// 渲染
function animate() {requestAnimationFrame(animate);renderer.render(scene, camera);
};
这里是添加了几个辅助对象,方便找到光照和场景坐标位置
添加模型
这里我们直接导入模型,在《Three.js学习四——模型导入》中有相对详细的介绍
let model = null;
function loadModel() {// 加载模型并开启阴影和接受阴影const gltfLoader = new GLTFLoader();gltfLoader.setPath('./static/3dmod/gltf/').load('Soldier.glb', function (gltf) {// gltf.scene.rotation.y = Math.PI;// console.log("gltf", gltf)gltf.scene.scale.set(1, 1, 1)gltf.scene.traverse(function (object) {if (object.isMesh) {object.castShadow = true; //阴影object.receiveShadow = true; //接受别人投的阴影}});scene.add(gltf.scene);model = gltf.scene;}, function (res) {// console.log(res.total, res.loaded)});
}
增加运动轨迹
用到了Three.js提供的CatmullRomCurve3:使用Catmull-Rom算法, 从一系列的点创建一条平滑的三维样条曲线。
let curve = null;
function makeCurve() {//Create a closed wavey loopcurve = new THREE.CatmullRomCurve3([new THREE.Vector3(0, 0, 0),new THREE.Vector3(5, 0, 0),new THREE.Vector3(0, 0, 5)]);curve.curveType = "catmullrom";curve.closed = true;//设置是否闭环curve.tension = 0.5![请添加图片描述](https://img-blog.csdnimg.cn/12a2fa45062d44a58bb7cbf719e4b20f.gif)
; //设置线的张力,0为无弧度折线// 为曲线添加材质在场景中显示出来,不显示也不会影响运动轨迹,相当于一个Helperconst points = curve.getPoints(50);const geometry = new THREE.BufferGeometry().setFromPoints(points);const material = new THREE.LineBasicMaterial({ color: 0x000000 });// Create the final object to add to the sceneconst curveObject = new THREE.Line(geometry, material);scene.add(curveObject)
}
让模型沿轨迹运动
let progress = 0; // 物体运动时在运动路径的初始位置,范围0~1
const velocity = 0.001; // 影响运动速率的一个值,范围0~1,需要和渲染频率结合计算才能得到真正的速率
// 物体沿线移动方法
function moveOnCurve() {if (curve == null || model == null) {console.log("Loading")} else {if (progress <= 1 - velocity) {const point = curve.getPointAt(progress); //获取样条曲线指定点坐标const pointBox = curve.getPointAt(progress + velocity); //获取样条曲线指定点坐标if (point && pointBox) {model.position.set(point.x, point.y, point.z);// model.lookAt(pointBox.x, pointBox.y, pointBox.z); //因为这个模型加载进来默认面部是正对Z轴负方向的,所以直接lookAt会导致出现倒着跑的现象,这里用重新设置朝向的方法来解决。var targetPos = pointBox //目标位置点var offsetAngle = 0 //目标移动时的朝向偏移// //以下代码在多段路径时可重复执行var mtx = new THREE.Matrix4() //创建一个4维矩阵// .lookAt ( eye : Vector3, target : Vector3, up : Vector3 ) : this,构造一个旋转矩阵,从eye 指向 target,由向量 up 定向。mtx.lookAt(model.position, targetPos, model.up) //设置朝向mtx.multiply(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, offsetAngle, 0)))var toRot = new THREE.Quaternion().setFromRotationMatrix(mtx) //计算出需要进行旋转的四元数值model.quaternion.slerp(toRot, 0.2)}progress += velocity;} else {progress = 0;}}};
// moveOnCurve()需要在渲染中一直调用更新,以达到物体移动效果
function animate() {requestAnimationFrame(animate);moveOnCurve();renderer.render(scene, camera);
};
完整代码和效果
完整代码
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>My first three.js app</title><style>body {margin: 0;}</style>
</head><body><script type="importmap">{"imports": {"three": "./three.js-master/build/three.module.js"}}</script><script type="module">import * as THREE from "three";import { OrbitControls } from "./three.js-master/examples/jsm/controls/OrbitControls.js";import { GLTFLoader } from "./three.js-master/examples/jsm/loaders/GLTFLoader.js";let scene, camera, renderer;let curve = null, model = null;let progress = 0; // 物体运动时在运动路径的初始位置,范围0~1const velocity = 0.001; // 影响运动速率的一个值,范围0~1,需要和渲染频率结合计算才能得到真正的速率// 渲染器开启阴影渲染:renderer.shadowMapEnabled = true;// 灯光需要开启“引起阴影”:light.castShadow = true;// 物体需要开启“引起阴影”和“接收阴影”:mesh.castShadow = mesh.receiveShadow = true;function init() {scene = new THREE.Scene();camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);renderer = new THREE.WebGLRenderer();// position and point the camera to the center of the scenecamera.position.set(5, 5, 5);camera.lookAt(scene.position);// 增加坐标系红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.// 添加坐标系到场景中const axes = new THREE.AxesHelper(20);scene.add(axes);// 调整背景颜色,边界雾化scene.background = new THREE.Color(0xa0a0a0);scene.fog = new THREE.Fog(0xa0a0a0, 10, 30);// 半球形光源const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);hemiLight.position.set(0, 10, 0);scene.add(hemiLight);// 创建一个虚拟的球形网格 Mesh 的辅助对象来模拟 半球形光源 HemisphereLight.const hemiLighthelper = new THREE.HemisphereLightHelper(hemiLight, 5);scene.add(hemiLighthelper);// 地面const mesh = new THREE.Mesh(new THREE.PlaneGeometry(100, 100), new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false }));mesh.rotation.x = - Math.PI / 2;mesh.receiveShadow = true;scene.add(mesh);// 平行光const directionalLight = new THREE.DirectionalLight(0xFFFFFF);directionalLight.castShadow = true;directionalLight.shadow.camera.near = 0.5;directionalLight.shadow.camera.far = 50;directionalLight.shadow.camera.left = -10;directionalLight.shadow.camera.right = 10;directionalLight.shadow.camera.top = 10;directionalLight.shadow.camera.bottom = -10;directionalLight.position.set(0, 5, 5);scene.add(directionalLight);// 用于模拟场景中平行光 DirectionalLight 的辅助对象. 其中包含了表示光位置的平面和表示光方向的线段.const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5);scene.add(directionalLightHelper);renderer.shadowMap.enabled = true;renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 控制器const controls = new OrbitControls(camera, renderer.domElement);}function loadModel() {// 加载模型并开启阴影和接受阴影const gltfLoader = new GLTFLoader();gltfLoader.setPath('./static/3dmod/gltf/').load('Soldier.glb', function (gltf) {// gltf.scene.rotation.y = Math.PI;// console.log("gltf", gltf)gltf.scene.scale.set(1, 1, 1)gltf.scene.traverse(function (object) {if (object.isMesh) {object.castShadow = true; //阴影object.receiveShadow = true; //接受别人投的阴影}});scene.add(gltf.scene);model = gltf.scene;}, function (res) {// console.log(res.total, res.loaded)});}function makeCurve() {//Create a closed wavey loopcurve = new THREE.CatmullRomCurve3([new THREE.Vector3(0, 0, 0),new THREE.Vector3(5, 0, 0),new THREE.Vector3(0, 0, 5)]);curve.curveType = "catmullrom";curve.closed = true;//设置是否闭环curve.tension = 0.5; //设置线的张力,0为无弧度折线// 为曲线添加材质在场景中显示出来,不显示也不会影响运动轨迹,相当于一个Helperconst points = curve.getPoints(50);const geometry = new THREE.BufferGeometry().setFromPoints(points);const material = new THREE.LineBasicMaterial({ color: 0x000000 });// Create the final object to add to the sceneconst curveObject = new THREE.Line(geometry, material);scene.add(curveObject)}// 物体沿线移动方法function moveOnCurve() {if (curve == null || model == null) {console.log("Loading")} else {if (progress <= 1 - velocity) {const point = curve.getPointAt(progress); //获取样条曲线指定点坐标const pointBox = curve.getPointAt(progress + velocity); //获取样条曲线指定点坐标if (point && pointBox) {model.position.set(point.x, point.y, point.z);// model.lookAt(pointBox.x, pointBox.y, pointBox.z);//因为这个模型加载进来默认面部是正对Z轴负方向的,所以直接lookAt会导致出现倒着跑的现象,这里用重新设置朝向的方法来解决。var targetPos = pointBox //目标位置点var offsetAngle = 0 //目标移动时的朝向偏移// //以下代码在多段路径时可重复执行var mtx = new THREE.Matrix4() //创建一个4维矩阵// .lookAt ( eye : Vector3, target : Vector3, up : Vector3 ) : this,构造一个旋转矩阵,从eye 指向 target,由向量 up 定向。mtx.lookAt(model.position, targetPos, model.up) //设置朝向mtx.multiply(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, offsetAngle, 0)))var toRot = new THREE.Quaternion().setFromRotationMatrix(mtx) //计算出需要进行旋转的四元数值model.quaternion.slerp(toRot, 0.2)}progress += velocity;} else {progress = 0;}}};function animate() {requestAnimationFrame(animate);moveOnCurve();renderer.render(scene, camera);};init();loadModel();makeCurve();animate();</script>
</body></html>
效果:
Three.js学习五——让模型沿着轨迹移动相关推荐
- Three.js学习七——播放模型动画时模型沿着轨迹移动
目录 效果描述 实现流程 基本流程 工程文件 搭建场景 添加模型和播放动画 添加路径和模型移动 完整代码和实现效果 效果描述 在播放导入的模型动画同时,让模型沿着预定路径轨迹移动.例如导入一个会跑步动 ...
- Node.js学习五(事件)
文章目录 一.Node.js事件循环 二.事件驱动程序 三.事件触发器 1.EventEmitter类 2.事件触发器的方法 (1)EventEmitter类的on方法 (2)EventEmitter ...
- js学习五之常用函数
第一节:JavaScript 全局函数 全局函数不属于任何一个内置对象. JS 包含以下 7 个全局函数,用于一些常用的功能:escape(),eval(),isNan(),isFinite(),pa ...
- JS学习笔记(五)函数类型、箭头函数、arguments参数、标签函数
JS学习笔记(五) 本系列更多文章,可以查看专栏 JS学习笔记 文章目录 JS学习笔记(五) 一.函数 1. 函数定义 2. 方法( 对象 + 函数 ) 二.函数参数及返回值 1. 传递原始类型参数 ...
- three.js学习笔记(十二)——使用Blender自定义模型
这次我们将学习如何用3D软件创建自己的模型 选择软件 有很多软件如Cinema 4D.Maya.3DS Max.Blender.ZBrush.Marmoset Toolbag.Substance Pa ...
- Three.js学习六——模型动画
目录 Three.js动画系统(Animation system) 实现流程 基本流程 工程文件 场景搭建 添加模型 模型动画 动画实现的基本流程 相关对象方法和代码 完整代码和实现效果 Three. ...
- Vue.js 学习笔记 五 常用的事件修饰符
介绍几个常用的事件修饰符 直接上代码 <div id="divApp"><div class="divColor" v-on:click=&q ...
- WebGL three.js学习笔记 6种类型的纹理介绍及应用
WebGL three.js学习笔记 6种类型的纹理介绍及应用 本文所使用到的demo演示: 高光贴图Demo演示 反光效果Demo演示(因为是加载的模型,所以速度会慢) (一)普通纹理 计算机图形学 ...
- HTML5+CSS3的学习(五)
HTML5+CSS3的学习(五) 2018版李立超html+css基础 103集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1sW411T78k?spm_id ...
最新文章
- python3 字符串格式化
- python变量存为matlab,详解如何在python中读写和存储matlab的数据文件(*.mat)
- 什么是多任务、进程、线程
- Linux下高性能网络编程中的几个TCP/IP选项_SO_REUSEADDR、SO_RECVBUF、SO_SNDBUF、SO_KEEPALIVE、SO_LINGER、TCP_CORK、TCP_NODE
- SQL Server DATEPART() 函数
- Your port 80 is actually used by : Server: Microsoft-IIS/10.0 Cannot install the Apache service, p
- ArcGIS实验教程:ArcGIS 10.2手把手图文安装教程(经典版)
- 接触Firefox的xpi
- MacBook快捷键
- jabber服务器搭建
- springboot分层构建Docker镜像实践,统统都会!
- 从0开始,利用docker搭建一套大数据开发环境(一)
- ELGamal算法的编程实现
- nohup sh xxx.sh /dev/null
- Django讲课笔记02:Django环境搭建
- wdm驱动的学习初步
- ubuntu 16.04 镜像下载(国内开源镜像站)
- 关于储备(应急储备、管理储备、储备分析)的总结讨论
- 基于LSTM网络的视觉识别研究与实现——详细版
- 《现代职业教育》期刊简介及投稿要求
热门文章
- 输入框只允许输入数字字母下划线
- 9招教你防止电脑辐射
- Oulipo HDU - 1686--strlen()耗时啊啊啊--KMP
- 使用 VS 2008 编译 ECW SDK 3.3
- ubuntu 拨号上网
- 移动硬盘拷贝linux文件,Linux(CentOS)挂载U盘、移动硬盘以及文件拷贝、备份
- 1 php方式实现购物车原理,PHP购物车实现的原理
- 什么是Hash哈希(散列表)
- c语言中整形变量,C语言基本数据类型:整型(int)用法详解
- 【tableau】4个基本图表