什么是全景图?
一般我们拍照都是拍一个方向,而全景图是拍上下左右前后 6 个方向,360 度,这样能够立体的记录所在的场景。
那全景图怎么浏览呢?
全景图拍的是六个方向的图,放在一个平面看会很别扭,所以会有专门的浏览的工具,根据视角的改变来切换看到的内容,这样就能 360 度的还原拍照的场景。
用 Three.js 做这样的一个全景图浏览工具,是再简单不过的事情,只需要几行代码,但却很有用。
那我们就来学一下 Three.js 怎么做全景图浏览吧。
Three.js 基础回顾
我们简单回顾下 Three.js 的基础:
Three.js 是通过场景 Scene 来管理 3D 场景中的各种物体的,有一个三维坐标系,每个物体放在不同的位置,然后在某个位置放置相机,来观察 Scene 中的各种物体,看到的内容就是二维的,通过渲染器 Renderer 渲染出来就行。这就是 Three.js 的 3D 场景的创建和渲染成 2D 的流程。
简单回顾了下基础,那全景图改怎么浏览呢?
全景图浏览的原理
全景图是六个方向的照片,我们可以在 3D 的场景中放一个立方体,六个面贴上不同方向的图,相机放在其中,转动相机就可以看到不同方向的内容。这也是为什么全景图浏览也叫天空盒,因为就是通过立方体贴图的方式实现的。
当然,也可以用球体来做,直接贴上一个大的全景图,相机放在中间,转动相机也可以看到不同方向的内容。
那这么说做全景图浏览需要先创建个立方体或者球体喽?
其实不用,场景 Scene 是可以设置背景的纹理的,我们可以设置成立方体纹理 CubeTexture,也就是 6 个面的图片,这样转动相机,就能看到场景 Scene 的不同方向的内容。根本不用单独创建立方体或球体。
设置个纹理也就几行代码的事情,我们来写下代码。
Three.js 实现全景图浏览
我们创建 3D 场景 Scene:
constscene=newTHREE.Scene();
然后设置它的背景,用立方体的纹理来设置,需要分别指定左右上下前后的 6 个方向的图:
leturls=['./img/home.left.jpg','./img/home.right.jpg','./img/home.top.jpg','./img/home.bottom.jpg','./img/home.front.jpg','./img/home.back.jpg'];letcubeTexture=newTHREE.CubeTextureLoader().load(urls);scene.background=cubeTexture;
这样整个背景就是一个全景图,就这么几行代码。
当然,我们还要设置下相机位置,这里用透视相机就行,它的特点是从一个点去看 3D 场景,看到的内容是近大远小的。
constwidth=window.innerWidth;constheight=window.innerHeight;constcamera=newTHREE.PerspectiveCamera(45,width/height,0.1,1000);camera.position.set(0,0,100);camera.lookAt(scene.position);
需要设置看到的角度,这里设置了 45 度,看到内容的宽高比,这里用窗口宽高比,再就是远近范围,这个设置范围大一点就行。
相机位置设置在了 z 轴 100 的位置,这样看 z 为 0 的位置就是从正面去看的,可以感受下这个看的方向。
有了 3D 的 Scene,设置好了相机,就可以用 Renderer 把它渲染出来了。
constrenderer=newTHREE.WebGLRenderer();renderer.setSize(width,height);document.body.appendChild(renderer.domElement)functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();
我们用 requestAnimationFrame 来一帧帧的调用 renderer 渲染。
当然,还要加上鼠标控制,可以通过鼠标的拖动方向来改变相机看到的角度,这个用 Three.js 提供的 Controls 就行,不用自己写。
我们需要 360 度的看,用 OrbitsControls 来做交互就行,他叫轨道控制器,也就是卫星绕地球的那种轨道的感觉。
constcontrols=newTHREE.OrbitControls(camera);
OrbitControls 参数是 camera,因为它就是通过改变 camera 位置实现的。
至此,我们就实现了全景图的浏览。来看下效果:
全部代码上传了 github: https://github.com/QuarkGluonPlasma/threejs-exercize
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>全景图</title><style>body{margin:0;overflow:hidden;}</style><scriptsrc="./js/three.js"></script><scriptsrc="./js/OrbitControls.js"></script></head><body><script>constwidth=window.innerWidth;constheight=window.innerHeight;constcamera=newTHREE.PerspectiveCamera(45,width/height,0.1,1000);constscene=newTHREE.Scene();constrenderer=newTHREE.WebGLRenderer();camera.position.set(0,0,100);camera.lookAt(scene.position);renderer.setSize(width,height);document.body.appendChild(renderer.domElement)functioncreate(){leturls=['./img/home.left.jpg','./img/home.right.jpg','./img/home.top.jpg','./img/home.bottom.jpg','./img/home.front.jpg','./img/home.back.jpg'];letcubeTexture=newTHREE.CubeTextureLoader().load(urls);scene.background=cubeTexture;}functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}create();render();constcontrols=newTHREE.OrbitControls(camera);</script></body></html>
一共也没几行代码。
我们来做下小结:
全景图浏览不用创建立方体或者球体,直接给场景(Scene)设置立方体纹理(CubeTexture)的背景就可以了,贴上 6 张图。之后设置下相机(Camera)位置,用渲染器(Renderer)一帧帧渲染出来,还要加上轨道控制器来支持拖拽改变相机位置。
主要的逻辑讲完了,但还有一个支线剧情要讲:6 张图是怎么来的?
全景图转 6 张贴图
全景图网上能搜到很多,我们手机的相机也都能拍全景图,但是它是一张完整的大图,而立方体纹理要加载 6 张不同方向的图,如果把全景图裁切成 6 张图呢?
这个有工具来做,我是用的 PTGui (试用版)来做的裁切。
官网有下载地址:https://www.ptgui.com/download.html?ps=main
点击 tools 里面的 convert to cube faces,会打开一个窗口,然后选择一个全景图,设置导出的格式,点导出就行了,就能生成上下左右前后的六个方向的图。
总结
一般的照片只是一个方向的画面,而全景图是上下左右前后 360 度的画面,它能立体的记录拍照位置的场景。
全景图需要专门的工具来浏览,我们可以用 Three.js 来实现。原理就是通过立方体贴 6 张图(也叫天空盒),或者通过球体贴一张大图,把相机设置在中间,转动相机就可以看到不同方向的画面。
其实实现全景图浏览更简单的方式是直接给 Scene 设置立方体纹理,不用再单独创建立方体或球体,用 CubeTextureLoader 加载六张图,设置到 Scene 的背景上就行。
还要设置下相机,加上轨道控制器,通过渲染器一帧帧的渲染出来,这样就实现了全景图浏览的功能。
至于那六张贴图,通过 PTGui 或者类似的工具就可以裁切出来。
全景图浏览一共也没几行代码,但是这个功能还是很有用的。如果你会拍全景图,那就更棒了,可以把生活中一些场景立体的记录下来,自己写一个工具来浏览。