我是靠谱客的博主 繁荣缘分,这篇文章主要介绍AR室内导航-Three.js概述初始化室内地图创建楼层控件导航使用Three.js 生成AR模块原理初始化Three,现在分享给大家,希望可以做个参考。

AR室内导航-Three.js

  • 概述
  • 初始化室内地图
  • 创建楼层控件
  • 导航
  • 使用Three.js 生成AR模块原理
  • 初始化Three

概述

如有不明白的可以加QQ:2354528292;wx: aichitudousien
更多教学视频请访问:https://space.bilibili.com/236087412
源码获取:https://item.taobao.com/item.htm?spm=a21dvs.23580594.0.0.3c3a645ebB8H6o&ft=t&id=714574529746

这一次的AR室内导航是使用蜂鸟云地图加上three.js做的,具备室内楼层切换,2D/3D模型切换,指北针控件,AR开启/关闭。模拟室内导航的功能,先来看看视频效果

AR室内导航

初始化室内地图

初始化蜂鸟云室内地图很简单,使用的也是蜂鸟云自带的地图数据
vue文件中调用mapCreate创建地图

复制代码
1
2
3
4
this.$nextTick(() => { this.mapCreate(); });

地图配置参数,需要自己去创建key值

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
options: { appName: '蜂鸟研发SDK_2_0', key: '', mapID: '1321274646113083394', // 缩放级别 mapZoom: 20, // 显示楼层 visibleLevels: [1, 2, 3, 4, 5], // 默认显示几楼 level: 3 } window.map = new fengmap.FMMap(this.options);

此时地图创建显示成功
在这里插入图片描述

创建楼层控件

地图创建完成后生成楼层控件,指北针,导航控件

复制代码
1
2
3
4
5
6
7
8
9
10
//监听地图加载完成 map.on('loaded', () => { //创建导航对象 this.creatNavigation(); //创建楼层控件 this.creatFloorControl(); //创建指北针控件 this.creatCompassControl(); });

楼层控件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
creatFloorControl() { let toolbar = new fengmap.FMToolbar({ //默认在右上角 position: fengmap.FMControlPosition.RIGHT_TOP, //初始是否是多层显示,默认单层显示 allLayer: false, //是否显示多层/单层切换按钮 needAllLayerBtn: true, //控件位置x,y的偏移量 offset: { x: -10, y: 320 } }); toolbar.addTo(map); },

指北针

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let compass = new fengmap.FMCompass({ position: fengmap.FMControlPosition.LEFT_TOP, width: 40, height: 40, offset: { x: 12, y: 460 } }); compass.addTo(map); compass.on('click', function() { map.setRotation({ rotation: 0, animate: true, duration: 0.3 }); });

导航控件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// FMNaviAnalyser 是可分析最短路径、最快路径并返回分析结果的路径类。可独立于地图工作,支持Web Worker 和 Node let analyser = new fengmap.FMNaviAnalyser( this.options, function() { // FMNavigation 是导航相关的功能类, 可用于模拟导航和真实导航使用 window.navi = new fengmap.FMNavigation({ map: map, analyser: analyser, locationMarkerUrl: './img/导航.png', locationMarkerSize: 32 }); }, (error) => { console.log(error); } );

在这里插入图片描述
此时就可以切换楼层显示和控制2D/3D转换

导航

一个输入开始地址和结束地点的UI,随便写写就ok
在这里插入图片描述
然后需在地图点击时输入起始点和终点,需要在地图上绑定点击事件
isNavBoxShow 为组件显示状态,startPointSelect 为起始点状态,endPointSelect 为结束点状态

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// //路径规划 map.on('click', (event) => { if (this.$store.state.isNavBoxShow === true) { if (this.$store.state.startPointSelect === true) { window.routeOpiton.start = { x: event.coords.x, y: event.coords.y, level: event.targets[0].level, url: './img/start.png', height: 3 }; navi.setStartPoint(window.routeOpiton.start); if (event.targets[0].name) { document.getElementById('startInput').value = event.targets[0].name; } else { document.getElementById('startInput').value = '当前起点位置'; } this.$store.commit('startPointSelectFalse'); } else if (this.$store.state.endPointSelect === true) { window.routeOpiton.end = { x: event.coords.x, y: event.coords.y, level: event.targets[0].level, url: './img/end.png', height: 3 } navi.setDestPoint(window.routeOpiton.end); if (event.targets[0].name) { document.getElementById('endInput').value = event.targets[0].name; } else { document.getElementById('endInput').value = '当前终点位置'; } this.$store.commit('endPointSelectFalse'); } } });

此时我们点击地图模块就可以输入起始点和结束点了
在这里插入图片描述
点击确定后调用路径计算函数
window.routeOpiton 为起始点和结束点对象

复制代码
1
2
3
4
5
6
7
8
9
10
navi.route(window.routeOpiton, function(result) { let line = navi.drawNaviLine(); let coordinates = []; result.subs.forEach(item => { item.waypoint.points.forEach(point => { coordinates.push(point) }) }); })

在这里插入图片描述

使用Three.js 生成AR模块原理

说明一下生成步骤,第一步同样是先验证是否能打开摄像头,然后初始化Three.js,然后将摄像头的视频流使用video贴图map到three.js的背景中,这样就可以呈现了,然后怎么在场景中显示路径呢,也不难,蜂鸟云的api会返回一条最短路径的数组,通过这个最短路径的数据我们就可以计算,首先判断每一个点之间的距离是否大于1,如何计算两点之间的距离呢,通过两点的的平方开根就好了,计算出后大于1的就是存在有转角的,这时我们就要计算角度了,角度通过反正切来计算,这里需要注意的是轴的旋转方向,最后在监听陀螺仪来改变生成的点和线的角度就可以了,整体来说思路ok了接下来就是变成代码就行了,实现代码不难,主要是思路~

初始化Three

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//初始参数 canvas = document.getElementById('webGL3d') arWidth = canvas.offsetWidth arHeight = canvas.offsetHeight scene = new THREE.Scene() camera = new THREE.PerspectiveCamera(60, arWidth / arHeight, 0.0001, 7000) camera.position.set(0, -7, 5) // //renderer参数 let renderParam = { antialias: true, // true/false表示是否开启反锯齿 // alpha: true, // true/false 表示是否可以设置背景色透明 precision: 'highp', // highp/mediump/lowp 表示着色精度选择 premultipliedAlpha: false, maxLights: 3, canvas: canvas } renderer = new THREE.WebGLRenderer(renderParam) renderer.setSize(arWidth, arHeight) orbitControls = new OrbitControls(camera, renderer.domElement)

判断是否支持摄像头并返回视频流,这里有一个小细节,判断是否是手机还是PC,手机强制使用后置摄像头

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let video = document.createElement('video'); // navigator.mediaDevices.getUserMedia 提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。 const stream = await navigator.mediaDevices.getUserMedia({ // 关闭音频 audio: false, video: { // 在移动设备上面,表示优先使用前置摄像头 // facingMode: 'user', facingMode: isMobile() ? { exact: "environment" } : 'user', width: width, height: height } }); video.srcObject = stream; video.play(); video.width = width; video.height = height; return new Promise((resolve) => { // 在视频的元数据加载后执行 JavaScript video.onloadedmetadata = () => { resolve(video); }; });
复制代码
1
2
3
4
5
6
function isMobile() { const isAndroid = /Android/i.test(navigator.userAgent); const isiOS = /iPhone|iPad|iPod/i.test(navigator.userAgent); return isAndroid || isiOS; }

获取到视频流后将视频贴到three.js的背景中

复制代码
1
2
3
4
5
6
let video = await openCamera(arWidth, arHeight); console.log(video); videoTexture = new THREE.Texture(video); videoTexture.minFilter = THREE.LinearFilter; scene.background = videoTexture;

这里我们就可以在看到视频了
接着我们创建一个起始点标记

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let plane = new THREE.PlaneGeometry(1, 1) let map = new THREE.TextureLoader().load(require('@/assets/img/WechatIMG1129.png')) let material = new THREE.MeshBasicMaterial({ map: map, alphaTest: 0.1, color: 0xffffff, side: THREE.DoubleSide, }) nowPosPic = new THREE.Mesh(plane, material) nowPosPic.position.set(0, offsetY, 0) scene.add(nowPosPic) //添加坐标轴 let axes = new THREE.AxesHelper(500) scene.add(axes)

在这里插入图片描述
绘制导航线

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if (coordinates.length !== 0) { group = new THREE.Group() let starPoint = { x: 0, y: 0 } for (let i = 1; i < coordinates.length; i++) { let x = coordinates[i].x - coordinates[0].x let y = coordinates[i].y - coordinates[0].y // 计算两点的距离 let distance = Math.sqrt(Math.pow(x - starPoint.x, 2) + Math.pow(y - starPoint.y, 2)) if (distance >= 1) { // 计算弧度 let angle = calAngleX(x - starPoint.x, y - starPoint.y) // 生成线 createLine(starPoint, distance, angle) starPoint.x = x starPoint.y = y } } scene.add(group) group.position.y = offsetY group.rotation.z = -alpha * Math.PI / 180 }

计算弧度代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//计算偏转角度(X逆时针) function calAngleX(x, y) { let angle = Math.atan(Math.abs(y) / Math.abs(x)) if (x >= 0 && y >= 0) { } else if (x <= 0 && y >= 0) { angle = Math.PI - angle } else if (x <= 0 && y <= 0) { angle = Math.PI + angle } else { angle = Math.PI * 2 - angle } return angle }

生成线

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let plane = new THREE.PlaneGeometry(1, 1) let map = new THREE.TextureLoader().load(require('@/assets/img/WechatIMG1123.png')) let material = new THREE.MeshBasicMaterial({ map: map, alphaTest: 0.1, color: 0xffffff, side: THREE.DoubleSide, }) for (let i = 0.6; i <= length; i++) { let mesh = new THREE.Mesh(plane, material) let x = starPoint.x + i * Math.cos(angle) let y = starPoint.y + i * Math.sin(angle) mesh.position.set(x, y, 0) let obj = { x: x + coordinates[0].x, y: y + coordinates[0].y } lingMeshArray.push(obj) mesh.rotation.z = angle - Math.PI / 2 group.add(mesh) }

到这里就可以看到生成的线了
在这里插入图片描述
监听陀螺仪window.DeviceOrientationEvent
window.DeviceOrientationEvent说明
DeviceOrientationEvent.absolute 只读
用来说明设备是提供的旋转数据是否是绝对定位的布尔值。
DeviceOrientationEvent.alpha 只读
一个表示设备绕z轴旋转的角度(范围在0-360之间)的数字
DeviceOrientationEvent.beta 只读
一个表示设备绕x轴旋转(范围在-180到180之间)的数字,从前到后的方向为正方向。
DeviceOrientationEvent.gamma 只读
一个表示设备绕y轴旋转(范围在-90到90之间)的数字,从左向右为正方向。
throttle只是节流函数

复制代码
1
2
3
4
5
6
if (window.DeviceOrientationEvent) { window.addEventListener('deviceorientation', throttle(setMeshCamera, 100), false) } else { console.log('你的浏览器不支持陀螺仪') }

最后根据陀螺仪计算起始点和线的旋转角度就可以了

最后

以上就是繁荣缘分最近收集整理的关于AR室内导航-Three.js概述初始化室内地图创建楼层控件导航使用Three.js 生成AR模块原理初始化Three的全部内容,更多相关AR室内导航-Three.js概述初始化室内地图创建楼层控件导航使用Three.js内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(131)

评论列表共有 0 条评论

立即
投稿
返回
顶部