Three.js碰撞检测 之 射线检测

import * as THREE from "three";

// 定义照相机、场景、渲染器
let camera, scene, renderer;

scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    2000
);
camera.position.set(0, 6, 5);
camera.lookAt(0, 0, 0);

// 渲染器
renderer = new THREE.WebGLRenderer({
    // 抗锯齿
    antialias: true,
    // 设置对数深度缓冲区
    logarithmicDepthBuffer: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0xeeeeee));
document.body.appendChild(renderer.domElement);

// 灯光
var spotLight = new THREE.SpotLight();
spotLight.position.set(30, 30, 30);
spotLight.castShadow = true;
scene.add(spotLight);

// 创建立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({
    color: 0x6688aa,
})
const cube = new THREE.Mesh(geometry, material);
cube.position.x = -1;
scene.add(cube);

// 创建立方体2
const material2 = new THREE.MeshPhongMaterial({
    color: 0x6688aa,
})
const cube2 = new THREE.Mesh(geometry, material2);
cube2.position.x = 1;
scene.add(cube2);

// 为第一个cube绑定移动事件
document.addEventListener(
    'keydown',
    (e) => {
        var ev = e || window.event;
        switch (ev.code) {
            case 'ArrowLeft':
            case 'KeyA':
                cube.position.x -= 0.05;
                break;
            case 'ArrowRight':
            case 'KeyD':
                cube.position.x += 0.05;
                break;
            default:
                break;
        }
    },
    false
)

// 碰撞检测 之 射线检测法
function onIntersect() {
    // 声明一个变量来表示是否碰撞
    let isCollide = false;
    // 获取物体的中心坐标、网格中心、世界坐标
    const centerCoord = cube.position.clone();
    // 获取网格中,几何对象的顶点对象
    const position = cube.geometry.attributes.position;
    // 顶点三维向量
    const vertices = [];

    for (let i = 0; i < position.count; i++) {
        vertices.push(new THREE.Vector3(position.getX(i), position.getY(i), position.getZ(i)));
    }

    for (let i = 0; i < vertices.length; i++) {
        // cube.matrixWorld 物体的世界坐标变换 -- 是一个记录物体旋转、位移的四维矩阵
        // .applyMatrix4() 将vertices[i]向量乘以四维矩阵,获取世界坐标下,网格变换之后的新的三维向量
        let vertexWorldCoord = vertices[i].clone().applyMatrix4(cube.matrixWorld);
        // 然后,将变换后的三维向量,减去物体的中心坐标,获得由中心指向几何对象顶点的向量
        var dir = vertexWorldCoord.clone().sub(centerCoord);
        // 发射光线
        // Raycaster的四个参数介绍
        // origin - 光线投射的原点向量
        // direction - 射线的方向向量,应该归一化.normalize()
        // near - 所有返回的结果,要比near远。near不能为负数,默认值是0
        // far - 所有返回的结果,要比far近。far不能小于near,默认值为无穷大
        let raycaster = new THREE.Raycaster(centerCoord, dir.clone().normalize());
        // 放入要检测的物体cube2,返回相交物体
        let intersects = raycaster.intersectObjects([cube2], true);
        // 如果存在相交物体
        if (intersects.length > 0) {
            // 如果和相交物体的距离,小于,网格中心到顶点的距离,则代表发生了碰撞
            if (intersects[0].distance < dir.length()) {
                isCollide = true;
            }
        }
    }

    return isCollide;
}

// 请求动画帧渲染
function render() {
    requestAnimationFrame(render);
    if (onIntersect()) {
        cube.material.color.set('yellow')
    } else {
        cube.material.color.set(0x6688aa)
    }
    renderer.render(scene, camera);

}
requestAnimationFrame(render);

// 这样还是存在许多的问题,比如说如果cube在一个很大的物体内部,那么就无法进行碰撞检测了。
// 当两个物体能够互相穿过,且有较大部分重合时,检测效果也不理想。
// 涉及到物理引擎这方面还是建议使用ammo.js
posted @ 2022-08-12 13:55  笔下洛璃  阅读(1048)  评论(0编辑  收藏  举报