众所周知实现全景有很种不同的方法,今天主要介绍使用three.js来实现效果,Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多、使用最广泛的三维引擎。
1:初始化设置
所有的初始化操作都是在mounted中实现,这里为什么不用created?因为我们需要操作dom,
而created是获取不到dom的。
// 创建场景
const scene = new THREE.Scene() //初始化场景
var ambient = new THREE.AmbientLight(0xfffffff, 2) //添加光源 颜色和光照强度
scene.add(ambient) //向场景中添加光源
首先是对于场景的初始,光源是一般是必须要添加上的,不过也有些特殊材质能自身发光的情况下光源也不必添加,当然这是很少见的一种情况,没有光源会导致黑屏。
// 创建相机
var width = 800 //窗口宽度
var height = 800 //窗口高度
const camera = new THREE.PerspectiveCamera(90, width / height, 1, 1000) //使用透视相机
camera.position.set(30, 0, 10) //设置相机位置
camera.lookAt(new THREE.Vector3(30, 0, 0)) // 相机看向
这里我们使用的是透视相机,这是最接近自然的视图,符合近大远小的规律。
// 创建渲染器
width = window.innerWidth //窗口宽度 window.innerWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏)包含滚条
height = window.innerHeight //窗口高度 window.innerHeight
const renderer = new THREE.WebGLRenderer() //创建渲染器
renderer.setSize(width, height) // 设定渲染器尺寸
renderer.setPixelRatio(window.devicePixelRatio)
this.$refs.container.appendChild(renderer.domElement) //通过 this.$ref
渲染器就相当于画布,所有的展示效果都需要在画布上来显示我们需要在html中添加一个ref为container的盒子,当然你也可以自己来命名。
// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
// 缩放限制
controls.maxDistance = 12
controls.target.set(30, 0, 0)
这里我们需要对控制器来进行一个对于缩放的限制,因为全景的原理是创建一个正方体或者一个球体,然后让视角在盒子里面,所以我们不能将外盒子暴露给用户。
2:添加模型
接下来是最关键的步骤了,我们需要创建一个球体或者正方体,不同的材质所需要的全景图是不一样的,球体只需要一张完整的全景照片即可实现全景,缺点是可能某些视角效果不是很好,而正方体需要六张不同方向的照片,这个对于初学者难度要求过高,往往不能做到无缝贴合,但是他避免球体的缺点,展现效果是非常不错的。
// 添加一个球体
const geometry = new THREE.SphereGeometry(130, 256, 256)
var textureLoader = new THREE.TextureLoader() //创建纹理贴图
var img = textureLoader.load(require('../../../public/image/vrTest3.jpg'))
const material = new THREE.MeshLambertMaterial({
map: img, //设置颜色贴图属性值
side: THREE.DoubleSide //双面渲染
})
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
最后是渲染场景
// 渲染场景
const animate = function () {
requestAnimationFrame(animate)
renderer.render(scene, camera)
}
animate()
这里你也可以加上动画,让场景自己移动。
完成上诉你应该完成了一个最基本的vr全景图了。
这里我添加一个3D模型在里面,你们也基于自己的业务进行拓展
3:场景漫游
场景漫游分为同场景漫游和不同场景漫游
一:不同场景漫游
由于是不同场景,一个场景是一个物体,在这里我们需要循环的创建多个球体,切记场景与场景之间不可重叠,原理主要是在切换时改变相机的位置和控制中心的位置,因为我们在开始的时候进行了缩放的限制所以用户是无法发现的,测试的时候可以把限制注释掉来进行调试。
<template>
<div class="home">
<div ref="container"></div>
<!-- 场景切换点 -->
<div class="switch">
<span
class="buttons"
v-for="(room, index) in rooms"
:key="index"
@click="handleSwitchButtonClick(room.key)"
v-show="room.key !== currentRoom"
>
<b class="text">{{ room.name }}</b>
<i class="icon"></i>
</span>
</div>
</div>
</template>
与画布并列,将切换盒子固定在全景图的右侧,需要将盒子的index大于画布
对于多个场景的创建添加可以参考上面的代码,去循环创建他就行了,这里我就不进行多余的阐述了。
// 点击切换场景
async handleSwitchButtonClick(key) {
if (key == 'internet-hall') {
this.cameras.position.set(280, 0, 10)
this.cameras.lookAt(new THREE.Vector3(280, 0, 0))
this.controles.target.set(280, 0, 0)
this.currentRoom = 'internet-hall'
} else if (key == 'insect-hall') {
this.cameras.position.set(0, 0, 10)
this.cameras.lookAt(new THREE.Vector3(0, 0, 0))
this.controles.target.set(0, 0, 0)
this.currentRoom = 'insect-hall'
} else if (key == 'agriculture-hall') {
this.cameras.position.set(580, 0, 10)
this.cameras.lookAt(new THREE.Vector3(580, 0, 0))
this.controles.target.set(580, 0, 0)
this.currentRoom = 'agriculture-hall'
}
}
切换场景在场景不多的时候可以用我这种方法,当然你也可以将数据放在data里面,使用循环来切换不同的场景。
二:同场景漫游
对于同场景的漫游,我使用了移动来实现
window.addEventListener(
'keydown',
(e) => {
var ev = e || window.event
switch (ev.keyCode) {
case 87:
camera.position.x += 2
controls.target.x += 2
break
case 83:
camera.position.x -= 2
controls.target.x -= 2
break
case 65:
camera.position.z -= 2
controls.target.z -= 2
break
case 68:
camera.position.z += 2
controls.target.z += 2
break
default:
break
}
},
false
)
有什么问题可以在评论下留言哦。