three.js镜头追踪移动的实现难点有哪些
Admin 2022-11-28 群英技术资讯 490 次浏览
指定一条折线路径,镜头沿着路径向前移动,类似第一视角走在当前路径上。
很简单画一条折线路径,将镜头位置动态绑定在当前路径上,同时设置镜头朝向路径正前方。
画一条折线路径,通常将每一个转折点标出来画出的THREE.Line,会变成曲线。
难点解答:
对于controls绑定的camera,修改camera的lookAt和rotation并无反应。
难点解答:
相机观察方向camera.lookAt设置无效需要设置controls.target
对于controls绑定的camera,动态修改camera的位置总存在一定错位。
难点解答:
苍天啊,这个问题纠结我好久,怎么设置都不对,即便参考上一个问题控制controls.object.position也不对。
结果这是一个假的难点,镜头位置是受控的,感觉不受控是因为,设置了相机距离原点的最近距离!!! 导致转弯时距离太近镜头会往回退着转弯,碰到旁边的东西啊,哭唧唧。
// 设置相机距离原点的最近距离 即可控制放大限值 // controls.minDistance = 4 // 设置相机距离原点的最远距离 即可控制缩小限值 controls.maxDistance = 40
镜头抖动,怀疑是设置位置和朝向时坐标被四舍五入时,导致一会上一会下一会左一会右的抖动。
难点解答:
开始以为是我整个场景太小了,放大场景,拉长折线,拉远相机,并没有什么用。
最后发现是在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}) } }
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() },
个人喜欢方法二,只要找好了线路上的控制点,动画效果更佳更容易控制每段动画的时间。
过程中的使用过的其他方法,仅做记录用。
// 获取折线点数组 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°。
// 获取折线点数组 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 } } }
说明:
推进里层相机,相机移动和转向正常,直角转弯处突兀,因为是多个线段拼接出来的点;
推进外层相机,相机移动有些许错位(因为设置了最近距离),相机转向正常,但是直角转弯处突兀,因为是多个线段拼接出来的点。
// 获取折线点数组 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] > 0) { derection = 0 } else { derection = Math.PI } } else { // z相同 if (cameraTest.position.x - point[k][0] > 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 < count - 1) { self.tweenCameraTest(point, k + 1) } else { console.log('结束了!!!!!!') } // callBack && 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] > 0) { derection = 0 } else { derection = Math.PI } } else { // z相同 if (camera.position.x - point[k][0] > 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 < count - 1) { self.tweenCamera(point, k + 1) } else { console.log('结束了!!!!!!') } // callBack && callBack() }) // tween.easing(TWEEN.Easing.Cubic.InOut) tween.start() }
说明:
控制里层相机使用tweenCameraTest()方法,相机移动正常,通过rotation.y控制直接转向,转弯时略突兀因为没有动画控制rotation.y转动;
控制外层相机使用tweenCamera()方法,相机移动有些许错位(因为设置了最近距离),相机转向完全不受控,似乎始终看向坐标原点。
// 获取折线点数组 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°的问题。
解决了,推进外层相机镜头乱切的问题。
但是,相机移动在转弯时有明显的往后闪(因为设置了最近距离),并不是严格跟随折线前进。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
排他思想的算法就是排除掉其他的,本文主要介绍了JavaScript 排他思想的实现,以及介绍了两个示例,感兴趣的可以了解一下
如何在Typescript中使用for...in ?本人在TS中用for...in出现了些问题,也想到了一些解决方法。那么先来看看下面报错的代码吧。
axios怎么取消请求和避免重复请求?在实际的应用中,在项目的一些场景会有连续发送多个请求的情况,而异步会导致最后得到的是我们不想要的结果,这对于性能的影响是很大的,因此了解axios怎么取消请求和避免重复请求还是很有必要的,下面我们就来具体了解看看。
jquery获取select选中的值示例详解,js获取选中option的value值很简单,下面看本jquery教程的:selected选择器的使用。 html代码部分 selectoptionvalue=1张三/optionoptionvalue=2selected=selected李四/optionoptionvalue=3王五/option/selectbuttonid=getS
这篇文章主要介绍node.js中内置模块的内容,一些常见的node.js中内置模块有path模块、until模块 、fs模块、events模块等等,本文就给大家简单的介绍一下这些内置模块的用法,感兴趣的朋友可以参考。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008