Three.js 实现 360 度全景浏览的最简单方式

9,007 阅读5分钟

什么是全景图?

一般我们拍照都是拍一个方向,而全景图是拍上下左右前后 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:

const scene = new THREE.Scene();

然后设置它的背景,用立方体的纹理来设置,需要分别指定左右上下前后的 6 个方向的图:

let urls = [
    './img/home.left.jpg',
    './img/home.right.jpg',
    './img/home.top.jpg',
    './img/home.bottom.jpg',
    './img/home.front.jpg',
    './img/home.back.jpg'
];
let cubeTexture = new THREE.CubeTextureLoader().load(urls);
scene.background = cubeTexture;

这样整个背景就是一个全景图,就这么几行代码。

当然,我们还要设置下相机位置,这里用透视相机就行,它的特点是从一个点去看 3D 场景,看到的内容是近大远小的。

const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.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 把它渲染出来了。

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement)

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

我们用 requestAnimationFrame 来一帧帧的调用 renderer 渲染。

当然,还要加上鼠标控制,可以通过鼠标的拖动方向来改变相机看到的角度,这个用 Three.js 提供的 Controls 就行,不用自己写。

我们需要 360 度的看,用 OrbitsControls 来做交互就行,他叫轨道控制器,也就是卫星绕地球的那种轨道的感觉。

const controls = new THREE.OrbitControls(camera);

OrbitControls 参数是 camera,因为它就是通过改变 camera 位置实现的。

至此,我们就实现了全景图的浏览。来看下效果:

全部代码上传了 github: github.com/QuarkGluonP…

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>全景图</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="./js/three.js"></script>
    <script src="./js/OrbitControls.js"></script>
</head>
<body>
<script>
    const width = window.innerWidth;
    const height = window.innerHeight;
    const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

    const scene = new THREE.Scene();
    const renderer = new THREE.WebGLRenderer();

    camera.position.set(0,0, 100);
    camera.lookAt(scene.position);

    renderer.setSize(width, height);
    document.body.appendChild(renderer.domElement)

    function create() {
        let urls = [
            './img/home.left.jpg',
            './img/home.right.jpg',
            './img/home.top.jpg',
            './img/home.bottom.jpg',
            './img/home.front.jpg',
            './img/home.back.jpg'
        ];
        let cubeTexture = new THREE.CubeTextureLoader().load(urls);
        scene.background = cubeTexture;
    }

    function render() {
        renderer.render(scene, camera);
        requestAnimationFrame(render);
    }

    create();
    render();
    const controls = new THREE.OrbitControls(camera);

</script>
</body>
</html>

一共也没几行代码。

我们来做下小结:

全景图浏览不用创建立方体或者球体,直接给场景(Scene)设置立方体纹理(CubeTexture)的背景就可以了,贴上 6 张图。之后设置下相机(Camera)位置,用渲染器(Renderer)一帧帧渲染出来,还要加上轨道控制器来支持拖拽改变相机位置。

主要的逻辑讲完了,但还有一个支线剧情要讲:6 张图是怎么来的?

全景图转 6 张贴图

全景图网上能搜到很多,我们手机的相机也都能拍全景图,但是它是一张完整的大图,而立方体纹理要加载 6 张不同方向的图,如果把全景图裁切成 6 张图呢?

这个有工具来做,我是用的 PTGui (试用版)来做的裁切。

官网有下载地址:www.ptgui.com/download.ht…

点击 tools 里面的 convert to cube faces,会打开一个窗口,然后选择一个全景图,设置导出的格式,点导出就行了,就能生成上下左右前后的六个方向的图。

总结

一般的照片只是一个方向的画面,而全景图是上下左右前后 360 度的画面,它能立体的记录拍照位置的场景。

全景图需要专门的工具来浏览,我们可以用 Three.js 来实现。原理就是通过立方体贴 6 张图(也叫天空盒),或者通过球体贴一张大图,把相机设置在中间,转动相机就可以看到不同方向的画面。

其实实现全景图浏览更简单的方式是直接给 Scene 设置立方体纹理,不用再单独创建立方体或球体,用 CubeTextureLoader 加载六张图,设置到 Scene 的背景上就行。

还要设置下相机,加上轨道控制器,通过渲染器一帧帧的渲染出来,这样就实现了全景图浏览的功能。

至于那六张贴图,通过 PTGui 或者类似的工具就可以裁切出来。

全景图浏览一共也没几行代码,但是这个功能还是很有用的。如果你会拍全景图,那就更棒了,可以把生活中一些场景立体的记录下来,自己写一个工具来浏览。