Threejs之射线拾取模型

参考资料

  • 射线Ray
  • 射线拾取Sprite控制场景

知识点

注:基于Three.jsv0.155.0

  • 射线Ray
  • Raycaster(射线拾取模型)
  • 屏幕坐标转标准设备坐标
  • Raycaster(鼠标点击选中模型)
  • Canvas尺寸变化(射线坐标计算)
  • 射线拾取层级模型(模型描边)
  • 射线拾取Sprite控制场景

代码实现

DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Three.jstitle>
head>
  <body>
  body>
  
  <script type="importmap">
    {
      "imports": {
        "three": "./js/three.module.js",
        "three/addons/": "../three.js/examples/jsm/"
      }
    }
  script>
  <script type="module">
    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
   
    const width = 800
    const height = 500

    // 场景
    const scene = new THREE.Scene();
    // 几何体
    const geometry = new THREE.BoxGeometry(50, 50, 50);
    // 材质
    const material = new THREE.MeshBasicMaterial({
      color: 0x00ff00,
      transparent: true,
      opacity: 0.5
    });
    // 网格模型:物体
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(0, 0, 0);
    scene.add(mesh);

    const mesh1 = mesh.clone()
    mesh1.position.set(100, 0, 0);
    mesh1.material = mesh.material.clone();
    scene.add(mesh1);

    const mesh2 = mesh.clone()
    mesh2.position.set(0, 100, 0);
    mesh2.material = mesh.material.clone();
    scene.add(mesh2);

    // 坐标系
    const axes = new THREE.AxesHelper(200);
    scene.add(axes);

    // 相机
    const camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 1000);
    camera.position.set(200, 200, 200);
    camera.lookAt(scene.position);

    // 渲染器
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(width, height);
    renderer.render(scene, camera);
    // 设置设备像素比,避免canvas画布输出模糊
    renderer.setPixelRatio(window.devicePixelRatio);
    document.body.appendChild(renderer.domElement);
    
    // 射线Ray Start
    // 创建射线对象Ray
    const ray = new THREE.Ray()
    // 设置射线起点
    ray.origin = new THREE.Vector3(1,0,3);

    // 表示射线沿着x轴正方向
    ray.direction = new THREE.Vector3(1,0,0);
    // 三角形三个点坐标
    const p1 = new THREE.Vector3(100, 25, 0);
    const p2 = new THREE.Vector3(100, -25, 25);
    const p3 = new THREE.Vector3(100, -25, -25);
    const point = new THREE.Vector3();//用来记录射线和三角形的交叉点
    // `.intersectTriangle()`计算射线和三角形是否相交叉,相交返回交点,不相交返回null
    const result = ray.intersectTriangle(p1,p2,p3,true,point);
    console.log('交叉点坐标', point);
    console.log('查看是否相交', result);
    // 射线Ray End

    // 3. 屏幕坐标转标准设备坐标 Start
    addEventListener('click',function(event){
        // event对象有很多鼠标事件相关信息
        console.log('event',event);
        const px = event.offsetX;
        const py = event.offsetY;
        //屏幕坐标px、py转标准设备坐标x、y
        //width、height表示canvas画布宽高度
        const x = (px / width) * 2 - 1;
        const y = -(py / height) * 2 + 1;
        console.log(' ~ file: 14.射线拾取模型.html:96 ~ addEventListener ~ y:', y)
        //创建一个射线投射器`Raycaster`
        const raycaster = new THREE.Raycaster();
        //.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
        // 形象点说就是在点击位置创建一条射线,射线穿过的模型代表选中
        raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
        //.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算
        // 未选中对象返回空数组[],选中一个对象,数组1个元素,选中两个对象,数组两个元素
        const intersects = raycaster.intersectObjects([mesh1, mesh2, mesh]);
        console.log("射线器返回的对象", intersects);
        // intersects.length大于0说明,说明选中了模型
        if (intersects.length > 0) {
            // 选中模型的第一个模型,设置为红色
            intersects[0].object.material.color.set(0x0000ff);
        }
    })
    // 3. 屏幕坐标转标准设备坐标 Ene

    // Raycaster(射线拾取模型) Start
    const raycaster = new THREE.Raycaster();
    console.log('射线属性',raycaster.ray);
    // 设置射线起点
    raycaster.ray.origin = new THREE.Vector3(-100, 0, 0);
    // 设置射线方向射线方向沿着x轴
    raycaster.ray.direction = new THREE.Vector3(1, 0, 0);
    // 射线发射拾取模型对象
    const intersects = raycaster.intersectObjects([mesh1, mesh2, mesh]);
    console.log("射线器返回的对象", intersects);
    // intersects.length大于0说明,说明选中了模型
    if (intersects.length > 0) {
        console.log("交叉点坐标", intersects[0].point);
        console.log("交叉对象",intersects[0].object);
        console.log("射线原点和交叉点距离",intersects[0].distance);
        intersects[0].object.material.color.set(0xff0000);
    }
    // Raycaster(射线拾取模型) End


    // 动画渲染
    // 跟踪时间
    var clock = new THREE.Clock();
    function render() {
      const spt = clock.getDelta() * 1000;
      // mesh.rotation.y += 0.01;
      renderer.render(scene, camera);
      requestAnimationFrame(render);
    }

    render();

    // 控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.addEventListener('change', () => {
      // 因为动画渲染了,所以这里可以省略
      // renderer.render(scene, camera);
    });
  script>
html>

你可能感兴趣的:(Web3D,Web3D,threejs)