three.js 几种搭建全景图的方案

之后可能会做一个类似全景导航的项目,最近手里没活,就调研一下怎么做;

参照demo:是贝壳找房“VR看房”中的全景模式

全景图效果,网上有很多叫天空盒(SkyBox)的,不知这个名字从何而来;

简介

单纯的做全景图效果,方法比较多,根据几何体结构的不同,分为两种情况:

一种是球面全景图,素材如图1,就是将图片绘制到一个球体上,相机在球体中心,这样就能看到全景图了,具体效果及代码可查看 Threejs官方demo,效果并不是很好,底部有很明显的错位,也有可能是素材的问题,具体没有仔细研究,看个大概就好,这种方式并不是主流的全景图制作方案;

图1 – 球面全景图素材

另一种是立方体全景图,素材又分为两类:

  • 一种是一张全景图如图2,效果和代码可查看 Threejs官方demo
  • 一种是六张拆开的小图如图3,这里可以找到资源图片,下载下来就是单独的6张图;
图2 – 立方体全景图素材1
图3 – 立方体全景图素材2

看到网上说还有用圆柱体实现全景图的,没找到对应的demo,所以就不列举了;

本文主要讨论用六张小图搭建一个立方体全景图的实现方式;

1、CSS3DRender 方案

DEMO

自制demo官方demo参考

实现方式

六个图片最终在页面中生成6个img标签,用CSS做出3D效果;

缺点

不能使用材质(Material)和几何(Geometries),只能将DOM元素包装到特殊的对象中,然后添加到渲染器下,最终生成都是DOM元素

2、Scene 背景方案

DEMO

自制demo官方demo1官方Demo2

实现方式

将6张图放到一个立方体纹理(CubeTexture)上,然后设置为场景(Scene)的背景;

缺点

这种方式下,场景中的物体是没办法超出背景的,比如一个立方体,一半在场景里,一半在场景外,这种效果是实现不了的,不过算不上缺点,毕竟全景图下,不会有这种需求;

主要代码

var urls = ['right.png', 'left.png', 'up.png', 'down.png', 'front.png', 'back.png'];
var textureCube = new THREE.CubeTextureLoader().load(urls);
textureCube.mapping = THREE.CubeRefractionMapping;
scene.background = textureCube;

上面的代码中,urls中的图片是有顺序的,我喜欢用前后左右上下来命名图片;如果换成坐标系的名称,则为:[x正向,x反向,y正向,y反向,z正向,z反向],官方文档是这么描述的:

这些URL将被指定顺序: pos-x, neg-x, pos-y, neg-y, pos-z, neg-z. 数组内容也可以为 Data URIs.

请注意,一般来说,在立方体贴图坐标系中,当查找positive-z轴时,positive-x表示右侧 – 换句话说,此坐标系使用左手坐标系。由于three.js使用右手坐标系,环境贴图将在three.js进行pos-x和neg-x进行交互.

3、立方体 + MeshBasicMaterial 方案

DEMO

自制demo官方demo参考(这个官方demo不是很准确,因为他的素材是六张小图组成的一张大图);

实现方式

在场景中加一个立方体,立方体六个面分别对应一个材质(MeshBasicMaterial),每个材质对应一个纹理图片;相机(Camera)放在立方体的中心,这种情况还要设置立方体结构的scale,x或z设置为-1,这样才能正常运行,否则是黑乎乎一片,什么都看不见;

*关于设置立方体结构的scale,我是这样理解的,在一个盒子内部,是看不到盒子表面的,如果用scale把一个方向缩放至-1,则把里外两侧的表面翻转,这时候就能看到了图片;

单独翻转x或z是因为前后左右四个面在x和z轴上,翻转只表示四个面的位置做了调换,不影响观感,y轴是上下两个面,翻转则整个场景就翻转了;当然,如果同时设置x和z为-1,则还是黑乎乎,没有效果,因为翻转两次就又把图片翻到外面了;

主要代码

var materials = [];
var urls = ['right.png', 'left.png', 'up.png', 'down.png', 'front.png', 'back.png'];
for (var i = 0; i < urls.length; i++) {
  var texture = new THREE.TextureLoader().load(urls[i]);
  var material = new THREE.MeshBasicMaterial({map: texture, transparent: true, opacity: 1});
  material.opacity = .99;  // 改变透明度
  materials.push(material);
}

var size = 1000;
var skyBox = new THREE.Mesh(new THREE.BoxGeometry(size, size, size), materials); // 或 BoxBufferGeometry
skyBox.geometry.scale(1, 1, -1);

scene.add(skyBox);

这段代码中的urls,和“2、Scene背景方案”中的逻辑一致;

*这里还有一个地方存疑,MeshBasicMaterial构造函数中,还可以设置一个side:THREE.BackSide,这样就不用再设置 x 或 z 的 scale 了;BackSide 表示将纹理设置在背面,不用翻转就能看到图片了;但实际用的时候,左右两个面做了镜像翻转,比如demo中桥头一侧有个白塔,正常应该在桥头的左侧,如果按照上面的方案,就成了右侧;

具体的 demo 代码中,还引入了一个 OrbitControls.js,这个算是官方的 camera 控制器,使用方法在官方文档里能找到,源代码在这儿;每个控制器都封装了某一种或几种特定场景下,鼠标、键盘对 camera 位置和朝向的控制操作;

针对该demo,简单来说,引入这个js并初始化,就不用再为了关联鼠标和场景操作添加各种事件了;

controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = false;  // 是否启用放大/缩小
controls.enablePan = false;  // 是否启用摄像机平移
controls.enableDamping = true; // 是否启用缓冲效果
controls.dampingFactor = 0.1;  // 缓冲效果下的加速度
controls.rotateSpeed = -0.06;  // 滚动的速度
  • enablePan:是否启用摄像机平移,不明白什么意思,该demo中true和false都不影响效果;
  • enabledDamping:鼠标拖拽结束后,场景是否进行一个减速运动;如果设为false,mouseup时,场景马上停止运动;
  • dampingFactor:enabledDamping为true才起作用,缓冲效果的加速度,值越大,鼠标up后,移动的距离越大;
  • rotateSpeed:滚动的速度,和鼠标的移动距离成比例,小于0,鼠标和场景移动方向一致,大于0,相反;

4、立方体 + ShaderMaterial 方案

DEMO

自制demo官方demo参考

实现方式

和3的差别,仅仅在材质上,3中用的是在MeshBasicMaterial材质,这里用的是ShaderMaterial材质;

主要代码

var texture = new THREE.CubeTextureLoader().load(urls);

var shader = THREE.ShaderLib['cube'];
shader.uniforms['tCube'].value = texture;
shader.uniforms['opacity'].value = 1;

var shaderMaterial = new THREE.ShaderMaterial({
  fragmentShader: shader.fragmentShader,
  vertexShader: shader.vertexShader,
  uniforms: shader.uniforms,
  side: THREE.BackSide
});

var size = 1000;
var skyBox = new THREE.Mesh(new THREE.BoxGeometry(size, size, size), shaderMaterial);
//skyBox.geometry.scale(1, 1, -1);
skyBox.name = 'skyBox';

scene.add(skyBox);

这里 side:THREE.BackSide 和 skyBox.geometry.scale(1, 1, -1); 都可以实现翻转,任选一种方案即可;至于为什么没有3中的镜像翻转问题,我还没有搞清楚;

如果这篇文章对你有用,可以点击下面的按钮告诉我

2

One thought on “three.js 几种搭建全景图的方案

发表回复