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

旋转Mesh多方向射线投射问题:射线方向未随旋转更新

解决旋转Mesh的多方向射线投射问题

问题说明

我尝试从一个旋转的Mesh发射多条射线,射线朝向按数量均分圆周的多个方向。调试时给每条射线添加了ArrowHelpers,预期箭头命中物体时变为红色、未命中时为白色,长度随命中距离变化,未命中时保持射线的最大长度(far)。但目前所有射线始终检测Mesh的同一前方方向,尝试过object3D.localToGlobal、Vector3.applyQuaternion等方法,因数学基础不足未能解决。

错误原因

原代码存在两个核心问题:

  • 循环处理射线时,错误复用第一条射线的方向向量做变换,导致所有射线方向完全一致
  • 方向变换逻辑错误,没有正确将Mesh本地坐标系下的射线方向转换为世界坐标系方向

修正后的代码

import { RefObject } from "react";
import * as THREE from "three";
import React from "react";
import { useFrame, useThree } from "@react-three/fiber";

export type RayCastResult = {
  hit: boolean;
  angle: number;
  direction: THREE.Vector3;
  distance: number;
};

export const useRaycasts = ({
  count = 4,
  near = 1,
  far = 10,
  obj
}: {
  count?: number;
  near?: number;
  far?: number;
  obj: RefObject<THREE.Mesh>;
}): { rays: RayCastResult[] } => {
  const rays = React.useMemo(() => {
    const rays: RayCastResult[] = [];
    let angle = 0;
    const step = (2 * Math.PI) / count;

    for (let i = 0; i < count; i++) {
      // 初始化Mesh本地坐标系下的射线方向(绕Y轴均分圆周)
      rays.push({
        hit: false,
        angle: angle,
        direction: new THREE.Vector3(
          Math.cos(angle),
          0,
          Math.sin(angle)
        ).normalize(),
        distance: far
      });
      angle += step;
    }

    return rays;
  }, [count, far]);

  const pos = React.useMemo(() => new THREE.Vector3(), []);
  // 临时向量,避免修改原射线的本地方向数据
  const worldDir = React.useMemo(() => new THREE.Vector3(), []);

  const { scene, raycaster } = useThree();

  useFrame(() => {
    if (!obj.current) return;

    const mesh = obj.current;
    mesh.getWorldPosition(pos);
    const quaternion = mesh.quaternion;

    raycaster.near = near;
    raycaster.far = far;

    rays.forEach((rayResult, i) => {
      // 将本地方向应用Mesh的四元数,转换为世界空间下的方向
      worldDir.copy(rayResult.direction).applyQuaternion(quaternion).normalize();
      
      raycaster.set(pos, worldDir);
      // 第二个参数设为true,确保检测场景中所有嵌套子物体
      const intersects = raycaster.intersectObjects(scene.children, true);

      // 更新射线检测结果
      if (intersects.length > 0) {
        rayResult.hit = true;
        rayResult.distance = intersects[0].distance;
      } else {
        rayResult.hit = false;
        rayResult.distance = far;
      }
    });
  });

  return { rays };
};

关键修正点

  • 本地方向转世界方向:使用Vector3.applyQuaternion(quaternion)是将物体本地方向转换为世界方向的标准方法,能正确继承Mesh的所有旋转信息
  • 避免复用错误:循环中使用当前索引i对应的射线本地方向,不再固定复用第一条
  • 数据隔离:用worldDir临时向量存储转换后的方向,防止修改原射线的基础方向数据
  • 优化检测范围:intersectObjects传入第二个参数true,确保检测场景中所有嵌套的子物体(如果你的Mesh有层级结构)

内容的提问来源于stack exchange,提问作者Diederik Van Remoortere

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:40:17