全景图的基本原理

全景图是一种广角图。通过全景播放器可以让观看者身临其境地进入到全景图所记录的场景中去。比如像是这个。这种看起来很高大上的效果其实背后的原理并不复杂。

通常标准的全景图是一张2:1的图像,其背后的实质就是等距圆柱投影。等距圆柱投影是一种将球体上的各个点投影到圆柱体的侧面上的一种投影方式,投影完之后再将它展开就是一张2:1的长方形的图像。比较常见的就是应用在地图上的投影。

而在对全景图进行展示之前就需要得到一张这样的图像,这种图像可以自己用普通相机拍摄再自己合成,也可以直接使用专门的全景相机进行拍摄。全景照片的拍摄在网上有比较多的教程,由于这不是摄影分享就不详细的去说了:P。

在得到了全景图之后,就是要怎么去展示的问题了。接下来就要说说全景展示的原理。全景展示其实是等距圆柱投影的逆过程,我们要做的就是将我们得到的全景图,贴图到一个球体上,熟悉webgl的,可以用它画一个球体,然后将全景图作为材质贴到这个球体上进行渲染。由于使用webgl来进行编程的话,需要自己进行比较多的3d运算,所以也可以选择使用api更加友好的3D库,如THREE.JS来编程。比如下面的这张全景图,在球面上进行贴图。


这时我们看到的还跟预想的全景不一样,那是因为我们在球的外面,当我们在球的里面时,看到的就是跟一开始的示例一样的效果了。像是下面的这个示意图这样。

用threejs进行编程的话,关键的代码如下:

//新建一个球体
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( 'panoPic.jpg' )
} );
//将几何体和材质进行结合。
mesh = new THREE.Mesh( geometry, material );

兼容性

虽然使用webgl可以很容易的就生成一个全景的场景,但是在web里,兼容性似乎是个挥之不去的话题。主要是由于webgl不支持Android5.0以下的机器,所以,用webgl来实现将会将很多用户排除在外。所以只能寻求更好的解决方案。首先想到的就是css 3D transform 和 2D canvas画布。在threejs里支持在2D的canvas里进行绘制,本是一个比较好的方案,但是经过测试之后,发现2d画布来绘制3d的场景,性能上太吃力。所以,也被排除。剩下的就是css变换了。但是,要怎么用css来画一个一个球体呢?答案显然是不行的。虽然css不能绘制一个球体,但是css通过3D变换来绘制立方体还是简单一些的。那么用立方体可不可以实现一样的效果呢?

球体到立方体

根据全景图的原理,我们是把视角放在球的中心,通过从球心观看球面上正式场景在球面上的映像从而产生一种空间中全方位的视觉体验,同理,对于立方体,应该也可以使用相同的方式来实现。而我们要做,就是把球面上的像素点映射到立方体上。

说了基本的原理,接下来就是进行数学建模了。首先我们建立一个球坐标系,坐标系描述的变量分别为半径r,竖直方向上的夹角θ,水平方向上的夹角ø,对于球体,我们可以假定

r=1
0 < θ < π
-π/4 < ø < 7π/4

这样我们就可以得到球面上的各个点在直角坐标系中的x,y,z

x= r sin θ cos ø
y= r sin θ sin ø
z= r cos θ

对于球面到立方体上的投影,我们需要的是角度θ和ø相同时,延长球的半径r直到和立方体的面相交,假设这个长度是R,由于我们设了半径r是1所以球面上的点为 (sin θ cos ø, sin θ sin ø, cos θ) 对应的立方体上的点是(Rsin θ cos ø, Rsin θ sin ø, Rcos θ)

如果我们要求x=1这个平面上的点,则

1=Rsin θ cos ø

则可以求出来

R= 1/(sin θ cos ø)

所以在x平面上映射的点就是

(1, tan ø, cot θ / cos ø)

在立方体另外的五个平面上的投影也可以类似地得出。通过上述方法转换一张全景图,可以得到以下结果。

离我们想要的效果还有一些差距,图中似乎多了一些黑色的线。导致这种现象的原因是,由于我们的处理是以像素为单位来进行处理的,通过遍历球面图上的每个像素然后投影到立方体上的面来实现。经过这种方式进行投射之后,立方体的面上就会有一些像素被重复设置,而一些地方的像素就会缺失,比如图中的黑色部分(由于底色的黑色的)。为了解决这个问题,我们可以通过逆向的方法来解决,也就是遍历立方体上面上的每个点,求得映射到球面上的位置,然后获取球面上最接近的位置的像素。

得到了立方体上需要的图之后,就可以用css 3D变换来实现全景图了。

展示更多信息

单单地进行全景观看可能还不能满足我们的需求,也许我们还需要展示更多的信息,而这些信息可能是跟全景中的内容相关的,比如给全景中的物品打标,给全景中的内容添加评论,像是下图这样

对于这个实现的关键在于,屏幕2D坐标和空间3D坐标之间的相互转换。第一步需要实现的是记录,当用户点击屏幕,要根据点击的位置来计算出和空间中的立方体相交的点并记下这个点的位置信息。一些3d库当中会有一些api来帮助完成这项工作。而在THREEJS中使用的是Raycaster,Raycaster可以生成一条直线,然后可以很方便地得到三维空间中,和这条直线相交的物体和点。由于THREEJS中是以绘制的中心作为原点,而鼠标的点击位置是以左上角为原点,所以需要进行一下转换。

//将鼠标点击事件中的位置信息,转换到位置中心
var mouse = new THREE.Vector2(
( ev.clientX / _this.wrapper.width() ) * 2 - 1,
-( ev.clientY / _this.wrapper.height() ) * 2 + 1
)

然后就可以初始化一个Raycaster获得需要的内容了

//创建一个Raycaster实例
var raycaster = new THREE.Raycaster()
//根据点击的位置,从镜头开始初始化一和镜头的屏幕垂直的直线
raycaster.setFromCamera(mouse, _this.camera)
//获得和直线相交的物体
var intersects = raycaster.intersectObjects(_this.scene.children)

这样就在空间中记录下了一个目标位置。

有了三维中的位置之后,如果我们想要展示这个位置相关信息,比如打一个标签,就是需要将空间中的位置还原到屏幕的二维坐标上,然后用传统的css方法来进行展示就可以了。

//根据上一步中记录的位置生成一个向量
var vector = new THREE.Vector3(pos.x, pos.y, pos.z)
//将这个向量映射到镜头的平面上
vector.project(camera)
//将位置信息还原成以左上角为原点的位置信息
var screenPos = {
left: Math.round(( vector.x + 1 ) * wrapper.width() / 2),
top: Math.round(( -vector.y + 1 ) * wrapper.height() / 2)
}

作者:阿里聚划算技术团队
链接:https://www.imooc.com/article/14196
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作

Web全景图的原理及实现的更多相关文章

  1. web服务器工作原理

    Web服务器工作原理概述 转载自http://www.importnew.com/15020.html 很多时候我们都想知道,web容器或web服务器(比如Tomcat或者jboss)是怎样工作的?它 ...

  2. 【转载】Web应用工作原理

    问题描述:           Web应用工作原理   问题解决:          参考资料:http://blog.csdn.net/lcore/article/details/8964642   ...

  3. Java Web程序工作原理

    Web开发的最重要的基本功能是HTTP:Java Web开发的最重要的基本功是Servlet Specification.HTTP和Servlet Specitication对于Web Server和 ...

  4. Web程序工作原理

    1.Web程序工作原理 (1)Web一词的含义 Network:[计算机]电脑网络,网 Web:[计算机]万维网(World Wide Web),互联网(Internet) Web程序,顾名思义,即工 ...

  5. 六大Web负载均衡原理与实现

    还有个姊妹篇也可以参考这个文章:LVS(Linus Virtual Server):三种负载均衡方式比较+另三种负载均衡方式, LVS 实现了负载均衡,NAT,DR,TUN zookeeper使用ZA ...

  6. atitit.  web组件化原理与设计

    atitit.  web组件化原理与设计 1. Web Components提供了一种组件化的推荐方式,具体来说,就是:1 2. 组件化的本质目的并不一定是要为了可复用,而是提升可维护性. 不具有复用 ...

  7. Web网站工作原理解析

    Web的工作原理   Web采用的是客户机--服务器架构(Client--Server model),如下图所示,其中客户端(Client)可以通过网络连接访问另一台计算机的资源或服务,而提供资源或服 ...

  8. Web框架的原理详情

    Web框架的原理 Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. socket服务 ...

  9. Web的工作原理(二)

    1.工作过程:如下图所示描述了Web的工作原理. (1) 用户打开计算机(客户机),启动浏览器程序,并在浏览器中指定一个URL(Uniform Resource Locator,统一资源定位器),浏览 ...

随机推荐

  1. Web | jQuery快速上手

    jQuery伴随前端走过一段辉煌的时光,虽然现在已经慢慢的走下顶峰,但是过去的很多项目都是用jQuery写的,它的一些封装思想也非常值得借鉴,懂得jQuery是前端必不可少的. jQuery顶级对象 ...

  2. list 去重复元素

    public static List removeDuplicate(List list){ List listTemp = new ArrayList(); for(int i=0;i<lis ...

  3. JAVA WEB 前台实时监控后台程序运行

    基本思路: 1. 操作状态在类中以静态变量方式(或公共类存储公共变量方式,SESSION方式.COOKIE方式)存在 2. 前台采用AJAX方式激发后台进行业务逻辑操作,并实时更新操作状态信息 3.  ...

  4. 非空校验在oracle和mysql中的用法

    oracle判断是否为null nvl(参数1,参数2) :如果参数1为null则返回参数2,否则返回参数1 mysql判断是否为null ifnull(参数1,参数2) :如果参数1为null则返回 ...

  5. IE浏览器中找不到开发者工具

    ie浏览器不知道什么原因开发者工具不见了.打开以后在任务栏中显示打开了控制台,但是看不到. 解决方法 : F12 打开开发者工具后,按下 “ Ctrl + P ”

  6. jQuery实现页面回到顶部功能

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  7. Delphi XE5的Android开发平台搭建

    Delphi XE5支持Android ARM的开发,可以在Android虚拟机里运行,因此建议将XE5安装在64bit的Windows,内存可以大于3GB Delphi XE5安装光盘中包含了最基本 ...

  8. mapreduce使用 left outer join 的几种方式

    需求 数据: [主表]:存放在log.txt中 -------------------------------------------------------- 手机号码 品牌类型 登录时间 在线时长 ...

  9. node.js(二)

    今天我们学习如何运行起来一个项目,我还不会新建项目,所以我们打开一个小伙伴创建的项目,我用的开发工具是vscode 选择项目所在文件夹就好了. 打开后是这样子的 我们还要安装一下npm, 在这里安装, ...

  10. Oracle的物理存储与逻辑存储关系对应

    逻辑结构: TableSapce 由 多个 Segment组成 Sgement 由多个 Extent 组成 Extent 由 多个数据块组成 物理结构: 一个Tablespace 可以包括多个数据文件 ...