资讯专栏INFORMATION COLUMN

现在做 Web 全景合适吗?

psychola / 3523人阅读

摘要:全景在以前带宽有限的条件下常常用来作为街景和全景图片的查看。后面,我们来了解一下,如何在端实现全景视频。现在对支持度也不是特别友好,但是,对于全景视频来说,在机器换代更新的前提下,全景在性能方面的瓶颈慢慢消失了。

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 仓库了,有兴趣的可以实践观察一下。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90628.html

相关文章

  • React VR 快速入门完全教程

    摘要:快速入门什么是是一个开放源代码的库,为呈现的数据提供了视图渲染。最后,项目根组件应该通过来进行注册,以便能够进行打包和正常运行。基本思想是渲染一个立方体,并将观众置于中心,随后移动。表示从指定方向平均照亮所有物体的光源。 React VR 快速入门 什么是React React是一个开放源代码的JavaScript库,为HTML呈现的数据提供了视图渲染。React视图通常使用指定的像H...

    andot 评论0 收藏0
  • CSS 3D Panorama(全景) - 淘宝造物节技术剖析

    摘要:淘宝造物节的活动页就是全景的一个很赞的页面,它将全景图分割成等份,相邻的元素构成的夹角,相邻两侧面相对于棱柱中心所构成的夹角。 本文转自凹凸实验室:https://aotu.io/notes/2016/08... showImg(https://segmentfault.com/img/remote/1460000011381045); 前言 3D 全景并不是什么新鲜事物了,但以前...

    LiuRhoRamen 评论0 收藏0
  • css3d-engine源码学习简析

    摘要:全景旋转首先学习一下基础坐标系这个只要记住一下轴各自方向就可以,下面分析会用到。 开始 从这里开始准备攻略webgl(准备挖新坑),Flutter框架当然也会继续补充,但是今天学习的不是webgl,而是css3d-engine这个库,因为之前搞活动看到了一个全景旋转活动就是使用这个库完成,颇为惊艳(一开始以为是webgl实现的,但是看了代码才知道用CSS3就可以完成,虽然觉得还是应该用...

    pinecone 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<