You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js如何创建始终位于相机前方的Raycaster相交平面

Three.js 射线求交平面随相机同步移动方案

问题描述

场景中已实现物体跟随鼠标朝向旋转的交互:通过Raycaster射线投射与固定平面求交,获取鼠标对应的三维空间坐标后,调用物体的lookAt方法完成旋转,初始视角下运行正常。
核心初始化代码如下:

this.intersectionPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 10);
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.pointOfIntersection = new THREE.Vector3();

原有鼠标移动事件处理逻辑:

onDocumentMouseMove = (event) => {
        event.preventDefault();
        this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        this.raycaster.setFromCamera(this.mouse, this.camera);
        this.raycaster.ray.intersectPlane(this.intersectionPlane, this.pointOfIntersection);
        let v3 = new THREE.Vector3(this.pointOfIntersection.x*0.05, this.pointOfIntersection.y*0.05, this.pointOfIntersection.z);
        if(this.pebbleLogo){
            this.pebbleLogo.lookAt(v3);
        }
        if(this.videoWall){
            this.videoWall.lookAt(v3);
    }
}

存在的问题:

  • 原有相交平面是世界空间下的固定平面,当相机动画飞行到z轴-20深度位置时会穿过该平面,导致射线求交逻辑失效
  • 尝试调用translate方法平移平面,无法稳定维持平面与相机的固定相对位置
  • THREE.Plane是纯数学对象,不属于Object3D类型,无法作为相机子物体加入场景层级,跟随相机同步变换的方案不生效

THREE.Plane的两个核心属性定义:

normal - 单位长度向量,定义平面法线方向,默认值(1, 0, 0)
constant - 原点到平面的有符号距离,默认值0

可行解决方案

不需要新增场景物体,也不需要修改现有交互逻辑,只需要每次执行射线求交前,根据相机当前的位置和朝向动态更新平面的法线与constant值,即可让平面始终保持在相机正前方固定距离处,完全避免相机穿平面的问题。
具体修改逻辑:

  • 提前定义平面到相机的固定距离(可根据场景尺度调整,比如取15单位,只要值在相机近裁剪面和远裁剪面范围内即可)
  • 每次触发鼠标移动事件时,先获取相机当前的世界空间前方向量,作为平面的新法线
  • 根据平面点法式公式,计算当前平面的constant值,保证平面沿法线方向、距离相机为预设的固定距离
  • 增加求交结果校验,避免极端情况下求交返回null导致坐标异常

修改后的事件处理代码:

onDocumentMouseMove = (event) => {
        event.preventDefault();
        this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

        // 动态更新相交平面,始终保持在相机正前方固定距离
        const currentCamDir = new THREE.Vector3();
        this.camera.getWorldDirection(currentCamDir);
        this.intersectionPlane.normal.copy(currentCamDir);
        const fixedPlaneDistance = 15; // 可根据场景尺度自行调整
        this.intersectionPlane.constant = -this.camera.position.dot(currentCamDir) + fixedPlaneDistance;

        this.raycaster.setFromCamera(this.mouse, this.camera);
        // 校验求交结果,无效则直接返回
        const hasIntersection = this.raycaster.ray.intersectPlane(this.intersectionPlane, this.pointOfIntersection);
        if (!hasIntersection) return;

        let v3 = new THREE.Vector3(this.pointOfIntersection.x*0.05, this.pointOfIntersection.y*0.05, this.pointOfIntersection.z);
        if(this.pebbleLogo){
            this.pebbleLogo.lookAt(v3);
        }
        if(this.videoWall){
            this.videoWall.lookAt(v3);
        }
    }

之前手动translate平面失效的核心原因是没有同步更新平面法线,相机发生旋转后平面朝向和相机视线方向不垂直,自然无法保持在相机正前方。该方案直接在数学层面同步平面的朝向和位置,和场景层级完全解耦,性能开销可以忽略。


内容的提问来源于stack exchange,提问作者Lee Probert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 01:09:58