三维空间点击事件的原理

在三维空间内判断鼠标点击的是哪个模型,核心的原理还是 射线碰撞 ,*即从相机(camera)的中心点到屏幕上鼠标点组成一条射线,计算三维场景内哪些模型被射线穿过。*原理比较简单,计算比较复杂。主要是涉及了三个坐标系的转换:

  1. 将鼠标点从屏幕坐标系(y正向向下,x轴向右)转换到三维空间里的可视窗口的二维坐标系(正常的二维坐标系,但xy的区间是-1~1);
  2. 将三维空间里的可视窗口的二维坐标系点转换为三维空间的xyz坐标系
  3. 将2转换后的点和camera的中心点组成射线

基于vue的环境准备,可以参考我之前的 博文

<template>
  <div id="app" @click="clickBox">
</template>
<script>
import {
  Scene,
  PerspectiveCamera,
  WebGLRenderer,
  Mesh,
  BoxGeometry, MeshBasicMaterial, Raycaster, Vector2
} from 'three';
import {OrbitControls} from "@/lib/OrbitControls"
export default {
  name: 'App',
  components: {},
  mounted() {
    let scene = new Scene();
    let camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    let renderer = new WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    let app = document.getElementById("app")
    app.appendChild(renderer.domElement);
    //加载场景控制插件
    let controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.enableZoom = true;
    controls.autoRotate = false;
    controls.autoRotateSpeed = 3;
    controls.enablePan = true;
    controls.enableKeys = true;
    controls.keyPanSpeed = 7;
    controls.keys = {
      LEFT: 37,
      UP: 38,
      RIGHT: 39,
      BOTTOM: 40
    this.controls = controls;
    this.createBox(scene);
    this.createBox(scene);
    this.createBox(scene);
    camera.position.z = 5;
    this.camera = camera;
    this.scene = scene;
    //渲染场景
    let animate = () => {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
    animate();
  methods: {
    //生成随机盒子模型
    createBox(scene) {
      let geometry = new BoxGeometry();
      let material = new MeshBasicMaterial({color: 0x00ff00});
      let cube = new Mesh(geometry, material);
      cube.position.x = Math.random() * 6-3
      cube.position.y = Math.random() * 6-3
      scene.add(cube);
    clickBox(event) {
      console.log(Math.random().toString(16))
      let raycaster = new Raycaster();
      let mouse = new Vector2();
      console.log(this.scene.children)
      //将鼠标点击位置的屏幕坐标转换成threejs中的标准坐标
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1
      mouse.y = 1-(event.clientY / window.innerHeight) * 2
      console.log(mouse)
      // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
      raycaster.setFromCamera(mouse, this.camera);
      // 获取raycaster直线和所有模型相交的数组集合
      var intersects = raycaster.intersectObjects(this.scene.children);
      console.log(intersects);
      //将所有的相交的模型的颜色设置为红色
      for (var i = 0; i < intersects.length; i++) {
        intersects[i].object.material.color.set(0xff0000);
</script>
<style>
#app {
body, html {
  margin: 0;
  padding: 0;
</style>

网上很多的代码写的都是

mouse.y = (event.clientY / window.innerHeight) * 2 + 1;

可以看出来这个y一直都是大于1的

object.traverse(child=>{ // if(object.name==='model.glb') { // console.log("加工中心",child.name) // // flashingName = "mesh_8" if(child instanceof THREE.Mesh var objects=[]; var raycaster= new THREE.Raycaster(); var mouse = new THREE.Vector2(), INTERSECTED, SELECTED; 需要把你要实现的效果的geo
这个小案例是当初我在学习的时候,小的一个小案例,代码还需要进一步优化;还请谅解~~;主要用到了threeJS创建mesh,创建平面,设置mesh的平移,旋转、缩放、自传、透明度、拉伸等这些小功能; (点击每个mesh,mesh的颜色会变为红色) 1 需要加载这些相关的js文件 2 下面是实现的一些小功能 3 下面是相关代码,代码还没有优化,请谅解~~(具...