vue+threejs写物体事件:滑过事件

406 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情


写在前面

本文用vue+threejs写物体事件:滑过事件,监听鼠标滑过物体的事件。

鼠标滑过物品时对物品进行勾边操作。

下面是演示gif:

20221008_112921.gif

代码说明

  1. html

用于插入渲染器dom节点

<template>
  <div class="item">
    <div id="THREE63"></div>
  </div>
</template>
  1. 引入threejs

引入threejs模块,引入轨道控制器OrbitControls,用于鼠标控制界面,引入后期处理EffectComposer,用于进行后期处理操作,RenderPass用于渲染,OutlinePass用于勾边

import * as THREE from "three";

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
  1. vue的mounted方法

在mounted方法中引入initThreejs方法

  mounted() {
    this.initThreejs();
  },
  1. initThreejs方法全部代码和思路说明

思路说明:

  • 相机camera、场景scene、渲染器renderer的定义和创建
  • 灯光light、轨道控制器controls、地面ground、物体(redMesh、greenMesh、blueMesh)的创建
  • 光线投射的使用raycaster = new THREE.Raycaster();,并且将物体添加到响应光线投射的数组中objects.push(redMesh, greenMesh, blueMesh);
  • 后期处理的引入和使用
  • 添加鼠标滑过事件document.addEventListener("mousemove", onMouseMove);,鼠标有滑过物体就进行勾边操作outlinePass.selectedObjects = [intersect.object];,没有滑过物体就不进行勾边操作outlinePass.selectedObjects = [];
  • 最后使用后期处理渲染composer.render();
    initThreejs() {
      let camera, scene, renderer;

      let composer, outlinePass;

      let raycaster, // 光线投射,用于鼠标拾取,返回鼠标在的位置的物体
        pointer, // 二维向量,用于存放鼠标位置
        objects = []; // 用于存放响应鼠标拾取的对象

      init();

      function init() {
        // 创建场景
        scene = new THREE.Scene();
        scene.background = new THREE.Color(0x000000); // 设置场景背景颜色

        // 创建灯光
        const light = new THREE.DirectionalLight(0xffffff); // 平行光
        light.position.set(0.5, 1.0, 0.5).normalize(); // 设置平行光的方向,从(0.5, 1.0, 0.5)->target一般(0, 0, 0)
        scene.add(light); // 将灯光添加到场景中

        // 创建相机
        camera = new THREE.PerspectiveCamera(
          35,
          (window.innerWidth - 201) / window.innerHeight,
          1,
          500
        ); // 透视相机
        camera.position.x = 36;
        camera.position.y = 67; // 设置相机的位置
        camera.position.z = 67;
        scene.add(camera); // 将相机添加到场景中

        // 创建渲染器
        renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth - 201, window.innerHeight);
        document.getElementById("THREE63").appendChild(renderer.domElement);

        // 光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体),返回值就是鼠标在的物体的集合
        raycaster = new THREE.Raycaster();
        pointer = new THREE.Vector2(); // 定义一个二维向量

        document.addEventListener("mousemove", onMouseMove);

        // 创建轨道控制器
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.addEventListener("change", render);
        controls.update();

        // 创建地面
        const ground = new THREE.Mesh(
          new THREE.BoxGeometry(50, 0.15, 50),
          new THREE.MeshPhongMaterial({
            color: 0x999999,
            depthWrite: false,
            transparent: true,
            opacity: 1,
          })
        );
        ground.receiveShadow = true;
        scene.add(ground);

        /**
         * 后期处理
         * OutlinePass就是勾边功能的主要模块
         * 这在文章:用vue+threejs写物体效果:勾边中进行了详细说明,这里就不展开了
         */
        composer = new EffectComposer(renderer);
        const renderPass = new RenderPass(scene, camera);
        composer.addPass(renderPass);
        outlinePass = new OutlinePass(
          new THREE.Vector2(window.innerWidth - 201, window.innerHeight),
          scene,
          camera
        );
        outlinePass.visibleEdgeColor.set(new THREE.Color(0xffff00));
        outlinePass.edgeStrength = 3;
        outlinePass.edgeThickness = 1;
        composer.addPass(outlinePass);

        // 创建红色立方体
        let redMesh = new THREE.Mesh(
          new THREE.BoxGeometry(2, 2, 2),
          new THREE.MeshBasicMaterial({ color: 0xff0000 })
        ); // 网格模型
        redMesh.position.y = 1;
        scene.add(redMesh); // 将这个立方体添加到场景中

        // 创建绿色立方体
        let greenMesh = new THREE.Mesh(
          new THREE.BoxGeometry(2, 2, 2),
          new THREE.MeshBasicMaterial({ color: 0x00ff00 })
        ); // 网格模型
        greenMesh.position.y = 1;
        greenMesh.position.x = 5;
        scene.add(greenMesh); // 将这个立方体添加到场景中

        // 创建蓝色立方体
        let blueMesh = new THREE.Mesh(
          new THREE.BoxGeometry(2, 2, 2),
          new THREE.MeshBasicMaterial({ color: 0x0000ff })
        ); // 网格模型
        blueMesh.position.y = 1;
        blueMesh.position.x = 6;
        blueMesh.position.z = 9;
        scene.add(blueMesh); // 将这个立方体添加到场景中

        objects.push(redMesh, greenMesh, blueMesh);

        render();
      }

      function onMouseMove(event) {
        pointer.set(
          (event.offsetX / (window.innerWidth - 201)) * 2 - 1,
          -(event.offsetY / window.innerHeight) * 2 + 1
        ); // 将鼠标位置归一化为设备坐标 x 和 y 方向的取值范围是 (-1 to +1)

        raycaster.setFromCamera(pointer, camera); // 通过相机和鼠标位置更新射线

        const intersects = raycaster.intersectObjects(objects, false); // 获取光线投射的对象
        if (intersects.length > 0) {
          const intersect = intersects[0];
          outlinePass.selectedObjects = [intersect.object]; // 第一个对象加上边框
        } else {
          outlinePass.selectedObjects = []; // 没有滑过的对象就置为空
        }

        render();
      }

      function render() {
        composer.render();
      }
    },

写在最后

以上就是所有的代码和说明。