Three.js 实现点击模型改变颜色

想实现点击模型,改变模型颜色的效果。在网上看了一些代码,发现特别搞笑,很多的博客给出的计算代码都是错误的,根本无法实现点击效果,还在不同的博客相互的复制,只能感叹抄袭党太多。下面我简单的介绍一些我的方法,我是在Vue中开发的。

三维空间点击事件的原理

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

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

环境准备

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

代码实现

<template>
  <div id="app" @click="clickBox">

  </div>
</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的

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐