Three.js教程
r3f
Usehelper

【Three.js + R3F】全面掌握 useHelper:让调试可视化

在调试 3D 场景时,很多人会遇到这样的痛点:

  • 光照打得太暗或太亮,看不出方向;

  • 相机视锥、包围盒、平面等边界不可见;

  • 不确定当前物体或阴影的范围是否正确。

而在 React Three Fiber(R3F)中,这些问题都可以通过一个极其实用的 Hook 来解决 —— useHelper

一、useHelper 是什么?

useHelper@react-three/drei 提供的一个调试辅助 Hook,
用于在场景中可视化相机、光源、坐标轴、包围盒等对象的辅助线

它的本质是对 Three.js 中各类 THREE.*Helper 对象的封装,
在 React 环境中声明式使用,自动挂载、自动清理。

例如,在原生 Three.js 中:

scene.add(new THREE.CameraHelper(camera))

而在 R3F 中,只需要一句:

useHelper(cameraRef, CameraHelper)

二、基本语法

useHelper(ref, HelperClass, ...args)
参数说明
ref指向要可视化的对象(相机、光源、Mesh 等)
HelperClassThree.js 内置的 Helper 类,例如 CameraHelper
...args其他可选参数(颜色、尺寸等)

三、常见 Helper 类型

类型对应 Helper示例用法
相机CameraHelperuseHelper(ref, CameraHelper, 'cyan')
平行光DirectionalLightHelperuseHelper(ref, DirectionalLightHelper, 5, 'yellow')
聚光灯SpotLightHelperuseHelper(ref, SpotLightHelper, 'red')
点光源PointLightHelperuseHelper(ref, PointLightHelper, 1)
半球光HemisphereLightHelperuseHelper(ref, HemisphereLightHelper, 2)
平面PlaneHelperuseHelper(plane, PlaneHelper, 5, '#00ff00')
包围盒BoxHelperuseHelper(meshRef, BoxHelper, 'blue')
矩形光源RectAreaLightHelperuseHelper(ref, RectAreaLightHelper)
坐标轴<axesHelper args={[size]} />直接 JSX 使用
网格<gridHelper args={[size, divisions]} />直接 JSX 使用
极坐标网格<polarGridHelper args={[radius, radials]} />JSX 使用

四、最佳实践

  1. 仅在开发调试阶段使用
    这些辅助对象会增加渲染负担,不建议在正式环境启用。
  2. 组合使用多种 Helper
    例如:
useHelper(dirLightRef, DirectionalLightHelper, 5, 'yellow')
useHelper(dirLightRef.current?.shadow?.camera, CameraHelper)

→ 同时展示光照方向与阴影相机的可视范围。

五、独立型 Helper 与依附型 Helper 的区别

类型示例使用方式
依附型 Helper相机、光源、包围盒等必须传入 refuseHelper(ref, Helper)
独立型 Helper坐标轴、网格、极坐标网格直接 JSX:<axesHelper /><gridHelper />

六、完整示例:覆盖所有常见 Helper

以下示例包含:

  • 相机(透视、正交)

  • 各类光源与阴影

  • 坐标轴、网格、极坐标网格

  • BoxHelper、PlaneHelper、RectAreaLightHelper

import { Canvas, useFrame } from "@react-three/fiber";
import * as THREE from "three";
import {
  PerspectiveCamera,
  OrthographicCamera,
  CameraControls,
  useHelper,
} from "@react-three/drei";
import React, { useRef, useMemo } from "react";
import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper.js";
 
const HelpersScene = () => {
  // 为了从 THREE 解构需要的 Helper 类
  const {
    CameraHelper,
    DirectionalLightHelper,
    SpotLightHelper,
    PointLightHelper,
    HemisphereLightHelper,
    BoxHelper,
    Plane,
    PlaneHelper,
    Color,
    Vector3,
  } = THREE;
 
  // === 相机 ===
  const perspRef = useRef();
  const orthoRef = useRef();
  useHelper(perspRef, CameraHelper, "cyan");   //  透视相机 helper
  useHelper(orthoRef, CameraHelper, "orange"); //  正交相机 helper
 
  // === 光源 ===
  const dirRef = useRef();
  const spotRef = useRef();
  const pointRef = useRef();
  const hemiRef = useRef();
  const rectRef = useRef();
 
  useHelper(dirRef, DirectionalLightHelper, 3, "yellow"); // 显示平行光方向与光锥,第三个参数为 helper 大小(长度)与颜色
  useHelper(spotRef, SpotLightHelper, "red");             // 显示聚光灯的方向与角度(颜色可选,用于观察光锥范围)
  useHelper(pointRef, PointLightHelper, 0.6, "white");    // 显示点光源位置与可视半径(尺寸、颜色可选)
  useHelper(hemiRef, HemisphereLightHelper, 1.5);         // 显示半球光的上下颜色对比与可视尺寸
  useHelper(rectRef, RectAreaLightHelper);                // 显示矩形区域光的方向和形状(无颜色参数,主要用于调试方向)
 
  // === BoxHelper (包围盒) ===
  const boxTarget = useRef();
  useHelper(boxTarget, BoxHelper, new Color("#38bdf8"));
 
  // === PlaneHelper(几何平面可视化)===
  // 注意:PlaneHelper 需要的是“平面对象”本身,而不是 ref。
  const plane = useMemo(() => new Plane(new Vector3(0, 1, 0), 0), []);
  useHelper(plane, PlaneHelper, 5, "#10b981");            //  关键修正点
 
  // 动一动盒子,方便观察 BoxHelper 变化
  useFrame(({ clock }) => {
    const t = clock.getElapsedTime();
    if (boxTarget.current) {
      boxTarget.current.rotation.y = t * 0.5;
      boxTarget.current.position.x = Math.sin(t) * 1.2;
    }
  });
 
  return (
    <>
      {/* 透视相机(主相机) */}
      <PerspectiveCamera
        ref={perspRef}
        makeDefault
        position={[6, 4, 10]}
        fov={55}
        near={0.1}
        far={200}
      />
 
      {/* 正交相机(展示 helper,不作为默认相机) */}
      <OrthographicCamera ref={orthoRef} position={[-8, 6, 10]} zoom={50} />
 
      {/* 控制器 */}
      <CameraControls dollySpeed={0.3} truckSpeed={0.6} />
 
      {/* 灯光们 */}
      <hemisphereLight ref={hemiRef} position={[0, 6, 0]} intensity={0.4} />
      <directionalLight
        ref={dirRef}
        position={[6, 10, 6]}
        intensity={1.2}
        castShadow
      />
      <spotLight
        ref={spotRef}
        position={[-6, 8, 2]}
        angle={0.4}
        penumbra={0.3}
        intensity={1.2}
        castShadow
      />
      <pointLight ref={pointRef} position={[0, 3, -6]} intensity={1.2} />
 
      {/* RectAreaLight(这里只演示 helper,可见光照需配合 physicallyCorrectLights 等) */}
      <rectAreaLight
        ref={rectRef}
        position={[0, 5, 6]}
        width={2.5}
        height={1.2}
        intensity={5}
        lookAt={[0, 0, 0]}
      />
 
      {/* ====== 这些“独立型” Helper 用 JSX 组件而不是 useHelper ====== */}
      {/* 坐标轴 */}
      <axesHelper args={[3]} />
      {/* 网格(size, divisions, color1, color2) */}
      <gridHelper args={[20, 20]} position={[0, -1.5, 0]} />
      {/* 极坐标网格(radius, radials, circles, divisions, color1, color2) */}
      <polarGridHelper args={[10, 16]} position={[0, -1.49, 0]} />
 
      {/* 地面 */}
      <mesh rotation-x={-Math.PI / 2} position={[0, -1.5, 0]} receiveShadow>
        <planeGeometry args={[100, 100]} />
        <meshStandardMaterial color="#0f172a" roughness={0.9} metalness={0.1} />
      </mesh>
 
      {/* 用于 BoxHelper 的目标物体 */}
      <group position={[0, 0, 0]} ref={boxTarget}>
        <mesh castShadow receiveShadow>
          <boxGeometry args={[1.2, 1.2, 1.2]} />
          <meshStandardMaterial
            color="#38bdf8"
            metalness={0.3}
            roughness={0.4}
          />
        </mesh>
        <mesh position={[0, 1.1, 0]} castShadow>
          <sphereGeometry args={[0.45, 32, 32]} />
          <meshStandardMaterial
            color="#a78bfa"
            metalness={0.2}
            roughness={0.5}
          />
        </mesh>
      </group>
 
      {/* 一些参考物体 */}
      <mesh
        position={[3, -0.5, -2]}
        rotation-y={Math.PI / 6}
        castShadow
        receiveShadow
      >
        <cylinderGeometry args={[0.5, 0.5, 2, 32]} />
        <meshStandardMaterial color="#10b981" />
      </mesh>
 
      <mesh
        position={[-3, -1.0, 2]}
        castShadow
        receiveShadow
        rotation-x={-Math.PI / 12}
      >
        <torusKnotGeometry args={[0.5, 0.18, 120, 16]} />
        <meshStandardMaterial
          color="#f59e0b"
          metalness={0.4}
          roughness={0.35}
        />
      </mesh>
    </>
  );
};
 
const Helper = () => {
  return (
    <Canvas
      shadows
      dpr={[1, 2]}
      gl={{ antialias: true }}
      camera={{ position: [6, 4, 10], fov: 55 }}
    >
      <color attach="background" args={["#0b1120"]} />
      <HelpersScene />
    </Canvas>
  );
};
 
export default Helper;

七、运行效果与总结

运行后,你会看到完整的调试辅助线:

  • 蓝/橙线框:相机视锥体(透视 + 正交)

  • 黄/红/白灯光辅助箭头:表示光照方向与角度

  • 绿色平面:PlaneHelper 显示的几何平面

  • 蓝色包围框:BoxHelper 动态跟随旋转物体

  • 坐标轴 + 网格:提供场景参考系

这些可视化线框能帮助开发者:

  • 确认相机和光照是否正确;

  • 调整阴影范围;

  • 验证几何体与包围盒的精度;

  • 快速定位场景问题。

八、总结要点

内容说明
主要用途可视化调试相机、光源、平面、包围盒等
导入方式import { useHelper } from "@react-three/drei"
使用方式useHelper(ref, HelperClass, ...args)
特殊情况坐标/网格类用 JSX;PlaneHelper 直接传 Plane 对象
建议开发调试启用,生产关闭

🧭 结语
useHelper 是 Three.js 调试阶段最有价值的工具之一。
它不参与渲染逻辑,却能让你“看清”场景内部的结构与光照逻辑。
在构建复杂的可视化项目时,它几乎是必备的开发助手。

是否希望我在文章末尾帮你补一段「公众号摘要 + 封面图文建议」?
可以用于微信后台发布时的封面摘要和推荐语。