17-InstancedMesh和Raycaster用户交互

30 阅读3分钟

Raycaster

  • threejs鼠标响应机制 Raycaster射线投射器: 用来检测鼠标点击物体(射线检测是一条线,从相机发出,穿过屏幕,检测物体,可能是多个物体)
  • 也就是说摄像机当眼睛与鼠标产生的直线就是所谓的射线投射器
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

// 用户交互的一个小案例
let renderer, scene, camera, light, meshes;
let axesHelper;
let controls;

let amount = 6;
let count = Math.pow(amount, 3); // 立方体个数
let color = new THREE.Color();
let white = new THREE.Color().setHex(0xffffff); // 白色

let raycaster = new THREE.Raycaster(); // 射线投射
let mouse = new THREE.Vector2(1, 1); // 鼠标位置信息

scene = new THREE.Scene(); //初始化场景
initRenderer(); //初始化渲染器
initCamera(); //初始化相机
initLight(); // 初始化灯光
initAxesHelper(); // 初始化辅助线
initMeshes(); // **初始化物体群**

controls = new OrbitControls(camera, renderer.domElement);
render(); // 渲染场景

window.addEventListener("resize", () => {
  camera.aspect = window.innerWidth / window.innerHeight; // 更新相机宽高比
  camera.updateProjectionMatrix(); // 更新相机投影矩阵
  renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器大小
});

document.addEventListener("mousemove", (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1; // 将鼠标位置归一化到-1到1之间
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; // 将鼠标位置归一化到-1到1之间
});

function initRenderer() {
  renderer = new THREE.WebGLRenderer(); // 初始化渲染器
  renderer.setPixelRatio(window.devicePixelRatio); // 设置渲染器像素比作用:让渲染器渲染出来的效果更细腻
  renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器大小
  document.body.appendChild(renderer.domElement); // 将渲染器添加到页面中
}

function initCamera() {
  camera = new THREE.PerspectiveCamera(
    60,
    window.innerWidth / window.innerHeight,
    1,
    1000
  ); // 初始化相机
  camera.position.set(10, 10, 10); // 设置相机位置
}

function initLight() {
  // 这个光源和环境光的灯源有点像,但是它不能产生阴影
  light = new THREE.HemisphereLight(0xffffff, 0x888888); // 初始化光源,第一个参数是天空的颜色,第二个参数是地面的颜色
  light.position.set(0, 1, 0); // 设置光源位置
  scene.add(light); // 将光源添加到场景中
}
function initAxesHelper() {
  // 初始化辅助线
  axesHelper = new THREE.AxesHelper(3); // 3表示辅助线的长度
  scene.add(axesHelper); // 将辅助线添加到场景中
}

function initMeshes() {
  const geometry = new THREE.IcosahedronGeometry(0.5, 2); // 创建正二十面体几何体(特殊几何图形)
  const meterial = new THREE.MeshPhongMaterial({ color: 0xffffff }); // 创建材质 白色

  //let mesh = new THREE.Mesh(geometry, meterial); // 创建网格
  // 创建一堆小球
  meshes = new THREE.InstancedMesh(geometry, meterial, count); // 这个api性能更好,占用资源少

  let index = 0;
  const offset = (amount - 1) / 2; // 计算偏移量,使小球在中心
  const matrix = new THREE.Matrix4(); // 创建一个矩阵,用来存储小球的位置和旋转信息
  for (let i = 0; i < amount; i++) {
    for (let j = 0; j < amount; j++) {
      for (let k = 0; k < amount; k++) {
        // 每个小球的位置
        matrix.setPosition(offset - i, offset - j, offset - k);
        // 每个小球给一个id,方便后面操作
        meshes.setMatrixAt(index, matrix); // 设置小球的矩阵信息
        meshes.setColorAt(index, white); // 设置小球颜色白色
        index += 1;
      }
    }
  }

  scene.add(meshes); // 将网格添加到场景中
}

function render() {
  requestAnimationFrame(render); // 请求下一帧渲染

  raycaster.setFromCamera(mouse, camera); // 设置射线发射器的位置和方向
  // 射线射向物体
  const intersection = raycaster.intersectObject(meshes); // 获取射线与物体相交的数组(交集)
  // 如果有物体被射线击中
  if (intersection.length > 0) {
    // 找到物体设置id
    const instanceId = intersection[0].instanceId; //看到的第一个物体
    meshes.getColorAt(instanceId, color); // 获取射线击中的物体的颜色

    if (color.equals(white)) {
      // 如果射线击中的物体颜色是白色,则改变颜色
      meshes.setColorAt(instanceId, color.setHex(Math.random() * 0xffffff)); //设置随机颜色
      meshes.instanceColor.needsUpdate = true; // 更新颜色,不然看不到效果
    }
  }

  renderer.render(scene, camera); // 渲染场景和相机
}
// 鼠标响应机制 Raycaster射线投射器: 用来检测鼠标点击物体(射线检测是一条线,从相机发出,穿过屏幕,检测物体,可能是多个物体)

image.png

image.png

总结

  • 创建矩阵小球每个小球设置id
  • 找到射线,找与球群相交的第一个元素
  • 白色才设置随机色