THREE.JS中如何使用光线投射(raycaster)完成3D交互效果

1,560 阅读2分钟

wallhaven-1pd1o9.jpg

前言

在网页开发中,我们通常使用 DOM 事件来操作 DOM 元素。但是在 canvas 画布中,如何监听用户对画布内容的 DOM 操作呢?除了常规的事件监听器外,在 three.js 中,我们还可以使用 Raycaster 来判断用户是否选中了画布中的物体。通过设置光线的起点和方向,Raycaster 可以检测到光线与画布中的物体是否相交,从而判断用户是否选中了某个物体。这种方式可以实现在 3D 环境中的交互操作,例如在模型上点击或者拖拽等。

本文通过一个小demo的实现,来阐述如何使用光线投射(raycaster)完成3D交互效果。

qevz8-oegxf.gif

Raycaster(射线拾取模型)中几个重要的API

射线交叉计算(.intersectObjects()方法)

射线投射器Raycaster通过.intersectObjects()方法可以计算出来与自身射线.ray相交的网格模型。

语法:Raycaster.intersectObjects([mesh1, mesh2, mesh3])

 计算射线(.setFromCamera()方法)

把鼠标单击位置坐标相机作为.setFromCamera()方法的参数,.setFromCamera()就会计算射线投射器Raycaster的射线属性.ray,形象点说就是在点击位置创建一条射线,用来选中拾取模型对象。

//创建一个射线投射器`Raycaster`
const raycaster = new THREE.Raycaster();
//.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
// 形象点说就是在点击位置创建一条射线,用来选中拾取模型对象
raycaster.setFromCamera(new THREE.Vector2(x, y), camera);

实现过程

创建3个不同颜色的小球

// 创建三个球
const sphere1 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x00ff00,
  })
);
sphere1.position.x = -4;
scene.add(sphere1);

const sphere2 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x0000ff,
  })
);

scene.add(sphere2);

const sphere3 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0xff00ff,
  })
);

sphere3.position.x = 4;
scene.add(sphere3);

image.png

创建射线和鼠标向量

// 创建射线
const raycaster = new THREE.Raycaster();
// 创建鼠标向量
const mouse = new THREE.Vector2();

监听页面点击事件

  1. 获取到鼠标点击位置
  2. 将鼠标点击位置转换为标准设备坐标
  3. 建立一条由相机鼠标点击位置的射线
  4. 通过intersectObjects计算出与该条射线相交的模型
  5. 改变对相交的模型的颜色

代码如下:

window.addEventListener("click", (event) => {
  console.log(event.clientX, event.clientY);
  // 设置鼠标向量的x,y值
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -((event.clientY / window.innerHeight) * 2 - 1);

  // console.log(mouse.x, mouse.y);
  // 通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(mouse, camera);

  // 计算物体和射线的焦点
  const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);

  if (intersects.length > 0) {
    // console.log(intersects[0].object);
    if (intersects[0].object._isSelect) {
      console.log(intersects[0].object._originColor);
      intersects[0].object.material.color.set(
        intersects[0].object._originColor
      );
      intersects[0].object._isSelect = false;
      return;
    }

    intersects[0].object._isSelect = true;
    intersects[0].object._originColor =
      intersects[0].object.material.color.getHex();
    intersects[0].object.material.color.set(0xff0000);
  }

  console.log(intersects);
});

总结

在 3D 交互中,光线投射(Raycaster)是一项常用的技术,掌握它可以提高交互效果的实现效率和质量。如有错误之处,欢迎大家指出,谢谢大家了。