自动巡游
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
有个需求是模拟保安巡检以及类似观光沿着轨道自动巡游,通过解析拆分。可以通过一系列的点连接成线,并将相机或
保安模型沿着线进行运动,并调整其角度沿着切线方向保持~
剖析
graph LR
鼠标拾取存储点位 --> 多点成线 --> 流光动画 --> 相机或模型沿线运动
需求分解:
- 鼠标拾取三维坐标中的点(用于多点连接成线)
- 多点成线(利用通道将点连接成通道)
- 线段流光效果实现
- 相机或模型沿线运动
解决方案:
- 通过鼠标事件,拾取二维坐标转换为三维坐标
- 存储点位三维坐标数组,生成对应的管道
- 通过控制贴图的移动,实现流光效果
- 实时改变更新相机或模型的位置以及朝向,实现沿线运动
鼠标拾取三维点位
- 通过鼠标拾取屏幕二维坐标
- 将二维坐标转换为三维坐标
- 将点位坐标记录下来
- 每次记录销毁之前的管道,生成新的管道
//双击事件
this.el.addEventListener('dblclick', function (e) {
const mouse = new THREE.Vector2()
mouse.x = (e.clientX / window.innerWidth) * 2 - 1
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1
// 这里我们只检测模型的选中情况
raycaster.setFromCamera(mouse, that.camera)
const intersects = raycaster.intersectObjects(that.scene.children, true)
if (intersects.length > 0) {
var selected = intersects[0]//取第一个物体
this.pointList.push(new THREE.Vector3(selected.point.x, 5, selected.point.z))//记录点位
this.makeLine()//划线
}
})
类的调用,生成一个巡游类,传入对应的场景、相机层级、相机、animate函数,通过不断销毁生成新的管道来
创建流光线
makeLine = function () {
if (!this.zhdParade){//判断类是否存在
this.zhdParade = new zhdParade({
scene:this.scene, //场景
layers:this.currentlayers, //层级
camera:this.camera, //相机或者模型
renderFunction:this.renderFunction //animate
})
}else{
this.zhdParade.removeLine() //每次创建线要先销毁线
}
this.zhdParade.makeLine(this.pointList) //划线
}
自动巡游类的封装
由于上次的文章,一个老哥说代码太乱了,这回我贴上封装后的代码,虽然没有用上ts,但是这回应该不是很乱~~
有什么看不懂的地方可以留言,此次的代码量相对于之前较少也简单些~
以下是通用类的封装,基础类的继承就不放出来了
export class zhdParade extends zhdObject { //基础类
constructor(options) {
super()
this.type = 'zhdParade'
this.line = null
this.texture = null
this.camera=options.camera
this.layers = options.layers
this.scene = options.scene
this.renderFunction = options.renderFunction
this.texture_animate()
}
}
画线
下面采用CatmullRomCurve3通道方法
通过鼠标拾取点位数组生成管道和线,生成管道主要用于移动巡游,线便于查看管道的形态
makeLine(pointList) {
if (pointList.length < 2){
return
}
this.curve = new THREE.CatmullRomCurve3(pointList) //管道
this.curve.arcLengthDivisions = 1000
this.texture = new THREE.TextureLoader().load('static/images/line.png') //贴图管道流光效果
this.texture.wrapS = this.texture.wrapT = THREE.RepeatWrapping //每个都重复
this.texture.repeat.set(1, 1)
let tubeGeometry = new THREE.TubeGeometry(curve, 80, 1)
let material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.BackSide,
transparent: true
})
this.line = new THREE.Mesh(tubeGeometry, material)
this.line.layers.set(this.layers)
this.scene.add(this.line)
}
流光动画
通过改变线贴图的位置,形成流光效果
texture_animate() {
this.renderFunction.push(() => { //animate
if (this.texture) this.texture.offset.x -= 0.01 //改变贴图位置形成流光动画
})
}
移除管道
避免线叠加,占用内存,创建新的线前移除存在的线
removeLine() {
if (this.line) {
this.scene.remove(this.line)
this.line = null
}
}
自动巡游
控制速度因素:
- 两个点位之间的距离
- progress累加的数值大小
- 四维矩阵变化调整相机角度
简单描述:通过进度0-1之间的值来获取线上某个点的坐标,通过坐标来调整相机位置以及角度以及lookAt的位置
autoParade() {
let progress = 0
this.renderFunction.push(() => {//animate函数
let offsetAngle = Math.PI*2 //角度
if (this.carDirection === 'GO') { //沿着线前进
if (progress > 1.0) {
this.carDirection = 'BACK' //沿着线返回
} else {
progress += 0.0019 //
}
} else {
// offsetAngle = -Math.PI / 2
if (progress < 0) {
this.carDirection = 'GO'
} else {
progress -= 0.0019
}
}
if (this.curve && this.camera) {
let point = this.curve.getPoint(progress) // 获取这个进度下线上某个进度点的坐标
//模型的偏移量
//创建一个4维矩阵
let mtx = new THREE.Matrix4()
mtx.lookAt(this.camera.position.clone(), point, this.camera.up) //相机看向的位置点
mtx.multiply(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, offsetAngle, 0)))
//计算出需要进行旋转的四元数值
let toRot = new THREE.Quaternion().setFromRotationMatrix(mtx)
//根据以上值调整角度
this.camera.quaternion.slerp(toRot, 1)
this.camera.position.set(point.x, point.y, point.z)
}
})
}
今天的文章threejs自动巡游分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16600.html