Web三维可视化监控系统搭建(2)——VR场景显示和交互

文章目录

  • 1.本文主要内容
  • 2.VR/全景图场景的基本原理
    • 2.1 VR场景是个球
    • 2.2 VR图到球的映射关系
  • 3.VR/全景图场景的获取
  • 4.Three.js内展示全景图
    • 4.1 改变视野大小
  • 5. 全景图交互
    • 5.1 全景图场景切换
    • 5.2 显示传感器标签等
  • 6. 后记

1.本文主要内容

基础知识我已经在上一篇文章中说完了,从这一篇开始讲讲代码实现。这一篇文章主要说说VR/全景场景显示和交互的原理。
放一个做的半成品,可以展现全景图的html代码。但是交互之类的,都没放进去,动动手往里加哈,毕竟我们是说技术,不是代做毕设。
在这篇文章和几乎整个系列中,VR图和全景图这两个概念我们混用,认为是完全相同的。
码云示例仓库地址,考虑到国内访问gayhub的速度,我就放在码云上了。运行其中的vr_test.html即可看到vr效果。强调一点,不然打开前部署,不然就使用旧版edge打开,否则会获取不到本地的VR图片。

2.VR/全景图场景的基本原理

首先我还是把上一节用到的VR看房的链接放在这里。我们点进去任意一个案例,都能看到这样的变化:
首先,出现一个球;然后我们的视点一直往球内部走,当然也可能有旋转之类的,此时的图像会有些变形;最后我们到达了正常的视点,周围的景物看起来就非常正常了,仿佛我们置身于一个3D场景中。

2.1 VR场景是个球

由于我懒得画图——我就用嘴凭空叙述VR图的原理了。我们想象一个场景——我觉得这是我举例子的口头禅——我们打开相机的“全景图”模式,相信大家都用过这么模式,它可以拍长图,但是并不能一下快门就出图,它得拍摄者原地旋转360°,然后出一张长图。这个原理我们很容易就能想到,隔一段时间拍一幅图像,然后把这些水平移动过程中产生的图像通过某种算法拼接在一起即可。
我还要申明一下,手机的全景图模式和我这里说的全景图/vr展示,是两个概念。
那我们再想象一下——如果我们全方位地拍摄这个点周围的图像,包括天和地,那是不是就可以根据我在场景中观察的角度,直接加载对应角度的图像,达到了“我处于这个场景中”的假象呢?
假如我现在看向水平0°这个方位,那我眼前就要出现一张相机在这个点、这个方位拍摄地照片,如果我左转10°,那我的眼前就要出现一张类似的图片,这样我不管怎么改变看的角度,都能出现对应的图像,不就是给了我身处场景的假象了吗!
我们再回到这个看房的例子上,一开始出现的是一个球——我们能看到它是一个球的原因是,我们现在还在球的外面看呢!就好像在太空看地球,那确实是一个球嘛!然后我们的视点开始往球内移动,但是在到达球心之前,由于图像是在球心拍摄的,我们看到的图都会变形,横线会变得弯弯曲曲;最后到达球心之后,我们看到的就是正常的图像了。
如果说了这么多还没能让你明白呢——我给一个链接,这个链接能把VR技术和球的关系解释得非常清楚,因为它有丰富的图片。
https://blog.csdn.net/lxy19880708/article/details/91579624

2.2 VR图到球的映射关系

接下来,我们还差一步——VR图怎么映射到球上?我们先看看VR图长啥样——同样,这图直接百度copy的。看起来变形很严重啊!当然了,毕竟它最后要变成一个球啊!所以看起来肯定和正常拍摄的图像不一样。
我们先要介绍一下VR图的一个重要特点:它是2:1的。而且一定是2:1的!如果不是2:1,那说的肯定不是我这里的全景图/vr图!
好了,那我们说说为什么它是2:1的。大家都知道,地球有经纬度。经度范围是-180-180°,纬度范围是-90-90°。如果这个你不知道,我也没有办法啊!我们可以看到,经度范围刚好是纬度的两倍!
所以下面就很容易想到了:VR图都是2:1的,我们简化一下,它的像素刚好是360x180的,那岂不是球上任意一个地点的经度作为水平像素位置,纬度作为竖直像素位置(当然,还得把负的平移到正的,比如-90-90平移到0-180°),这样就可以把球面任意一点和全景图里的像素点位置对应起来了。
讲到这里,后面就没什么理论的东西了,而且大家都会觉得这也太简单了吧!是的,门外看着觉得复杂,其实门内就发现不过是只纸老虎。
Web三维可视化监控系统搭建(2)——VR场景显示和交互_第1张图片

3.VR/全景图场景的获取

如果只是试一试,那从百度找就是了,很多高质量的VR图网站,每天登录能免费下一张图吧。
如果是项目里要用,那就使用全景相机。比如 insta360 ONE R之类的消费级产品,大概2k左右,高级一点的也有十万的,看自己需求了。另外我觉得应该也有专门的摄影工作室提供拍摄吧??
另外说一下insta360 ONE R的效果。室外不知如何,但是室内的场景拼接经常不太好,可能和光照不均匀有关,但是基本上是能看的。
还有一种丐版方法,买个云台,让云台夹着手机——我没有试过,但是确实有很多这样的教程。

4.Three.js内展示全景图

我们从原理上解释了全景/vr图的原理,并且说了如何获取——很简单,有全景相机就行,不就是钱的问题嘛!
现在万事俱备,只欠东风——怎么显示,怎么交互?
核心代码不超过五句话——就五句话,我就直接抄来了哈。

var geometry = new THREE.SphereGeometry( 500, 60, 40 );
//沿x轴进行-1的scale,让球体的面朝内(因为我们将从球内进行观看)。
geometry.scale( - 1, 1, 1 );

//载入一张全景图生成threejs中可以使用的材质
var material = new THREE.MeshBasicMaterial( {
	map: new THREE.TextureLoader().load( 'vr_test2.jpg' )
} );
//将几何体和材质进行结合。
mesh = new THREE.Mesh( geometry, material );
scene.add(mesh);

4.1 改变视野大小

抄完代码,我再解释几个细节。第一个是,有时候我们的全景图没那么清晰,想让它清晰一点,就可以把视野扩大,同样的面积容纳的东西更多了,就显得不那么模糊了。同样,有时候我们也想缩小视野。一开始我傻傻地去调SphereGeometry的第一个参数——半径,但是调来调去,视野都不变化。后来突然意识到,我要调整的应该是相机的视角。毕竟我们是站在圆心,不管半径如何变化,看到的东西的经纬度范围都是一样的,但是如果扩大视角,就可以看到更多的东西了。
解释了这么多,代码就一句。在创建相机的时候,改变PerspectiveCamera的第一个参数就是改变视角了。

var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);

5. 全景图交互

和3D模型中的交互类似,都是采用Threejs的RayCaster模块实现。简单来说,就是我们点击场景中的一点时,我们生成一条射线,垂直屏幕向内,使用RayCaster来判断期间穿过了哪些面(Mesh),还可以获取交点的距离和空间坐标。
大家可以参考这篇博文学习一下RayCaster的用法。
得到了交点的坐标后,我们只要把它转化成球面的“经纬度”即可。使用Three.js自带的坐标转换函数setFromVector3即可。
从获取点击的交点坐标,到将坐标转化为和图像对应的坐标,代码如下:

//取点击到的最近的那个物体的交点
var pointClicked = intersects[0].point;
var spherical = new THREE.Spherical().setFromVector3(pointClicked)
// 同样,我们也可以把球面坐标转化成空间坐标,采用下面的函数
// var pointCl = new THREE.Vector3().setFromSphericalCoords(spherical.radius, spherical.phi, spherical.theta);
// 下面这些是对经纬度坐标进行线性变换,使其与图像对齐
spherical.theta = (Math.PI / 2 - spherical.theta) / Math.PI * 50;
if (spherical.theta < 0) {
    spherical.theta += 100
}

spherical.phi = spherical.phi / Math.PI * 100;
if(spherical.phi > top && spherical.phi < bottom && spherical.theta > left && spherical.theta < right ){
	//触发函数
}

Three.js内,球面坐标系的范围是,phi表示纬度,在0-180°之间;theta表示经度,在-180-180°之间,我为了和图片对应,把他们都转化到了0~100之间。此外要注意的是,经度的-180和图像最左侧不一定是对应的,需要进行线性的变换。具体变换成啥样的,看大家需要什么样的效果了。
这样,我们就可以获取点击的坐标了。我们可以划定一块区域,比如图像上一块方形区域,作为鼠标点击的响应区域,当点击的点在这块区域内时,就触发响应函数。

5.1 全景图场景切换

场景切换也是全景/vr图中常见的交互。具体过程就是,点击全景图中的某个区域,就会切换到下一个场景,比如在VR看房中,我们初始可能在客厅。此时点击卫生间的门,就会切换到卫生间的场景。
这个实现方式也很简单。首先我们通过上面的代码获取到点击的区域,然后根据点击区域触发函数。

if(spherical.phi > top && spherical.phi < bottom && spherical.theta > left && spherical.theta < right ){
	//从场景中移除原来的面,并且释放外部资源
	scene.remove(mesh);
    material.dispose();
    //载入一张新的全景图
	var material = new THREE.MeshBasicMaterial( {
		map: new THREE.TextureLoader().load( 'vr_test2.jpg' )
	} );
	//将几何体和材质进行结合。
	mesh = new THREE.Mesh( geometry, material );
	scene.add(mesh);
	//调整视角,看向指定的角度
	camera.lookAt(0, 0, 0);
}

最后一句代码是为了调整角度,使场景切换后初始看向的物体更合理——比方说,我从正面进入某个学校,那么接下来展现在我眼前的,是操场,而不是进去之后还得反过来看着大门。同样,如果我们从侧面教学楼楼梯口下来,这时的操场场景,我们应该看着另一边的教学楼,而不是看着大门之类的。

5.2 显示传感器标签等

同样,我们希望可以在点击传感器的时候显示对应的标签。可以采用sprite进行实现,具体的实现方法可以参考我的这篇文章

6. 后记

本文主要记述了VR显示和交互过程中的关键技术和代码,原理其实非常简单,并非什么高大上的东西。

你可能感兴趣的:(Web三维可视化系统)