three.js实现vr效果
- 初始化Three.js场景、相机和渲染器。
- 创建一个立方体网格对象,并设置其材质为由热点数据数组指定的贴图。
- 创建控制器,使用户可以通过鼠标控制场景的旋转和缩放。
- 在窗口大小变化时,更新相机的投影矩阵和渲染器的大小。
- 创建热点,根据热点数据数组中的位置和贴图信息,在场景中创建对应的Sprite对象。
- 监听点击事件,当用户点击场景时,检测与射线相交的对象,并执行相应的操作。
- 根据点击的热点对象,计算相机移动的动画路径,并在一定时间内移动相机到目标位置。
- 添加Tween.js动画更新函数,使相机的移动动画生效。
- 替换当前的网格对象,将原来的立方体网格对象替换为新的网格对象。
<template>
<div class="container" ref="container"></div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
let arr = ref([
"scene_left",
"scene_right",
"scene_top",
"scene_bottom",
"scene_front",
"scene_back",
]);
import TWEEN from "@tweenjs/tween.js";
const container = ref(null);
const { scene, camera, renderer } = initThree();
const mesh = createMesh();
const controls = setupControls();
const hotspotsData = [
{
type: "Hotspot_1",
position: new THREE.Vector3(0, 0, -25),
texturePath: "src/assets/images/arrow.png",
},
];
onMounted(() => {
container.value.appendChild(renderer.domElement);
animate();
createHotspots();
document.addEventListener("click", onDocumentClick);
});
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
function initThree() {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 0, 20);
const renderer = new THREE.WebGL1Renderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
return { scene, camera, renderer };
}
function setupControls() {
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.enableZoom = false;
return controls;
}
function createMesh() {
const geometry = new THREE.BoxGeometry(100, 100, 100);
const boxMaterials = [];
for (const item of arr.value) {
const texture = loadTexture(item);
boxMaterials.push(
new THREE.MeshBasicMaterial({
color: "#ffffff",
map: texture,
})
);
}
const mesh = new THREE.Mesh(geometry, boxMaterials);
mesh.geometry.scale(1, 1, -1);
scene.add(mesh);
return mesh;
}
function loadTexture(name) {
const texture = new THREE.TextureLoader().load(
new URL(`../assets/img/${name}.jpeg`, import.meta.url).href
);
return texture;
}
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
function createHotspot(type, position, texture) {
const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
const sprite = new THREE.Sprite(spriteMaterial);
sprite.userData.type = type;
sprite.position.copy(position);
sprite.scale.set(2, 2, 2);
scene.add(sprite);
}
function createHotspots() {
const textureLoader = new THREE.TextureLoader();
hotspotsData.forEach((hotspot) => {
const texture = textureLoader.load(hotspot.texturePath);
createHotspot(hotspot.type, hotspot.position, texture);
});
}
function onDocumentClick(event) {
event?.preventDefault();
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
intersects.forEach((intersect) => {
if (intersect.object.userData.type) {
const targetPosition = intersect.point;
const targetDirection = targetPosition
.clone()
.sub(camera.position)
.normalize();
console.log(targetDirection);
const startPos = camera.position.clone();
const endPos = targetPosition.clone();
const duration = 1000;
const tween = new TWEEN.Tween(startPos)
.to(endPos, duration)
.onUpdate(() => {
camera.position.copy(startPos);
camera.lookAt(targetPosition);
})
.start();
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
}
animate();
if (intersect.object.userData.type == "Hotspot_1") {
intersect.object.userData.type ='Hotspot_2'
scene.remove(mesh);
arr.value = ["0_r", "0_l", "0_u", "0_d", "0_f", "0_b"];
createMesh();
} else {
intersect.object.userData.type ='Hotspot_1'
scene.remove(mesh);
arr.value = [
"scene_left",
"scene_right",
"scene_top",
"scene_bottom",
"scene_front",
"scene_back",
];
createMesh();
}
}
});
}
function replaceMesh() {
scene.remove(mesh);
arr.value = ["0_r", "0_l", "0_u", "0_d", "0_f", "0_b"];
createMesh();
}
</script>
<style lang="scss" scoped>
.container {
@include Width(1920);
@include wHeight(800);
}
</style>