全景图的基本原理

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

通常标准的全景图是一张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. plsql developer 64位 注册码

    注册码product code(产品编码): 4vkjwhfeh3ufnqnmpr9brvcuyujrx3n3le serial Number(序列号):226959 password(口令): xs ...

  2. Web的基本工作原理、HTTP协议和URL说明

    Web工作原理 客户端和Web服务器通过HTTP协议进行通信.Web服务器有是也叫HTTP服务器或Web容器.HTTP协议采用的是请求/响应模式.即客户端发起HTTP请求,web服务器接收并解析处理H ...

  3. vue 复习(2)v-bind的应用 v-bind:classv-binf:style

    dasdclass与style绑定v-bind 1. 绑定HTML Class 对象语法 有些时候我们想动态的切换class的类名.在原生的js或jq中我们就要通过事件来动态的改变class类名,但在 ...

  4. async之诗句慢慢出现

    <body> <ul class="oul"></ul> <script> let str = ["君不见,黄河之水天上来 ...

  5. 树莓派3B+学习笔记:4、查看GPIO

    GPIO(General Purpose I/O Ports)意思为通用输入/输出端口. 可以在终端重直接查看GPIO的定义. 查看方式1: gpio readall 查看方式2: pinout 可以 ...

  6. python列表的通用操作

    #'+'和'*'#+可以将两个列表拼接为一个列表my_list = [1,2,3]+[4,5,6]#*可以将列表重复指定的次数my_list = [1,2,3]*5 print(my_list) #创 ...

  7. ubuntu18.04 没声音解决方案(坑自己版)

    那啥,半个月没开电脑了,这几天打开发现系统没声了 那咋办呢,修一修呗 搜索了下问题,还挺简单的 jiang@ryzen:~$ sudo apt install pavucontrol 打开 jiang ...

  8. Windows下安装配置MinGW GCC调试环境

    下载安装文件:Sourceforge  64位系统安装选项记得选x86_64.安装过程中连不上服务器的话也可以选择下载压缩包. 配置环境变量,假设mingw安装目录为C:\mingw-w64\ming ...

  9. 20155203 2016-2017-2 《Java程序设计》第10周学习总结

    20155203 2016-2017-2 <Java程序设计>第10周学习总结 教材学习内容总结 网络编程(Java Socket编程) Java最初是作为网络编程语言出现的,其对网络提供 ...

  10. 20155234java实验一

    实验内容 1.使用JDK编译.运行简单的Java程序: 2.使用Eclipse 编辑.编译.运行.调试Java程序. 实验要求 1.没有Linux基础的同学建议先学习Linux基础入门(新版))Vim ...