CSS 3D实现虚拟全景探究
随着移动设备的发展,手机浏览器的兼容性已经变得比较令人满意,绝大部分CSS3属性和HTML5 API已经被移动浏览器支持。今年淘宝造物节的H5活动宣传页实现的3D虚拟全景令人感到惊艳。本文就是来探究如何利用CSS3 和 HTML5 API来实现一个基于浏览器的虚拟全景。
几个例子
话不多说,先上例子。
核心技术介绍
主要的核心技术包括CSS3几个有关2D/3D transform的属性和HTML5设备陀螺仪API。
模拟视距的变化(translateZ, perspective)
- translateZ是指物体距离屏幕所在平面的前后距离。正值时,物体在屏幕所在平面之前;负值时,物体在屏幕所在平面之后。默认值为0,即物体在屏幕所在平面上。
- perspective是指人眼距离屏幕的距离,同时是否设置perspective的值也是浏览器用来判断是否开启3D视角:也就是说CSS3 transform由2D transform 变成了3D transform。默认值为none或者0,即不开启3D视角。
- perspective-origin是指视线直视屏幕上的点所在的位置。默认值为50% 50%,即观察点在屏幕中央。
具体意义可以参考下图。
模拟视角的变化(rotateX, rotateY, rotateZ)
模拟人视角变化主要利用到了transform中的rotateX, rotateY, rotateZ三个属性。通过这三个属性可以模拟人眼在与屏幕平行位置移动时观察屏幕内物体的视觉效果。
假设屏幕里有一个正方形。那么可以通过rotateX来模拟人上下移动时观察物体的效果;通过rotateY来模拟人左右移动时观察物体的效果;通过rotateZ来模拟人在倾斜时(比如扭头)观察物体的效果.具体效果如下所示。
模拟真实世界光照模型(transform-style)
在计算机图形学里,学习过光线跟踪算法(Ray Tracing)和辐射度算法(Radiosity)来计算模拟物体在真实世界的遮挡阴影效果。而在浏览器默认状态下,dom结构的中元素是根据其在文档流中的先后顺序来确定遮盖关系的,这显然与真实世界不相符。真实世界中,一般都是视距近的物体会遮挡视觉远的物体。
可以通过设置transform-style的值来开启模拟真实世界的光照模型(主要是物体遮挡效果,不包括散射、折射、阴影等)。transform-style只有两个值:默认值为flat,即不开启;开启则值设为preserve-3d。
HTML5设备陀螺仪API(deviceorientation)
在最开始给的两个例子里,用户可以通过扭转手机来改变屏幕中的视角。这是利用了移动设备中的陀螺仪来检测设备方向的变化。
在前端可以通过监听Deviceorientation Event来实现实时获取设备方向的变化。
|
|
由上面的代码可以看出:
- 可以通过window.DeviceOrientationEvent对象的存在与否来判断浏览器是否支持H5中的设备方向陀螺仪API。
- 可以从EventHandle参数中的Event对象中获得alpha, beta, gamma三个属性,这三个属性分别的代表了设备在空间坐标系上三个维度上方向的改变。alpha的取值范围为0~360,beta的取值范围为-90~90,gamma的取值范围为-180~180。三个属性全为0代表设备水平放置与地面平行,具体意义可以参考下图。
除了deviceorientation之外,HTML5设备陀螺仪API还支持DeviceMotion Events用来获取设备的移动加速度等信息(典型应用:摇一摇)。
具体实现
虚拟全景的实现类似于google街景地图(原理链接)。简单来说,就是需要将360°的景物平面图片拼成一个立体球形,然后将人的视角放在球的内部,这样当视角360°移动时,就会看到某个方向上的景物,形成3D的视觉效果。
所以搭建一个虚拟全景的步骤可以分为如下几个步骤:
- 获取图片,拼接成球;
- 定位观察点;
- 移动视角产生3D效果。
图片的拼接
由于真实情况下,获取的图片的都是平面的、不连续的,所以在拼接时,其实是在用多面体模拟真实环境的球体(面越多模拟效果越好)。那么如何将一系列的图片在屏幕中拼接成一个球型呢?这就要用到上面提到的模拟视角变化的3D transform中的rotateY和translateZ属性,通过改变各个图片rotateY和translateZ的值,来将所有图片拼接成一个首位相连的多面体。
下面举例说明。该拼接过程的例子引自这篇文章。
假设现在有9张系列全景图片,每张的长和宽都为210px。那么可以将其拼接成一个9面体来模拟球体,如下图所示。
- 第一步:为了实现拼接,则需要1~9张图片的rotateY值分别取0, 40deg, 80deg, 120deg, ….. ,320deg(从0开始,每次递增360°/n,n为n面体(不包括上下两个面))。
- 第二步:在设置了图片的rotateY值后,还需要将每个图片向外移动一定距离,来将各个图片首尾相连最终拼接成一个多面体。该距离等于所要构成的多面体的半径(通过设置图片translateZ的值),来将各个图片最终拼接成一个多面体。多面体半径的计算过程如下图所示。
下面的代码用动画演示了上面所描述的如何将图片拼接成多面体的两个步骤。
观察点的定位
在构建起多边体后,下一步需要将人的视角放在多边体的内部,来产生围绕四周360°的全景效果。这里由于视角的位置(perspective的值)一般是固定不变的,所以通过改变多面体父元素(可以认为是舞台stage)的translateZ属性的值来整体移动舞台,将视角置于多面体之内。
还是以上面的例子来说明,上面视角的perspective的值为1000px,多面体的每个面的translateZ的值为288px,那么舞台translateZ的值在[712px, 1288px]区间内都可以满足视角在多面体之内,具体的值可以根据所要展示的效果的来调整。
视角移动
DEMO
基于上面的介绍,下面来简单实现淘宝购物节的部分效果。
素材的准备与处理
- 首先原始图片素材为文章头图给出的造物节全景图图片(图片要是首尾相连的,以保证视图的连续性)。该图片尺寸为2580px宽、1170px高。将其按纵列切分为20等分,每份的宽为129px。
- 然后将十张图片拼接成一个20面体。根据上面的公式,可以得知十张图片的rotateY值分别取0, 20deg, 40deg, 60deg, ….. ,340deg;图片translateZ的值即多面体的半径为407px。
- 除此之外,为了观察图片没覆盖的区域的美观,还要准备一张整个环境的背景图。此外,淘宝购物节在多面体的顶部和底部空白处还加了相关LOGO图,如下所示。