思路:
1. 创建球体,背(双)面贴图,视角位于球体中心;
2. 创建立方体,六面贴图,视角位于立方体中心。
1. 首先需要一张高分辨率的全景图,要不然会糊。
下面是推荐的国外全景图网站,可以下载用于调试:
Poly Haven 全景图片https://polyhaven.com/hdris
2. 立方体贴图需要注意要按照顺序进行贴图:
可以使用以下工具进行图片切割:
GitHub:全景图至立方体贴图https://github.com/jaxry/panorama-to-cubemap
下载时可以看到已经自动命名好了,就不需要再去额外考虑:
3.代码:
<template>
<div ref="fullView"></div>
</template>
<script setup>
import * as THREE from 'three'
import TWEEN from "@tweenjs/tween.js";
import { ref, onMounted } from "vue";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
/********************** 创建3D场景 ***********************/
const scene = new THREE.Scene();
/********************** 创建光源 ***********************/
//添加光源
const ambiLight = new THREE.AmbientLight(0x333333);
scene.add(ambiLight);
/********************** 创建相机 ***********************/
const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 3000);
camera.position.set(0, 1500, 0);
/********************** 创建渲染器对象 ***********************/
const renderer = new THREE.WebGLRenderer();
//设置three.js渲染区域的尺寸(像素px)
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xC0C0C0, 1); // 设置背景色
/********************** 创建轨道控制器 ***********************/
const controls = new OrbitControls(camera, renderer.domElement);
/********************** 创建球体全景图 ***********************/
// 创建球体
const geometry = new THREE.SphereGeometry(300, 50, 50);
// 设置材质贴图
const material = new THREE.MeshBasicMaterial({ // 此材质不受光照影响
map: new THREE.TextureLoader().load('sky.png',() => { rotateEnter() }), //贴图,加载进场动画
side: THREE.BackSide, //背面贴图,也可以设为 THREE.DoubleSide 双面贴图,不过内部图像方向应该会反过来
});
// 加入到场景中
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
/********************** 创建正方体全景图 ***********************/
// const geometry = new THREE.BoxGeometry(300, 300, 300);
// // 6张全景图的名称
// const pathArr = ['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png'];
// // 声明一个数组,用来存储六张全景图对应的纹理对象Texture
// const materialArr = [];
// pathArr.forEach((src, idx) => {
// let textureLoader = new THREE.TextureLoader();
// let material = new THREE.MeshBasicMaterial({
// map: textureLoader.load(src, () => {
// if(idx == pathArr.length - 1) {
// rotateEnter()
// }
// }),
// side: THREE.BackSide, //背面贴图
// });
// materialArr.push(material)
// });
// const mesh = new THREE.Mesh(geometry, materialArr);
// scene.add(mesh);
/********************** 动画旋转进场 ***********************/
const rotateEnter = () => {
let tween = new TWEEN.Tween({ fov: camera.fov, y: camera.position.y })
.to({ fov: 70, y: 0 }, 3000) // 动画时间为3s
.easing(TWEEN.Easing.Sinusoidal.InOut)
.onUpdate((e) => {
// 更新相机位置和视角大小
camera.position.y = e.y;
camera.fov = e.fov;
// // 旋转效果
mesh.rotation.y += 0.05;
// // 将从上到下的视角 调整为 平行视角
if(e.y < 500) {
camera.lookAt(new THREE.Vector3(0, 0, 90));
}
})
.onComplete(() => {
TWEEN.remove(tween);
// 更新相机矩阵
camera.updateProjectionMatrix();
controls.update();
})
.start();
}
/********************** 挂载及渲染 ***********************/
const fullView = ref(null);
onMounted(()=>{
fullView.value.appendChild(renderer.domElement);
})
const renderScene = () => {
renderer.render(scene, camera);
requestAnimationFrame(renderScene);
TWEEN.update();
}
renderScene();
</script>
<style lang="scss" scoped>
</style>