three.js实现vr效果

本文介绍了如何利用Three.js创建一个支持鼠标交互的虚拟现实环境,包括初始化场景、创建立方体网格和控制器,响应热点点击并实现相机动画路径。通过热点数据和Tween.js库,实现网格对象根据用户点击动态切换效果。
摘要由CSDN通过智能技术生成

three.js实现vr效果

  1. 初始化Three.js场景、相机和渲染器。
  2. 创建一个立方体网格对象,并设置其材质为由热点数据数组指定的贴图。
  3. 创建控制器,使用户可以通过鼠标控制场景的旋转和缩放。
  4. 在窗口大小变化时,更新相机的投影矩阵和渲染器的大小。
  5. 创建热点,根据热点数据数组中的位置和贴图信息,在场景中创建对应的Sprite对象。
  6. 监听点击事件,当用户点击场景时,检测与射线相交的对象,并执行相应的操作。
  7. 根据点击的热点对象,计算相机移动的动画路径,并在一定时间内移动相机到目标位置。
  8. 添加Tween.js动画更新函数,使相机的移动动画生效。
  9. 替换当前的网格对象,将原来的立方体网格对象替换为新的网格对象。
<template>
  <div class="container" ref="container"></div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import * as THREE from "three";
// 导入轨道控制器 - 控制物体的左右上下移动( 可以设置xyz轴 )
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 引入扩展库GLTFLoader.js
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
let arr = ref([
  "scene_left",
  "scene_right",
  "scene_top",
  "scene_bottom",
  "scene_front",
  "scene_back",
]);
// 引入Tween.js库
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",
  },
  // {
  //   type: "Hotspot_2",
  //   position: new THREE.Vector3(15, 0, 25),
  //   texturePath: "src/assets/images/arrow.png",
  // },
  // {
  //   type: "Hotspot_3",
  //   position: new THREE.Vector3(-10, 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);
}
// 创建Three.js场景、相机、渲染器对象
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);
    // if (item === "scene_top") {
    //   texture.rotation = Math.PI;
    //   texture.center = new THREE.Vector2(0.5, 0.5);
    //   boxMaterials.push(
    //     new THREE.MeshBasicMaterial({
    //       color: "#ffff00",
    //       map: texture,
    //     })
    //   );
    // } else {
    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();
  // 将鼠标坐标转换为Three.js空间坐标
  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();

      // 更新Tween动画
      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();
  // mesh = newMesh; // 将新网格对象赋值给mesh变量
  // scene.add(mesh); // 添加新网格对象到场景中
}
</script>

<style lang="scss" scoped>
.container {
  @include Width(1920);
  @include wHeight(800);
}
</style>


  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大头先生296

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值