加入收藏 | 设为首页 | 会员中心 | 我要投稿 银川站长网 (https://www.0951zz.com/)- 云通信、基础存储、云上网络、机器学习、视觉智能!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

three.js镜头追踪移动的达成难点有哪些

发布时间:2023-07-03 13:13:14 所属栏目:语言 来源:
导读:很多朋友都对“three.js镜头追踪移动的实现难点有哪些”的内容比较感兴趣,对此小编整理了相关的知识分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获,那么感兴趣的朋友就继续往下看吧!指定一

很多朋友都对“three.js镜头追踪移动的实现难点有哪些”的内容比较感兴趣,对此小编整理了相关的知识分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获,那么感兴趣的朋友就继续往下看吧!

指定一条折线路径,镜头沿着路径向前移动,类似第一视角走在当前路径上。

很简单画一条折线路径,将镜头位置动态绑定在当前路径上,同时设置镜头朝向路径正前方。

1、折现变曲线

画一条折线路径,通常将每一个转折点标出来画出的THREE.Line,会变成曲线。

难点解答:

1.1、以转折点分隔,一段一段的直线来画,上一个线段的终点是下一个线段的起点。

1.2、画一条折线,在转折点处,通过多加一个点,构成一个特别细微的短弧线。

2、镜头朝向不受控

对于controls绑定的camera,修改camera的lookAt和rotation并无反应。

难点解答:

相机观察方向camera.lookAt设置无效需要设置controls.target

3、镜头位置绑定不受控

对于controls绑定的camera,动态修改camera的位置总存在一定错位。

难点解答:

苍天啊,这个问题纠结我好久,怎么设置都不对,即便参考上一个问题控制controls.object.position也不对。

结果这是一个假的难点,镜头位置是受控的,感觉不受控是因为,设置了相机距离原点的最近距离!!! 导致转弯时距离太近镜头会往回退着转弯,碰到旁边的东西啊,哭唧唧。

// 设置相机距离原点的最近距离 即可控制放大限值

// controls.minDistance = 4

// 设置相机距离原点的最远距离 即可控制缩小限值

controls.maxDistance = 40

4、镜头抖动

镜头抖动,怀疑是设置位置和朝向时坐标被四舍五入时,导致一会上一会下一会左一会右的抖动。

难点解答:

开始以为是我整个场景太小了,放大场景,拉长折线,拉远相机,并没有什么用。

最后发现是在animate()动画中设置相机位置,y坐标加了0.01:

controls.object.position.set(testList[testIndex].x, testList[testIndex].y + 0.01, testList[testIndex].z)

相机位置坐标和相机朝向坐标不在同一平面,导致的抖动,将+0.01去掉就正常了。

controls.object.position.set(testList[testIndex].x, testList[testIndex].y, testList[testIndex].z)

最终实现方法

在此通过两个相机,先观察相机cameraTest的移动路径和转向,再切换成原始相机camera。

公共代码如下:

// 外层相机,原始相机

let camera = null

// 内层相机和相机辅助线

let cameraTest = null

let cameraHelper = null

// 控制器

let controls = null

// 折线点的集合和索引

let testList = []

let testIndex = 0

initCamera () {

// 原始相机

camera = new THREE.PerspectiveCamera(45, div3D.clientWidth / div3D.clientHeight, 0.1, 1000)

camera.position.set(16, 6, 10)

// scene.add(camera)

// camera.lookAt(new THREE.Vector3(0, 0, 0))

// 设置第二个相机

cameraTest = new THREE.PerspectiveCamera(45, div3D.clientWidth / div3D.clientHeight, 0.1, 1000)

cameraTest.position.set(0, 0.6, 0)

cameraTest.lookAt(new THREE.Vector3(0, 0, 0))

cameraTest.rotation.x = 0

// 照相机帮助线

cameraHelper = new THREE.CameraHelper(cameraTest)

scene.add(cameraTest)

scene.add(cameraHelper)

}

// 初始化控制器

initControls () {

controls = new OrbitControls(camera, renderer.domElement)

}

方法一:镜头沿线推进

inspectCurveList () {

let curve = new THREE.CatmullRomCurve3([

new THREE.Vector3(2.9, 0.6, 7),

new THREE.Vector3(2.9, 0.6, 1.6),

new THREE.Vector3(2.89, 0.6, 1.6), // 用于直角转折

new THREE.Vector3(2.2, 0.6, 1.6),

new THREE.Vector3(2.2, 0.6, 1.59), // 用于直角转折

new THREE.Vector3(2.2, 0.6, -5),

new THREE.Vector3(2.21, 0.6, -5), // 用于直角转折

new THREE.Vector3(8, 0.6, -5),

new THREE.Vector3(8, 0.6, -5.01), // 用于直角转折

new THREE.Vector3(8, 0.6, -17),

new THREE.Vector3(7.99, 0.6, -17), // 用于直角转折

new THREE.Vector3(-1, 0.6, -17),

// new THREE.Vector3(-2, 0.6, -17.01), // 用于直角转折

new THREE.Vector3(-3, 0.6, -20.4),

new THREE.Vector3(-2, 0.6, 5)

])

let geometry = new THREE.Geometry()

let gap = 1000

for (let i = 0; i < gap; i++) {

let index = i / gap

let point = curve.getPointAt(index)

let position = point.clone()

curveList.push(position)

geometry.vertices.push(position)

}

// geometry.vertices = curve.getPoints(500)

// curveList = geometry.vertices

// let material = new THREE.LineBasicMaterial({color: 0x3cf0fa})

// let line = new THREE.Line(geometry, material) // 连成线

// line.name = 'switchInspectLine'

// scene.add(line) // 加入到场景中

}

// 模仿管道的镜头推进

if (curveList.length !== 0) {

if (curveIndex < curveList.length - 20) {

// 推进里层相机

/* cameraTest.position.set(curveList[curveIndex].x, curveList[curveIndex].y, curveList[curveIndex].z)

controls = new OrbitControls(cameraTest, labelRenderer.domElement) */

// 推进外层相机

// camera.position.set(curveList[curveIndex].x, curveList[curveIndex].y + 1, curveList[curveIndex].z)

controls.object.position.set(curveList[curveIndex].x, curveList[curveIndex].y, curveList[curveIndex].z)

controls.target = curveList[curveIndex + 20]

// controls.target = new THREE.Vector3(curveList[curveIndex + 2].x, curveList[curveIndex + 2].y, curveList[curveIndex + 2].z)

curveIndex += 1

} else {

curveList = []

curveIndex = 0

this.inspectSwitch = false

this.addRoomLabel()

this.removeLabel()

// 移除场景中的线

// let removeLine = scene.getObjectByName('switchInspectLine')

// if (removeLine !== undefined) {

// scene.remove(removeLine)

// }

// 还原镜头位置

this.animateCamera({x: 16, y: 6, z: 10}, {x: 0, y: 0, z: 0})

}

}

方法二:使用tween动画

inspectTween () {

let wayPoints = [

{

point: {x: 2.9, y: 0.6, z: 1.6},

camera: {x: 2.9, y: 0.6, z: 7},

time: 3000

},

{

point: {x: 2.2, y: 0.6, z: 1.6},

camera: {x: 2.9, y: 0.6, z: 1.6},

time: 5000

},

{

point: {x: 2.2, y: 0.6, z: -5},

camera: {x: 2.2, y: 0.6, z: 1.6},

time: 2000

},

{

point: {x: 8, y: 0.6, z: -5},

camera: {x: 2.2, y: 0.6, z: -5},

time: 6000

},

{

point: {x: 8, y: 0.6, z: -17},

camera: {x: 8, y: 0.6, z: -5},

time: 3000

},

{

point: {x: -2, y: 0.6, z: -17},

camera: {x: 8, y: 0.6, z: -17},

time: 3000

},

{

point: {x: -2, y: 0.6, z: -20.4},

camera: {x: -2, y: 0.6, z: -17},

time: 3000

},

{

point: {x: -2, y: 0.6, z: 5},

camera: {x: -3, y: 0.6, z: -17},

time: 3000

},

// {

// point: {x: -2, y: 0.6, z: 5},

// camera: {x: -2, y: 0.6, z: -20.4}

// },

{

point: {x: 0, y: 0, z: 0},

camera: {x: -2, y: 0.6, z: 5},

time: 3000

}

]

this.animateInspect(wayPoints, 0)

}

animateInspect (point, k) {

let self = this

let time = 3000

if (point[k].time) {

time = point[k].time

}

let count = point.length

let target = point[k].point

let position = point[k].camera

let tween = new TWEEN.Tween({

px: camera.position.x, // 起始相机位置x

py: camera.position.y, // 起始相机位置y

pz: camera.position.z, // 起始相机位置z

tx: controls.target.x, // 控制点的中心点x 起始目标位置x

ty: controls.target.y, // 控制点的中心点y 起始目标位置y

tz: controls.target.z // 控制点的中心点z 起始目标位置z

})

tween.to({

px: position.x,

py: position.y,

pz: position.z,

tx: target.x,

ty: target.y,

tz: target.z

}, time)

tween.onUpdate(function () {

camera.position.x = this.px

camera.position.y = this.py

camera.position.z = this.pz

controls.target.x = this.tx

controls.target.y = this.ty

controls.target.z = this.tz

// controls.update()

})

tween.onComplete(function () {

// controls.enabled = true

if (self.inspectSwitch && k < count - 1) {

self.animateInspect(point, k + 1)

} else {

self.inspectSwitch = false

self.addRoomLabel()

self.removeLabel()

}

// callBack && callBack()

})

// tween.easing(TWEEN.Easing.Cubic.InOut)

tween.start()

},

方法比较

方法一:镜头控制简单,但是不够平滑。

方法二:镜头控制麻烦,要指定当前点和目标点,镜头切换平滑但不严格受控。

个人喜欢方法二,只要找好了线路上的控制点,动画效果更佳更容易控制每段动画的时间。

其他方法

过程中的使用过的其他方法,仅做记录用。

方法一:绘制一条折线+animate镜头推进

// 获取折线点数组

testInspect () {

// 描折线点,为了能使一条折线能直角转弯,特添加“用于直角转折”的辅助点,尝试将所有标为“用于直角转折”的点去掉,折线马上变曲线。

let curve = new THREE.CatmullRomCurve3([

new THREE.Vector3(2.9, 0.6, 7),

new THREE.Vector3(2.9, 0.6, 1.6),

new THREE.Vector3(2.89, 0.6, 1.6), // 用于直角转折

new THREE.Vector3(2.2, 0.6, 1.6),

new THREE.Vector3(2.2, 0.6, 1.59), // 用于直角转折

new THREE.Vector3(2.2, 0.6, -5),

new THREE.Vector3(2.21, 0.6, -5), // 用于直角转折

new THREE.Vector3(8, 0.6, -5),

new THREE.Vector3(8, 0.6, -5.01), // 用于直角转折

new THREE.Vector3(8, 0.6, -17),

new THREE.Vector3(7.99, 0.6, -17), // 用于直角转折

new THREE.Vector3(-2, 0.6, -17),

new THREE.Vector3(-2, 0.6, -17.01), // 用于直角转折

new THREE.Vector3(-2, 0.6, -20.4),

new THREE.Vector3(-2, 0.6, 5),

])

let material = new THREE.LineBasicMaterial({color: 0x3cf0fa})

let geometry = new THREE.Geometry()

geometry.vertices = curve.getPoints(1500)

let line = new THREE.Line(geometry, material) // 连成线

scene.add(line) // 加入到场景中

testList = geometry.vertices

}

// 场景动画-推进相机

animate () {

// 模仿管道的镜头推进

if (testList.length !== 0) {

if (testIndex < testList.length - 2) {

// 推进里层相机

// cameraTest.position.set(testList[testIndex].x, testList[testIndex].y, testList[testIndex].z)

// controls = new OrbitControls(cameraTest, labelRenderer.domElement)

// controls.target = new THREE.Vector3(testList[testIndex + 2].x, testList[testIndex + 2].y, testList[testIndex + 2].z)

// testIndex += 1

// 推进外层相机

camera.position.set(testList[testIndex].x, testList[testIndex].y, testList[testIndex].z)

controls.target = new THREE.Vector3(testList[testIndex + 2].x, testList[testIndex + 2].y, testList[testIndex + 2].z)

testIndex += 1

} else {

testList = []

testIndex = 0

}

}

}

说明:

推进里层相机,相机移动和转向正常,且在直角转弯处,镜头转动>90°再切回90°;

推进外层相机,镜头突然开始乱切(因为设置了最近距离),且在直角转弯处,镜头转动>90°再切回90°。

方法二:绘制多条线段+animate镜头推进

// 获取折线点数组

testInspect () {

let points = [ [2.9, 7],

[2.9, 1.6],

[2.2, 1.6],

[2.2, -5],

[8, -5],

[8, -17],

[-2, -17],

[-2, -20.4],

[-2, 5]

]

testList = this.linePointList(points, 0.6)

}

linePointList (xz, y) {

let allPoint = []

for (let i = 0; i < xz.length - 1; i++) {

if (xz[i][0] === xz[i + 1][0]) {

let gap = (xz[i][1] - xz[i + 1][1]) / 100

for (let j = 0; j < 100; j++) {

allPoint.push(new THREE.Vector3(xz[i][0], y, xz[i][1] - gap * j))

}

} else {

let gap = (xz[i][0] - xz[i + 1][0]) / 100

for (let j = 0; j < 100; j++) {

allPoint.push(new THREE.Vector3(xz[i][0] - gap * j, y, xz[i][1]))

}

}

}

return allPoint

}

// 场景动画-推进相机

animate () {

// 模仿管道的镜头推进

if (testList.length !== 0) {

if (testIndex < testList.length - 2) {

// 推进里层相机

// cameraTest.position.set(testList[testIndex].x, testList[testIndex].y, testList[testIndex].z)

// controls = new OrbitControls(cameraTest, labelRenderer.domElement)

// controls.target = new THREE.Vector3(testList[testIndex + 2].x, testList[testIndex + 2].y, testList[testIndex + 2].z)

// testIndex += 1

// 推进外层相机

camera.position.set(testList[testIndex].x, testList[testIndex].y, testList[testIndex].z)

controls.target = new THREE.Vector3(testList[testIndex + 2].x, testList[testIndex + 2].y, testList[testIndex + 2].z)

testIndex += 1

} else {

testList = []

testIndex = 0

}

}

}

说明:

推进里层相机,相机移动和转向正常,直角转弯处突兀,因为是多个线段拼接出来的点;

推进外层相机,相机移动有些许错位(因为设置了最近距离),相机转向正常,但是直角转弯处突兀,因为是多个线段拼接出来的点。

方法三:绘制多条线段+tween动画变化镜头

// 获取折线点数组

testInspect () {

let points = [

[2.9, 7],

[2.9, 1.6],

[2.2, 1.6],

[2.2, -5],

[8, -5],

[8, -17],

[-2, -17],

[-2, -20.4],

[-2, 5]

]

this.tweenCameraTest(points, 0) // tween动画-控制里层相机

// this.tweenCamera(points, 0) // tween动画-控制外层相机

}

// tween动画-控制里层相机

tweenCameraTest (point, k) {

let self = this

let count = point.length

let derection = 0

if (cameraTest.position.x === point[k][0]) {

// x相同

if (cameraTest.position.z - point[k][1] &gt; 0) {

derection = 0

} else {

derection = Math.PI

}

} else {

// z相同

if (cameraTest.position.x - point[k][0] &gt; 0) {

derection = Math.PI / 2

} else {

derection = - Math.PI / 2

}

}

cameraTest.rotation.y = derection

let tween = new TWEEN.Tween({

px: cameraTest.position.x, // 起始相机位置x

py: cameraTest.position.y, // 起始相机位置y

pz: cameraTest.position.z // 起始相机位置z

})

tween.to({

px: point[k][0],

py: 0.6,

pz: point[k][1]

}, 3000)

tween.onUpdate(function () {

cameraTest.position.x = this.px

cameraTest.position.y = this.py

cameraTest.position.z = this.pz

})

tween.onComplete(function () {

if (k &lt; count - 1) {

self.tweenCameraTest(point, k + 1)

} else {

console.log('结束了!!!!!!')

}

// callBack &amp;&amp; callBack()

})

// tween.easing(TWEEN.Easing.Cubic.InOut)

tween.start()

}

// tween动画-控制外层相机

tweenCamera (point, k) {

let self = this

let count = point.length

let derection = 0

if (camera.position.x === point[k][0]) {

// x相同

if (camera.position.z - point[k][1] &gt; 0) {

derection = 0

} else {

derection = Math.PI

}

} else {

// z相同

if (camera.position.x - point[k][0] &gt; 0) {

derection = Math.PI / 2

} else {

derection = - Math.PI / 2

}

}

camera.rotation.y = derection

let tween = new TWEEN.Tween({

px: camera.position.x, // 起始相机位置x

py: camera.position.y, // 起始相机位置y

pz: camera.position.z // 起始相机位置z

})

tween.to({

px: point[k][0],

py: 0.6,

pz: point[k][1]

}, 3000)

tween.onUpdate(function () {

camera.position.x = this.px

camera.position.y = this.py

camera.position.z = this.pz

})

tween.onComplete(function () {

if (k &lt; count - 1) {

self.tweenCamera(point, k + 1)

} else {

console.log('结束了!!!!!!')

}

// callBack &amp;&amp; callBack()

})

// tween.easing(TWEEN.Easing.Cubic.InOut)

tween.start()

}

说明:

控制里层相机使用tweenCameraTest()方法,相机移动正常,通过rotation.y控制直接转向,转弯时略突兀因为没有动画控制rotation.y转动;

控制外层相机使用tweenCamera()方法,相机移动有些许错位(因为设置了最近距离),相机转向完全不受控,似乎始终看向坐标原点。

方法四:优化方法一,绘制一条折线+animate镜头推进

// 获取折线点数组

testInspect () {

// 描折线点,为了能使一条折线能直角转弯,特添加“用于直角转折”的辅助点,尝试将所有标为“用于直角转折”的点去掉,折线马上变曲线。

let curve = new THREE.CatmullRomCurve3([

new THREE.Vector3(2.9, 0.6, 7),

new THREE.Vector3(2.9, 0.6, 1.6),

new THREE.Vector3(2.89, 0.6, 1.6), // 用于直角转折

new THREE.Vector3(2.2, 0.6, 1.6),

new THREE.Vector3(2.2, 0.6, 1.59), // 用于直角转折

new THREE.Vector3(2.2, 0.6, -5),

new THREE.Vector3(2.21, 0.6, -5), // 用于直角转折

new THREE.Vector3(8, 0.6, -5),

new THREE.Vector3(8, 0.6, -5.01), // 用于直角转折

new THREE.Vector3(8, 0.6, -17),

new THREE.Vector3(7.99, 0.6, -17), // 用于直角转折

new THREE.Vector3(-2, 0.6, -17),

new THREE.Vector3(-2, 0.6, -17.01), // 用于直角转折

new THREE.Vector3(-2, 0.6, -20.4),

new THREE.Vector3(-2, 0.6, 5),

])

let material = new THREE.LineBasicMaterial({color: 0x3cf0fa})

let geometry = new THREE.Geometry()

let gap = 500

for (let i = 0; i < gap; i++) {

let index = i / gap

let point = curve.getPointAt(index)

let position = point.clone()

testList.push(position) // 通过此方法获取点比curve.getPoints(1500)更好,不信你试试,用getPoints获取,镜头会有明显的俯视效果不知为何。

geometry.vertices.push(position)

}

let line = new THREE.Line(geometry, material) // 连成线

scene.add(line) // 加入到场景中

}

// 场景动画-推进外层相机

animate () {

// 模仿管道的镜头推进

if (testList.length !== 0) {

if (testIndex < testList.length - 2) {

// 推进里层相机

// cameraTest.position.set(testList[testIndex].x, testList[testIndex].y, testList[testIndex].z)

// controls = new OrbitControls(cameraTest, labelRenderer.domElement)

// 推进外层相机

// camera.position.set(testList[testIndex].x, testList[testIndex].y + 0.01, testList[testIndex].z)

controls.object.position.set(testList[testIndex].x, testList[testIndex].y + 0.01, testList[testIndex].z) // 稍微讲相机位置上移,就不会出现似乎乱切镜头穿过旁边物体的效果。

controls.target = testList[testIndex + 2]

// controls.target = new THREE.Vector3(testList[testIndex + 2].x, testList[testIndex + 2].y, testList[testIndex + 2].z)

testIndex += 1

} else {

testList = []

testIndex = 0

}

}

}

说明:

解决了,直角转弯处,镜头转动>90°再切回90°的问题。

解决了,推进外层相机镜头乱切的问题。

但是,相机移动在转弯时有明显的往后闪(因为设置了最近距离),并不是严格跟随折线前进。

(编辑:银川站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!