乐趣区

关于javascript:细数实现全景图VR的几种方式panoramacubemapeac

  1. Three.js 系列: 在元宇宙看电影,享受 VR 视觉盛宴
  2. Three.js 系列: 造个陆地球池来学习物理引擎
  3. Three.js 系列: 游戏中的第一、三人称视角
  4. Three.js 系列: 数实现全景图 VR 的几种形式(panorama/cubemap/eac)

本篇是 Three.js 系列的第四篇内容,想看其余内容能够看上方☝️,明天就给大家来介绍介绍全景图相干的常识,咱们晓得因为最近疫情的影响,大家都没方法出门,很多全景的我的项目火了,比方各个驰名的风景区都凋谢了 VR。

除此之外,VR 设施也是十分的炽热,在我国 2022 年上半年,VR 市场销售额冲破了 8 亿元,同比增长了 81%

而在国外呢,截至 2022 年 Q1,曾经卖出了 1480 万台!

因为咱们学习制作 VR 技术就是趁势而为,毕竟雷布斯说过,“站在风口上
,猪都能够飞起来”。

接下来我就谈谈目前展现次要有几种模式。目前展现 VR 次要有 3 种 支流形式,别离为 建模、建模 + 全景图 和 全景图

建模 建模 + 全景图 全景图
代表作品 VR 游戏 贝壳系列看房 一般云旅行、云旅行
体验 极好 中等

咱们来理论体验一下他们的差别

以上为 VR 游戏《雇佣兵士》的体验,视角切换十分的晦涩,且场景十分的大,玩过 3D 类型游戏的敌人就能明确。这种场景都是通过建模来实现,利用 blender、3D Max、maya 等建模软件,再应用 Unity、UE 等游戏开发平台,各种成果能够说十分的好。

而到了贝壳这种呢,则是通过建模加上全景图两种形式联合应用,模型和全景图是通过线下采集失去,然而对于这种看房页面,要在 Web 上渲染精密的模型资源耗费是微小的,因而他们采取了一个折中的计划,就是毛糙模型 + 全景图,通过模型来补间场景切换的渐变感,变动过程中显著能感触的掉帧的感觉。尽管成果不如纯手动建模来的精密,然而总的来说体验也十分不错了。

最初这种云旅行,则是间接通过两张全景图间接切换失去的,这种形式最为简略,当然成果远远后面两种,然而单张图片的全景视角比起动态的图片而言,也是减少了空间感。

用表格总结起来就是以下:

建模 建模 + 全景图 全景图
代表作品 VR 游戏《雇佣兵士》 贝壳系列看房 一般云旅行、云旅行
实现难度 很难 简略
过渡成果 极度实在 个别
模型 Blender、3D Max、maya 带有光学传感相机 一般 360 相机

因为全景图是通过一个个点位拍摄而失去的。因而它无奈领有地位信息,也就是各个点位的依赖关系,因而当在切换场景的时候,咱们无奈失去沉迷式的过渡成果;而贝壳则是通过利用了模型的补间来改善过渡;VR 游戏《雇佣兵士》则是纯手动建模,因而成果十分好。

明天咱们次要解说的就是全景图模式 (因为它比较简单),当然也不是设想中那么简略,只是相比前两种形式而言,难度是降落了一个坡度,毕竟学习都是从趣味开始的,一开始来个高难度的,几乎就是劝退了。

首先咱们先来理解一些前置常识,目前支流全景图都有什么格局?

我翻阅总结后目前最罕用的大概为以下三种

  • 等距柱状投影格局(Equirectangular)
  • 等角度立方体贴图格局(Equi-Angular Cubemap)
  • 立方体贴图(Cube Map )

等距柱状投影

也就是最常见的世界地图的投影形式,做法是将经线和纬线等距地(或有疏密地)投影到一个矩形立体上。

这种格局的长处是比拟直观,并且投影是矩形的。毛病也很显著,球体的高低两极投影进去的像素数很多,而细节内容比拟丰盛的赤道区域相比来说像素数就很少,导致还原时清晰度比拟蹩脚。另外,这种格局的画面在未渲染的状况下扭曲比拟显著。

立方体贴图

是另一种全景画面的贮存格局,做法是将球体上的内容向外投影到一个立方体上,而后开展,而它比照等距柱状投影的劣势是,在雷同分辨率下,它的图片体积更小,约为 等距柱状投影 的 1/3

等角度立方体贴图

是谷歌所提出的进一步优化的格局,办法是更改优化投影时的采样点地位,使得边角与核心的像素密度相等。

这样做的益处就是在雷同的源视频分辨率下能够进步细节局部的清晰度。排版如下:

咱们简略总结一下:

等距柱状投影 立方体贴图 等角度立方体贴图
图源 简略 个别
技术实现 简略 简略 个别
图片体积 V 1/3 V 1/3 ~ 1/4 V
图片清晰度 个别 更好

v 为基准体积

接下来就到了咱们应用 Three.js 来实现以上成果的时刻了。

等距柱状投影

这种形式实现起来比较简单。首先咱们在 https://www.flickr.com/ 找一张全景图。

在后面的介绍中咱们能够失去 2:1 的等距投影全景图对应的几何体为球形,还记得咱们在前《造一个陆地球》学过,如何来创立一个球体,没错就是 **SphereGeometry**

... 省略场景初始化等代码

// 创立一个球体
const geometry = new THREE.SphereGeometry(30, 64, 32);

// 创立贴图, 并设置为红色
const material = new THREE.MeshBasicMaterial({color: "red",});

// 创建对象

const skyBox = new THREE.Mesh(geometry, material);

// 增加对象到场景中

scene.add(skyBox);

// 设置在远处观看
camera.position.z = 100
...

而后咱们就失去了一个小红球:

嗯,当初为止你曾经学会了如果创立一个小红球,接下来还有 2 个步骤加油!

接下来呢,咱们就将咱们的 2:1 的全景图贴到咱们的球体上

const material = new THREE.MeshBasicMaterial({ 
-    //color: "red",
+    map: new THREE.TextureLoader().load('./images/panorama/example.jpg')
});

咱们就失去了一个相似地球仪的球体。

当初咱们要做的,就是咱们不想在远处观看这些内容,而是要“身临其境”!

所以咱们须要把相机挪动到球体的外部,而不是在远处观看

- camera.position.z = 100
+ camera.position.z = 0.01

这个时候咱们发现,忽然乌黑一篇。

小问题,这是因为在 3d 渲染中,默认物体只会渲染一个面,这也是为了节俭性能。当然咱们能够也通过让物体默认只渲染外部,这就须要通过申明贴图的法线方向了,过程不是本节课的探讨范畴这里只提供一个思路。幸好 Three.js 给咱们提供了一个简略的办法 THREE.DoubleSide,通过这个办法,就能让咱们的物体渲染两个面。这样咱们即便在物体外部也能看到贴图啦。

const material = new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load('./images/panorama/example.jpg'),
+    side: THREE.DoubleSide,
});

当初咱们只用了 **SphereGeometry** 球体疾速实现了全景的成果。

立方体贴图

立方体贴图就和它的名字一样,咱们只须要应用一个立方体就能渲染进去一个全景成果,然而 2:1 的全景图必定是不能间接应用的,咱们首先须要通过 工具来进行转化,目前有两种比拟不便的形式。

  • https://jaxry.github.io/panorama-to-cubemap/
  • ffmpeg 5.x 应用命令 ffmpeg -i example.jpg -vf v360=input=equirect:output=c3x2 example-cube.jpg

最终咱们能够失去以下 6 张图

开始来写咱们的代码


... 省略场景初始化等代码
// 创立立方体
const box = new THREE.BoxGeometry(1, 1, 1);

// 创立贴图
function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) {const textures = [];
    for (let i = 0; i < tilesNum; i++) {textures[i] = new THREE.Texture();}
    new THREE.ImageLoader()
        .load(atlasImgUrl, (image) => {
            let canvas, context;
            const tileWidth = image.height;
            for (let i = 0; i < textures.length; i++) {canvas = document.createElement('canvas');
                context = canvas.getContext('2d');
                canvas.height = tileWidth;
                canvas.width = tileWidth;
                context.drawImage(image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth);
                textures[i].image = canvas;
                textures[i].needsUpdate = true;
            }
        });
    return textures;
}

const textures = getTexturesFromAtlasFile('./images/cube/example-cube.jpg', 6);
const materials = [];

for (let i = 0; i < 6; i ++) {
    materials.push( new THREE.MeshBasicMaterial( {map: textures[ i],
        side: THREE.DoubleSide
    } ) );
}
const skyBox = new THREE.Mesh(box, materials);
scene.add(skyBox);
...

这里有一个留神点,就是在 Three.js 中如果有多张贴图,是反对以数组模式传入的,例如此例子中,传入的程序为“左右高低前后”

此时咱们也失去了上方一样的成果。

等角度立方体贴图

这里也和 cubemap 一样,咱们须要通过工具转化能力失去对应格局的图片。这里只须要了 5.x 的 ffmpeg,因为它自带一个 360 filter,可能解决 EAC 的转化。首先通过以下命令失去一张 eac 的图。

ffmpeg -i example.jpg -vf v360=input=equirect:output=eac example-eac.jpg

这里因为 Three.js 默认不反对 EAC 的渲染,因而咱们应用了一个 egjs-view360 来进行渲染,原理为本人手写一个 shader 来解决 EAC 这种状况,这里临时先不开展解说,过程比拟干燥,后续单开一个章节来阐明。

应用 egjs-view360 来渲染 EAC 图,整体比较简单

... 省略依赖库
<div class="viewer" id="myPanoViewer">
</div>

<script>

    var PanoViewer = eg.view360.PanoViewer;
    var container = document.getElementById("myPanoViewer");
    var panoViewer = new PanoViewer(container, {
        image: "./images/eac/example-eac.jpg",
        projectionType: "cubestrip",
        cubemapConfig: {
            order: "BLFDRU",
            tileConfig: [{rotation: 0}, {rotation: 0}, {rotation: 0}, {rotation: 0}, {rotation: -90}, {rotation: 180}],
            trim: 3
        }
    });

    PanoControls.init(container, panoViewer);
    PanoControls.showLoading();
</script>

咱们最终也能失去以上的后果。

这里再给一组文件体积的数据:(所有图片对立应用了 tinypng 进行了压缩去除有效信息)

最终得出了一个这样的排名:

体验:EAC > CubeMap > Equirectangular

文件体积:CubeMap < EAC < Equirectangular

上手难度:EAC < CubeMap < Equirectangular

所以如果你想要高体验高画质,那么你就抉择 EAC,如果想要带宽小,那么就抉择 CubeMap,如果你是个初学者想要疾速实现成果,那么就应用 Equirectangular!

以上所有代码均在:https://github.com/hua1995116/Fly-Three.js 中能够找到。

这里最初补充一个小提示,球形贴图的一个益处就是人造地能够作为小行星的展现,例如这种特效。

本期咱们通过了从 VR 的倒退现状、VR 的几种实现形式,再到通过 Equirectangular、CubeMap、Equi-Angular Cubemap 三种全景图来实现 VR 进行了解说,心愿对你有所帮忙,咱们下期再见。👋🏻

参考资料

https://www.bilibili.com/read/cv788511

https://www.trekview.org/blog/2021/projection-type-360-photography/

https://ourcodeworld.com/articles/read/843/top-7-best-360-degrees-equirectangular-image-viewer-javascript-plugins

https://jiras.se/ffmpeg/equiangular.html

https://blog.google/products/google-ar-vr/bringing-pixels-front-and-center-vr-video/

https://jiras.se/ffmpeg/mono.html

https://ffmpeg.org/ffmpeg-filters.html#v360

https://blog.csdn.net/qiutiantxwd/article/details/107283224

退出移动版