vue+threejs写物体事件:单击事件

690 阅读3分钟

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


写在前面

本文用vue+threejs写物体事件:单击事件,监听单击物体的事件。

监听单击事件在实际应用中很常见,本文实现了通过单击事件选中物体。

演示gif如下:

20220930_155504.gif

代码说明

  1. 依旧是先创建一个id容器,用于插入渲染器节点
<template>
  <div class="item">
    <div id="THREE61"></div>
  </div>
</template>
  1. 引入threej以及需要用到的模块
  • 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. mounted方法中调用initThreejs方法
  mounted() {
    this.initThreejs();
  },
  1. initThreejs完整代码及说明
  • 先定义好相机、场景、渲染器,并且创建,此处就不多说了,代码中有注释说明,以前的文章也都详细说明过
  • 实现点击物体,给物体加上边框功能的主要有两部分,一部分是光线投射raycaster,另一部分是后期处理composer中的outlinePass,它能快速实现给物体加边框
  • 在场景中,我们创建三个立方体网格模型,分别用上红色、绿色、蓝色的基础材质scene.add(redMesh); scene.add(greenMesh); scene.add(blueMesh);,将这三个网格模型添加到光线投射的对象objects
  • 然后对点击事件进行监听document.addEventListener("click", onMouseClick);,点击时获取光线投射的对象,该对象就是我们点击的物体,然后将该物体加到outlinePass中,重新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("THREE61").appendChild(renderer.domElement);

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

    document.addEventListener("click", onMouseClick);

    // 创建轨道控制器
    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(0xffffff));
    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 onMouseClick(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]; // 第一个对象加上边框
    }

    render();
  }

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

写在最后

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