引言
我们在做项目的时候,有时候会遇到物体或者相机需要做复杂轨迹运动的情况,往往没法简单的通过修改位置来达成我们想要的运动效果。
这时候可以通过引入多段曲线去拟合我们想要的运动轨迹,再获取曲线的参数去控制物体做相应轨迹的运动。
目录
1、创建关键空间点数组
2、根据点数组绘制曲线
3、获取曲线上特定位置的点,修改物体位置
4、获取曲线上特定位置的切线,修改物体朝向
5、随时间实时改变物体位置和朝向
6、添加修改曲线功能
7、引入模型模拟应用场景
1、创建关键空间点数组
首先我们可以先找出运动轨迹上几个特定的点。
假设给定的点是(1,1,-1),(1,0,1),(-1,0,1),(-1,0,-1)
这里在每个点放了一个实体方块用于示意点的位置,同时为后面的调整功能做准备
const initialPoints = [
{ x: 1, y: 1, z: -1 },
{ x: 1, y: 0, z: 1 },
{ x: -1, y: 0, z: 1 },
{ x: -1, y: 0, z: -1 }
];
const addCube = (pos) => {
const geometry = new THREE.BoxBufferGeometry(0.1, 0.1, 0.1);
const material = new THREE.MeshBasicMaterial(0xffffff);
const cube = new THREE.Mesh(geometry, material);
cube.position.copy(pos);
scene.add(cube);
}
const cubeList = initialPoints.map(pos => {
return this.addCube(pos);
});
2、根据点数组绘制曲线
three.js 提供了好几种方法绘制曲线,这里采用的是 CatmullRom 插值的方法绘制曲线。
CatmullRom 插值的曲线一定会经过所有给定的点,所以这种方法会更适合用作轨迹曲线的绘制。
const curve = new THREE.CatmullRomCurve3(
cubeList.map((cube) => cube.position) // 直接绑定方块的position以便后续用方块调整曲线
);
curve.curveType = 'chordal'; // 曲线类型
curve.closed = true; // 曲线是否闭合
const points = curve.getPoints(50); // 50等分获取曲线点数组
const line = new THREE.LineLoop(
new THREE.BufferGeometry().setFromPoints(points),
new THREE.LineBasicMaterial({ color: 0x00ff00 })
); // 绘制实体线条,仅用于示意曲线,后面的向量线条同理,相关代码就省略了
scene.add(line);
3、获取曲线上特定位置的点,修改物体位置
有了曲线之后,可以通过 getPointAt 函数获取曲线上特定位置的点向量,然后复制给物体的 position
function changePosition (t) {
const position = curve.getPointAt(t); // t: 当前点在线条上的位置百分比,后面计算
mesh.position.copy(position);
}
为了直观表现下图采用 30 等分取点把位置向量绘制出来了,后面的图片也采用一样的方式展现向量
4、获取曲线上特定位置的切线,修改物体朝向
现在物体的位置对上了,但是朝向却是固定的,不符合生活经验。一般来说物体在运动的时候,正面总是朝向轨迹的切线方向的。
现在我们通过 getTangentAt 函数获取曲线上特定位置的切线向量,根据该切线向量和点的位置向量计算物体朝向的点向量,传入物体的 lookAt 函数
function changeLookAt (t) {
const tangent = curve.getTangentAt(t);
const lookAtVec = tangent.add(position); // 位置向量和切线向量相加即为所需朝向的点向量
mesh.lookAt(lookAtVec);
}
注意上图示的切线(黄线)实际起点为原点(0,0,0),这里为了示意切线在曲线上的位置,平移到了点所在位置上
因为 lookAt 实际上是指向某个点向量,如果直接传切线向量会导致物体朝向下图 A 点,需要和位置向量相加后才能得到所需的点向量(蓝线)即 C 点
5、随时间实时改变物体位置和朝向
现在轨迹上单一点的位置和朝向都可以获取到了,剩下的就是在渲染函数中实时修改了。
根据时间计算当前点在曲线上的位置百分比,传入第 3、4 步中
const loopTime = 10 * 1000; // loopTime: 循环一圈的时间
// 在渲染函数中获取当前时间
const render = () => {
let time = Date.now();
let t = (time % loopTime) / loopTime; // 计算当前时间进度百分比
changePosition(t);
changeLookAt(t);
requestAnimationFrame(render);
renderer.render(scene, camera);
}
requestAnimationFrame(render);
作者:DigitMagic魔数实验室
链接:https://www.jianshu.com/p/cb7ae00701cd
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。