You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在React-Three-Fiber中配置射线检测(Raycasting)

嘿,我来帮你搞定React-Three-Fiber里的射线检测和文本遮挡问题!R3F其实已经封装了很多原生Three.js的繁琐操作,不用手动单独管理scene、camera和raycaster,下面分步骤给你讲清楚:

在React-Three-Fiber中实现射线检测(支持JSX组件/GLTF网格)

方法一:使用R3F生态的useIntersection钩子(推荐)

这个钩子来自@react-three/drei,专门简化相交检测逻辑,不用手动操作raycaster。步骤非常简单:

  1. 给需要检测的网格组件添加React ref
  2. useIntersection传入这个ref,直接获取相交状态

示例代码:

import { useRef, useState } from 'react'
import { Canvas } from '@react-three/fiber'
import { useIntersection } from '@react-three/drei'

// 自定义JSX网格组件
const HoverableSphere = () => {
  const meshRef = useRef(null)
  // 获取相交状态,第二个参数可配置检测阈值等选项
  const intersection = useIntersection(meshRef, { threshold: 0.1 })
  const isHovered = intersection?.isIntersecting

  return (
    <mesh ref={meshRef} castShadow receiveShadow>
      <sphereGeometry args={[1, 32, 32]} />
      <meshStandardMaterial color={isHovered ? 'hotpink' : 'slategray'} />
    </mesh>
  )
}

// 主组件
function App() {
  return (
    <Canvas camera={{ position: [0, 0, 5] }}>
      <ambientLight intensity={0.5} />
      <directionalLight position={[5, 5, 5]} castShadow />
      <HoverableSphere />
    </Canvas>
  )
}

方法二:手动管理Raycaster(自定义交互逻辑)

如果你需要更底层的控制(比如自定义射线源、批量检测多个物体),可以用useThree钩子获取R3F内部的核心对象:

  1. 通过useThree拿到scene、camera、raycaster和标准化后的鼠标坐标
  2. 给目标网格/GLTF模型添加ref,获取对应的Three.js对象
  3. 在交互事件(如鼠标移动、点击)中调用raycaster.intersectObjects()

示例(检测GLTF模型组):

import { useRef, useState, useEffect } from 'react'
import { Canvas, useThree } from '@react-three/fiber'
import { useGLTF } from '@react-three/drei'

// GLTF模型组件
const CustomModel = ({ forwardedRef }) => {
  const { scene: gltfScene } = useGLTF('/your-model.gltf')
  // 把ref绑定到GLTF场景对象上
  return <primitive ref={forwardedRef} object={gltfScene} />
}

// 主组件中的射线检测逻辑
function App() {
  const [hoveredMesh, setHoveredMesh] = useState(null)
  const { scene, camera, raycaster, mouse } = useThree()
  const modelRef = useRef(null)

  useEffect(() => {
    const handleMouseMove = () => {
      // 更新射线的起点(相机)和方向(鼠标位置)
      raycaster.setFromCamera(mouse, camera)
      // 检测模型下的所有子物体,第二个参数true表示递归遍历子层级
      const intersects = raycaster.intersectObjects(modelRef.current.children, true)
      setHoveredMesh(intersects.length > 0 ? intersects[0].object : null)
    }
    window.addEventListener('mousemove', handleMouseMove)
    return () => window.removeEventListener('mousemove', handleMouseMove)
  }, [raycaster, camera, mouse])

  return (
    <Canvas camera={{ position: [0, 0, 10] }}>
      <ambientLight />
      <CustomModel forwardedRef={modelRef} />
      {/* 给选中的网格临时改色 */}
      {hoveredMesh && <primitive object={hoveredMesh} material-color="hotpink" />}
    </Canvas>
  )
}
解决球体另一侧文本遮挡的问题

针对球体遮挡文本的情况,给你几个实用的解决方案:

方案1:禁用深度检测

给文本材质添加depthTest={false},让文本忽略场景深度信息,始终显示在最上层:

<mesh position={[0, 0, -2]}>
  <textGeometry args={['Back Text']} />
  <meshBasicMaterial color="white" depthTest={false} />
</mesh>

方案2:调整渲染顺序

设置文本的renderOrder为更高值(默认是0,值越大越晚渲染),让它在球体之后渲染:

{/* 球体先渲染 */}
<mesh renderOrder={0}>
  <sphereGeometry args={[1, 32, 32]} />
  <meshStandardMaterial color="slategray" />
</mesh>
{/* 文本后渲染,不会被遮挡 */}
<mesh position={[0, 0, -2]} renderOrder={1}>
  <textGeometry args={['Back Text']} />
  <meshBasicMaterial color="white" />
</mesh>

方案3:双面文本+内部放置

如果文本在球体内部,给材质设置side={THREE.DoubleSide}确保两面可见,同时调整位置到球体内部:

import * as THREE from 'three'

<mesh position={[0, 0, 0]}>
  <textGeometry args={['Inner Text']} />
  <meshBasicMaterial color="white" side={THREE.DoubleSide} />
</mesh>

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

火山引擎 最新活动