细数实现全景图VR的几种方式(panorama/cubemap/eac)
- Three.js系列: 在元宇宙看电影,享受 VR 视觉盛宴
- Three.js系列: 造个海洋球池来学习物理引擎
- Three.js系列: 游戏中的第一、三人称视角
- 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://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
细数实现全景图VR的几种方式(panorama/cubemap/eac)的更多相关文章
- 刚開始学习的人非常有用:struts2中将jsp数据传到action的几种方式
先给上struts.xml代码: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE strut ...
- 细数iOS上的那些安全防护
细数iOS上的那些安全防护 龙磊,黑雪,蒸米 @阿里巴巴移动安全 0x00 序 随着苹果对iOS系统多年的研发,iOS上的安全防护机制也是越来越多,越来越复杂.这对于刚接触iOS安全的研究人员来说非 ...
- 细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一
细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一(转) ADO.NET Entity Framework ADO.NET Entity Framework 是微软以 ADO.N ...
- 细数Python Flask微信公众号开发中遇到的那些坑
最近两三个月的时间,断断续续边学边做完成了一个微信公众号页面的开发工作.这是一个快递系统,主要功能有用户管理.寄收件地址管理.用户下单,订单管理,订单查询及一些宣传页面等.本文主要细数下开发过程中遇到 ...
- 细数 Windows Phone 灭亡的七宗罪(过程很详细,评论很精彩,但主要还是因为太慢了,生态跟不上,太贪了,厂商不愿意推广)
曾梦想仗剑走天涯,看一看世界的繁华 年少的心有些轻狂,如今你四海为家 曾让你心疼的姑娘,如今已悄然无踪影 犹记得上大学攒钱买了第一台智能手机Lumia 520时,下载的第一首歌曲<曾经的你> ...
- 细数Qt开发的各种坑(欢迎围观)
1:Qt的版本多到你数都数不清,多到你开始怀疑人生.从4.6开始到5.8,从MSVC编译器到MINGW编译器,从32位到64位,从Windows到Linux到MAC.MSVC版本还必须安装对应的VS2 ...
- 迄今最安全的MySQL?细数5.7那些惊艳与鸡肋的新特性(上)【转载】
转自: DBAplus社群 http://www.toutiao.com/m5762164771/ 迄今最安全的MySQL?细数5.7那些惊艳与鸡肋的新特性(上) - 今日头条(TouTiao.com ...
- android企业级商城源码、360°全景图VR源码、全民直播源码等
Android精选源码 [新版]Android技术博客精华汇总 开源了:乐乐音乐5.0-Android音乐播放器 android实现仿真水波纹效果源码 360°全景图VR,这是一个值得把玩的APP a ...
- 180724-统计JVM进程中线程数两种方式小记
I. 统计进程中的线程数 相关系列博文推荐: 180711-JVM定位分析CPU性能消耗 180704-JDK常用监控参数 jvm调优的工具介绍 1. proc查询 /proc 目录以可读文本文件形式 ...
随机推荐
- LVGL库入门教程03-布局方式
LVGL布局方式 LVGL的布局 上一节介绍了如何在 LVGL 中创建控件.如果在创建控件时不给控件安排布局,那么控件默认会被放在父容器的左上角. 可以使用 lv_obj_set_pos(obj, x ...
- Java上传文件至SFTP服务器
Windows搭建SFTP服务器 https://www.cnblogs.com/wangjunguang/p/9453611.html 注意点: 1.以管理员权限运行FreeSSHd 2.如果无法启 ...
- JS:this关键字1
this 代表了当前的对象,哪个对象调用了this所在的函数,this就代表了哪个对象: 例1: function fn(){ var a = 1; console.log(this) } fn() ...
- 针对elementUI 中InfiniteScroll按需引入的一点注意事项
大家为了节省空间,常常进行按需引入来节省空间,这里我给大家来介绍一下element中按需引入无限滚动指令注意的事项. 针对前面element 按需引入的一些配置这里就不再详细介绍了. 那么这里讲的是在 ...
- XMAL中的x是何方神僧
在一开始我们接触WPF时,总是被小X牵着鼻子走,还不知道它是谁,比如 <Window x:Class="Blend_WPF.WindowStyle" xmlns ...
- 详细图解 Netty Reactor 启动全流程 | 万字长文 | 多图预警
本系列Netty源码解析文章基于 4.1.56.Final版本 大家第一眼看到这幅流程图,是不是脑瓜子嗡嗡的呢? 大家先不要惊慌,问题不大,本文笔者的目的就是要让大家清晰的理解这幅流程图,从而深刻的理 ...
- Java变量和Scanner类
1.变量的分类1)按数据类型分类 详细说明: 1. 整型:byte(1字节=8bit) \ short(2字节) \ int(4字节) \ long(8字节) ① byte范围:-128 ~ ...
- RocketMQ之消费者启动与消费流程
vivo 互联网服务器团队 - Li Kui 一.简介 1.1 RocketMQ 简介 RocketMQ是由阿里巴巴开源的分布式消息中间件,支持顺序消息.定时消息.自定义过滤器.负载均衡.pull/p ...
- SQLZOO练习(一)SELECT BASICS,SELECT form world
name continent area population gdp Afghanistan Asia 652230 25500100 20343000000 Albania Europe 28748 ...
- Java开发学习(十三)----基于注解开发定义第三方bean及注解开发总结
在前面的博客中定义bean的时候都是在自己开发的类上面写个注解就完成了,但如果是第三方的类,这些类都是在jar包中,我们没有办法在类上面添加注解,这个时候该怎么办? 遇到上述问题,我们就需要有一种更加 ...