Web 全景在以前带宽有限的条件下常常用来作为街景和 360° 全景图片的查看。它可以给用户一种 self-immersive 的体验,通过简单的操作,自由的查看周围的物体。随着一些运营商推出大王卡等免流服务,以及 4G 环境的普及,大流量的应用也逐渐得到推广。比如,我们是否可以将静态低流量的全景图片,变为动态直播的全景视频呢?在一定网速带宽下,是可以实现的。后面,我们来了解一下,如何在 Web 端实现全景视频。先看一下实例 gif:

tl;dr;

  • 使用 three.js 实现全景技术
  • UV 映射原理简介
  • 3D 坐标原理和移动控制
  • Web 陀螺仪简介
  • iv-panorama 简单库介绍

基于 Three.js

全景视频是基于 3D 空间,而在 Web 中,能够非常方便触摸到 3D 空间的技术,就是 WebGL。为了简化,这里就直接采用 Three.js 库。具体的工作原理就是将正在播放的 video 元素,映射到纹理(texture) 空间中,通过 UV 映射,直接贴到一个球面上。精简代码为:

let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100);
// 添加相机 camera.target = new THREE.Vector3(0, 0, 0);
// 设置相机的观察位置,通常在球心 scene = new THREE.Scene();
let geometry = new THREE.SphereBufferGeometry(400, 60, 60);
// 在贴图的时候,让像素点朝内(非常重要)
geometry.scale(-1, 1, 1); // 传入视频 VideoEle 进行绘制
var texture = new THREE.VideoTexture(videoElement); var material = new THREE.MeshBasicMaterial({ map: texture });
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh); renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio); // canvas 的比例
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);

具体的过程差不多就是上面的代码。上面代码中有两块需要注意一下,一个是 相机的视野范围值,一个是几何球体的相关参数设置。

相机视野范围

具体代码为:

let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100); 

这里主要利用透视类型的相机,模拟人眼的效果。设置合适的视野效果,这里的范围还需要根据球体的直径来决定,通常为 2*radius + 100,反正只要比球体直径大就行。

几何球体的参数设置

let  geometry = new THREE.SphereBufferGeometry(400, 60, 60);
// 在贴图的时候,让像素点朝内(非常重要)
geometry.scale(-1, 1, 1);

上面其实有两个部分需要讲解一下

  • 球体参数设置里面有三个属性值比较重要,该 API 格式为:SphereBufferGeometry(radius, widthSegments, heightSegments,...)

    • raidus: 设置球体的半径,半径越大,视频在 canvas 上绘制的内容也会被放大,该设置值合适就行。
    • width/height Segments: 切片数,主要用来控制球体在宽高两个维度上最多细分为多少个三角切片数量,越高纹理拼接的边角越清晰。不过,并不是无限制高的,高的同时性能损耗也是有的。
  • 在几何绘制时,通过坐标变换使 X 轴的像素点朝内,让用户看起来不会存在 凸出放大的效果。具体代码为:geometry.scale(-1, 1, 1)

UV 映射

上面只是简单介绍了一下代码,如果仅仅只是为了应用,那么这也就足够了。但是,如果后面遇到优化的问题,不知道更底层的或者更细节内容的话,就感觉很尴尬。在全景视频中,有两个非常重要的点:

  • UV 映射
  • 3D 移动

这里,我们主要探索一下 UV 映射的细节。UV 映射主要目的就是将 2D 图片映射到三维物体上,最经典的解释就是:

盒子是一个三维物体,正如同加到场景中的一个曲面网络("mesh")方块.
如果沿着边缝或折痕剪开盒子,可以把盒子摊开在一个桌面上.当我们从上往下俯视桌子时,我们可以认为U是左右方向,V是上下方向.盒子上的图片就在一个二维坐标中.我们使用U V代表"纹理坐标系"来代替通常在三维空间使用的 X Y.
在盒子重新被组装时,纸板上的特定的UV坐标被对应到盒子的一个空间(X Y Z)位置.这就是将2D图像包裹在3D物体上时计算机所做的.

from 浙江研报

这里,我们通过代码来细致讲解一下。我们需要完成一个贴图,将如下的 sprite,贴到一个正方体上。

from iefreer

这里,我们先将图片加载到纹理空间:

var material = new THREE.MeshPhongMaterial( { map: THREE.ImageUtils.loadTexture('images/texture-atlas.jpg') } );

那么,现在我们有一个如下的纹理空间区域:

这块内容,就实际涉及到 WebGL 的知识,纹理空间和物理空间并不是在一块,WebGL 中的 GLSL 语法,就是将纹理内容通过相关规则,映射到指定的三角形区域的表面。

这里需要注意的是,纹理空间并不存在所谓的最小三角区域,这里适应的只是在物理空间中划分的三角区域。为了简单起见,我们设置的 boxGeometry 只使用单位为 1 的 Segments,减少需要划分的三角形数量。

这样,就存在 12 块需要贴的三角区域。这里,我们就需要利用 Vector2 来手动划分一下纹理空间的区域,实际在映射的时候,就是按顺序,将物理空间的定点 和 纹理空间的定点一一映射,这样就实现了将纹理和物理空间联系到一起的步骤。

因为,Three.js 中 geometry.faceVertexUvs 在划分物理空间时,定义的面分解三角形的顺序 是 根据逆时针方向,按序号划分,如下图所示:

根据上图的定义,我们可以得到每个几何物体的面映射到纹理空间的坐标值可以分为:

left-bottom = [0,1,3]
right-top = [1,2,3]

所以,我们需要定义一下纹理坐标值:

face1_left = [new THREE.Vector2(0, 0),new THREE.Vector2(.5, 0),new THREE.Vector2(0, .333)]
face1_right = [new THREE.Vector2(.5, 0),new THREE.Vector2(.5, .333),new THREE.Vector2(0, .333)] //... 剩下 10 个面

定点 UV 映射 API 具体格式为:

geometry.faceVertexUvs[ 0 ][ faceIndex ][ vertexIndex ]

则定义具体面的映射为:

geometry.faceVertexUvs[0][0] = face1_left;
geometry.faceVertexUvs[0][0] = face1_right;
//...剩下 10 个面

如果,你写过原生的 WebGL 代码,对于理解 UV 映射原理应该很容易了。

3D 移动原理

这里需要注意的是 Web 全景不是 WebVR。全景没有 VR 那种沉浸式体验,单单只涉及三个维度上的旋转而没有移动距离这个说法。

上面的描述中,提到了三维,旋转角度 这两个概念,很容易让我们想到《高中数学》学到的一个坐标系--球坐标系(这里默认都是右手坐标系)。

  • φ 是和 z 轴正方向 <=180°的夹角
  • ∂ 是和 x 轴正方向 <=180°的夹角
  • p 是空间点距离原点的直线距离

计算公式为:

现在,如果应用到 Web 全景,我们可以知道几个已知条件:

  • p:定义的球体(SphereBufferGeometry)的半径大小
  • ∆φ:用户在 y 轴上移动的距离
  • ∆∂:用户在 x 轴上移动的距离

p 这个是不变的,而 ∆φ 和 ∆∂ 则是根据用户输入来决定的大小值。这里,就需要一个算法来统一协定。该算法控制的主要内容就是:

用户的手指在 x/y 平面轴上的 ∆x/∆y 通过一定的比例换算成为 ∆φ/∆∂

如果考虑到陀螺仪就是:

用户的手指在 x/y 平面轴上的 ∆x/∆y 通过一定的比例换算成为 ∆φ/∆∂,用户在 x/y 轴上旋转的角度值 ∆φ'/∆∂',分别和视角角度进行合并,算出结果。

为了更宽泛的兼容性,我们这里根据第二种算法的描述来进行讲解。上面 ∆φ/∆∂ 的变动主要映射的是我们视野范围的变化。

在 Threejs 中,就是用来控制相机的视野范围。那我们如何在 ThreeJS 控制视野范围呢?下面是最简代码:

phi = THREE.Math.degToRad(90 - lat);
theta = THREE.Math.degToRad(-lon);
camera.position.x = distance * Math.sin(phi) * Math.cos(theta);
camera.position.y = distance * Math.cos(phi);
camera.position.z = distance * Math.sin(phi) * Math.sin(theta);

这里主要模拟地球坐标:

  • lat 代表维度(latitude): 用户上下滑动改变的值,或者手机上下旋转
  • lon 代表经度(lontitude): 用户左右滑动改变的值,或者手机左右旋转

具体内容为:

在通常实践当中,改变全景视角的维度有两种,一种直接通过手滑,一种则根据陀螺仪旋转。

简单来说,就是监听 touchorientation 事件,根据触发信息来手动改变 lat/lon 的值。不过,这里有一个注意事项:

latitude 方向上最多只能达到 (-90,90),否则会造成屏幕翻转的效果,这种体验非常不好。

我们分别通过代码来实践一下。

添加 touch 控制

Touch 相关的事件在 Web 中,其实可以讲到你崩溃为止,比如,用户用几个手指触摸屏幕?用户具体在屏幕上的手势是什么(swipezoom)?

这里,我们简单起见,只针对一个手指滑动的距离来作为 相机 视角移动的数据。具体代码为:

// 为了给自己博客拉量,完整版可以去我的博客查看:
https://www.villainhr.com

iv-panorama 简介

iv-panorama 是 IVWEB 团队,针对于全景直播这个热点专门开发的一个播放器。现在 Web 对 VR 支持度也不是特别友好,但是,对于全景视频来说,在机器换代更新的前提下,全景在性能方面的瓶颈慢慢消失了。其主要特性为:

  • 依赖于 Three.js,需要预先挂载到 window 对象上
  • 灵活配置,内置支持陀螺仪和 touch 控制。
  • 支持灵敏度参数的动态调整
  • 使用 ES6 语法
  • 兼容 React,jQuery(简单凑数的)

项目地址为:iv-panorama。该项目使用非常简单,有两种全景模式,一个是 图片,一个是视频:

import VRPlayer from 'iv-panorama';

new VRPlayer({
player: {
url: '/test/003.mp4'
},
container:document.getElementById('container')
}); // image let panorama = new VRPlayer({
image: {
url: './banner.png'
},
container:document.getElementById('container')
});

全景资源都已经放在 github 仓库了,有兴趣的可以实践观察一下。

现在做 Web 全景合适吗?的更多相关文章

  1. 硬核看房利器——Web 全景的实现

    作者:凹凸曼 - EC 疫情期间,打破社交距离限制的交互模式被推向前台,为不少行业的传统交易提供了想象的空间. 疫情时期,房地产租售业受到的冲击无疑是巨大的,由于人口流动的限制,需求量大幅减少,无法现 ...

  2. 做web开发和测试,修改hosts指定某个域名访问某个特定的IP后,如何使hosts立即生效的方法

    本文转自SUN'S BLOG,原文地址:http://whosmall.com/post/143 hosts的配置方法: 在windows系统中,找到C:\windows\system32\drive ...

  3. 做web项目时对代码改动后浏览器端不生效的应对方法(持续更新)

    做web项目时,常常会遇到改动了代码,但浏览器端没有生效,原因是多种多样的,我会依据我遇到的情况逐步更新解决的方法 1.执行的时候採用debug模式,普通情况下使用项目部署button右边那个butt ...

  4. 做web项目时对代码修改后浏览器端不生效的应对方法(持续更新)

    做web项目时,经常会遇到修改了代码,但浏览器端没有生效,原因是多种多样的,我会根据我遇到的情况逐步更新解决办法 1.运行的时候采用debug模式,一般情况下使用项目部署按钮右边那个按钮下的tomca ...

  5. 为什么做Web开发要选择PHP

    大部分互联网公司做WEb开发都选择PHP,PHP的优势在哪?你应该知道的 以前偶尔被人问到,为什么你(和大部分互联网公司)做Web开发要选择PHP, PHP有什么好处.简单的回答便是“PHP简单,开发 ...

  6. 第一次写博客,就写如何向外行介绍自己做的是什么,那个我是做web的

    如果想外行问你是做什么的,改如何回答.和内行说java后台就可以了,但外行听不懂,我们该如何描述呢? 我的方法是:我做的是java web开发,不是内外的外,是个英文单词web,全名叫world wi ...

  7. Nginx是什么,有什么优点?为什么选择Nginx做web服务器软件?(经典经典)

    1.基础知识 代理服务器:    一般是指局域网内部的机器通过代理服务器发送请求到互联网上的服务器,代理服务器一般作用在客户端.应用比如:GoAgent,FQ神器.    一个完整的代理请求过程为:客 ...

  8. PHP做Web开发的MVC框架(Smarty使用说明 )

    PHP做Web开发的MVC框架(Smarty使用说明 ) 一.Smarty简明教程 1.安装演示 下载最新版本的Smarty-3.1.12,然后解压下载的文件.接下来演示Smarty自带的demo例子 ...

  9. java做web项目比较多

    WEB就是轻量级:如果要炫,FLEX或即将普及的html5.0都能做到像C/S那样. java做web项目比较多:如果是桌面程序,还是走C/S比较成熟. 如果是B/S架构的,后台还是JAVA,前台可以 ...

随机推荐

  1. ibv_get_device_list()函数

    struct ibv_device** ibv_get_device_list(int *num_devices); 描述 函数用来返回一个当前可用的RDMA设备数组. 注意 数组以NULL结尾: R ...

  2. Python字典的创建与复制

    Python 字典练习题 1.字典的创建 1.1 普通创建 d={'name':'Allen','age':21,'gender':'male'} print(d) # {'name': 'Allen ...

  3. cobbler check执行报错

    httpd does not appear to be running and proxying cobbler, or SELinux is in the way. 当执行cobbler check ...

  4. BBS项目(二)

    目录 BBS项目(二) ORM 创建相关表 表模型 修改admin样式 Simple-UI 注册表添加数据 注册forms类编写 注册功能前端搭建 头像实时显示功能实现 BBS项目(二) 可以在本地写 ...

  5. SQL常用数据类型 字段约束

    SQL中的常用数据类型: 整数:int 小数:double 字符串:varchar(长度),建议 用2的整数倍 日期:date 格式: 'YYYY-MM-DD' SQL中的约束: a.主键约束:pri ...

  6. 在数据结构与算法中 传值方式(C语言)

    传值方式 前言 当初学顺序链表的时候,书上就出现了这样的语言,如下所示: Status InitList_Sq(SqList &L) { //构造一个空的线性表L. L.elem = (Ele ...

  7. mac电脑sublime text3安装pretty json插件

    因http://packagecontrol.io/地址被墙无法实现自动安装,导致sublime Text3安装插件非常麻烦,总是出现There Are No Packages Available F ...

  8. Linux 环境下安装 Nexus 私服存储库

    镜像下载.域名解析.时间同步请点击阿里云开源镜像站 一.nexus私服存储库简介 Nexus 是一个强大的maven仓库管理器,它极大地简化了本地内部仓库的维护和外部仓库的访问.,还可以用来创建yum ...

  9. activemq 使用经验

    activemq 使用经验   ActiveMQ 是apache的一个开源JMS服务器,不仅具备标准JMS的功能,还有很多额外的功能.公司里引入ActiveMQ后,ActiveMQ成里我们公司业 务系 ...

  10. 一文了解MySQL的Buffer Pool

    摘要:Innodb 存储引擎设计了一个缓冲池(Buffer Pool),来提高数据库的读写性能. 本文分享自华为云社区<MySQL 的 Buffer Pool,终于被我搞懂了>,作者:小林 ...