首页
/ three.js LightProbeHelper 完全指南:用球体可视化场景光探针的 SH 辐照度

three.js LightProbeHelper 完全指南:用球体可视化场景光探针的 SH 辐照度

2026-09-07 11:53:00作者:何将鹤

LightProbeHelper 是 three.js 提供的一个辅助可视化工具,它在场景中渲染一个球体,把 LightProbe 通过球谐函数(Spherical Harmonics)编码的环境辐照度直观地呈现在球面上。本指南围绕其构造参数、属性、方法、WebGL/WebGPU 双后端实现差异展开,并结合 LightProbeHelper.js 源码与官方示例说明如何用它调试环境光照、对比探针采样效果。

快速上手:最小可用代码

LightProbeHelper 是继承自 Mesh 的辅助类(完整继承链为 EventDispatcher → Object3D → Mesh),只需传入一个 LightProbe 实例即可创建可视化的光探针球体,并将其加入场景:

import { LightProbe } from 'three';
import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelper.js';

// 先创建一个光探针(此处为空白探针,实际常由环境贴图生成)
const lightProbe = new LightProbe();
scene.add( lightProbe );

// 用球体可视化该探针,size 默认 1
const helper = new LightProbeHelper( lightProbe );
scene.add( helper );

运行后,场景中会出现一个球体,其表面颜色代表该探针所在位置测得的环境光辐照度:球面某处的颜色越接近真实光照方向与强度,说明该探针的 SH 系数质量越好。

安装与导入方式

LightProbeHelper 属于 examples 目录下的 addon(附加组件),不在 three 核心包内,需要显式导入,不能直接从 'three' 引入。标准导入语句为:

import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelper.js';

该 addon 同时被 Addons.js 的聚合导出覆盖,因此在支持别名构建的环境下也可通过 three/addons 统一入口获取。若你使用 npm 安装的 three 包,构建工具解析 three/addons/... 时实际会定位到仓库中的 examples/jsm 目录。

WebGL 与 WebGPU 必须区分入口:本文讲解的 LightProbeHelper.js 只能在 WebGLRenderer 下使用;当渲染器为 WebGPURenderer 时,必须改从 LightProbeHelperGPU.js 导入(见下文“WebGPU 变体”小节)。

构造函数参数详解

new LightProbeHelper( lightProbe : LightProbe, size : number )

参数 类型 说明 默认值
lightProbe LightProbe 需要可视化的光探针对象 无(必填)
size number 辅助球体的尺寸 1

其中:

  • lightProbe:要可视化的光探针。three.js 中的 LightProbeLight 的子类,自身不发光,而是通过 sh(类型为 SphericalHarmonics3)存储球谐系数,并在渲染时用这些数据近似计算照射到物体上的光。仓库注释将其描述为“与辐照度环境贴图(irradiance environment map)功能等价的漫反射光探针”。
  • size:helper 球体的半径尺寸。源码中构造器通过 this.size = size 保存该值,默认 1。

属性(Properties)

.lightProbe : LightProbe

返回被可视化的光探针。类型 LightProbe。官方示例常通过修改 lightProbe.intensity 来实时调整光照强度,由于 onBeforeRender 实现 会在每帧渲染前把 lightProbe.intensity 同步进材质 uniform,helper 球面的亮度也会随之实时变化。

.size : number

helper 的尺寸,默认 1。实际缩放逻辑为:每帧渲染前,helper 的 scale 会被重置为 (1, 1, 1) 后再统一乘以 this.size(见 .scale.set( 1, 1, 1 ).multiplyScalar( this.size )),因此该属性是球体半径的等比放大因子,数值大于 1 放大、小于 1 缩小。

此外源码中还在构造器里设置 this.type = 'LightProbeHelper',可用于运行时类型判断。

方法(Methods)

.dispose()

释放该实例占用的 GPU 相关资源。从源码看,它执行了:

dispose() {
	this.geometry.dispose();
	this.material.dispose();
}

即同时释放内部创建的 SphereGeometryShaderMaterial。官方文档明确指出:当实例不再需要使用时,必须调用该方法。常见的清理写法如下:

scene.remove( helper );
helper.dispose();

若在单帧内需要创建/销毁大量 helper(例如逐帧重建探针的调试 UI),建议复用同一 helper 并配合 dispose() 及时回收,避免 GPU 内存泄漏。

源码级的实现原理

几何体与材质

LightProbeHelper.js 的构造器可以看到其内部实现细节:

  • 几何体new SphereGeometry( 1, 32, 16 ),即半径 1、32 段宽、16 段高的单位球。真正的“尺寸”通过物体的 scale 缩放实现,而不是修改几何体参数。
  • 材质:自建 ShaderMaterial,type 标记为 LightProbeHelperMaterial,包含两个 uniform:
    • sh按引用传入 lightProbe.sh.coefficients(9 个三维球谐系数向量,对应 3 阶/band 0~2);
    • intensity:取自 lightProbe.intensity

片元着色器如何用 SH 系数画颜色

helper 的可视化本质是把每个片元法线方向的“球谐辐照度”编码为颜色输出。片元着色器核心逻辑为:

  1. 将片元法线从视图空间反变换到世界空间(利用正交视图矩阵的性质,normalize( ( vec4( normal, 0.0 ) * viewMatrix ).xyz ));
  2. 调用 shGetIrradianceAt( worldNormal, sh ) 计算辐照度——该函数用 3 阶球谐基(band 0 的常数项 0.886227,band 1 的线性项 0.511664,band 2 的二次项 0.429043/0.743125 等系数)对 9 个系数加权求和,这一辐照度重建算法源于经典的球谐环境光照论文(源码注释中标注了 Stanford 的 envmap 论文出处);
  3. 输出 outgoingLight = RECIPROCAL_PI * irradiance * intensity,即辐照度除以 π 得到出射辐射率,再乘探针强度;
  4. 最后用 linearToOutputTexel 做色彩空间转换,保证颜色显示正确。

由此可以理解球面颜色的含义:球面上每个位置显示的颜色 = 该方向法线收到的环境辐照度(除以 π 后)× 探针强度。这正是 LightProbe 在渲染时用于光照近似的那份数据,因此该 helper 非常适合用来“眼见为实”地检查探针采样质量。

每帧自动同步(onBeforeRender)

虽然构造器接收了 lightProbe,但代码还在构造末尾调用了 this.onBeforeRender(),并在每次渲染前执行三件事(onBeforeRender 实现):

onBeforeRender() {
	this.position.copy( this.lightProbe.position );           // 跟随探针位置
	this.scale.set( 1, 1, 1 ).multiplyScalar( this.size );   // 应用尺寸
	this.material.uniforms.intensity.value = this.lightProbe.intensity; // 同步强度
}

也就是说 helper 会自动跟随 LightProbe 的位置。这里有一个重要且易踩坑的事实:LightProbe 的位置本身不参与场景光照计算(示例代码注释明确说明 "position not used in scene lighting calculations (helper honors the position, however)"),探针更像一个“采样点”而非光源。当你在场景中移动 helper 时,它只是可视化地告诉你“如果把探针放在这里,测得的辐照度大致是这样”,并不会真的改变该处物体的受光结果。

WebGPU 变体:LightProbeHelperGPU

当你使用 WebGPURenderer(three.js 的 WebGPU 渲染路径)时,应导入 GPU 版 helper:

import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelperGPU.js';

LightProbeHelperGPU.js 的源码可以看到它与 WebGL 版的差异:

  • 基础类来自 three/webgpu,材质使用 NodeMaterial 而非 ShaderMaterial;
  • SH 系数与强度通过 TSL(Three Shading Language)的 uniformArray / uniform 封装,片元逻辑用 Fn(() => ...) 节点函数描述;
  • 辐照度计算直接复用 TSL 内置函数 getShIrradianceAt( normalWorld, sh ),算法上与 WebGL 版 GLSL 中的 shGetIrradianceAt 等价;
  • 注释中标有 @private,说明该实现主要面向内部/渲染器绑定场景。

其余构造参数、属性语义与 dispose 行为(geometry 与 material 各自 dispose)两个版本完全一致。混淆两个入口是常见错误:在 WebGLRenderer 下使用 GPU 版(或反之)会因材质体系与渲染器不匹配而无法得到预期结果。

实战:用 CubeTexture 生成探针并可视化

LightProbe 通常由(辐射)环境贴图生成,工具类是 LightProbeGenerator源码 提供 fromCubeTexturefromRenderTarget 两个静态工厂方法)。参考官方示例 webgl_lightprobe.html,一个完整闭环如下:

import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js';
import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelper.js';

// 1) 加载 6 面 cube 环境贴图
const urls = genCubeUrls( 'textures/cube/pisa/', '.png' ); // px/nx/py/ny/pz/nz
const cubeTexture = await new THREE.CubeTextureLoader().loadAsync( urls );

// 2) 从贴图生成 LightProbe 并加入场景
const lightProbe = new THREE.LightProbe();
lightProbe.copy( LightProbeGenerator.fromCubeTexture( cubeTexture ) );
lightProbe.intensity = 0.5;
lightProbe.position.set( - 10, 0, 0 ); // 记住:位置不参与光照计算
scene.add( lightProbe );

// 3) 添加 helper 可视化探针
const helper = new LightProbeHelper( lightProbe, 1 );
scene.add( helper );

// 4) 后续可通过 GUI 动态调节探针强度观察球面变化
lightProbe.intensity = newValue; // onBeforeRender 会自动同步到 helper

官方还提供了一个更贴近真实场景的探针采样示例 webgl_lightprobe_cubecamera.html:它从 CubeCamera 渲染出的实时环境图生成探针,来为镜面球提供环境反射。LightProbeHelper 在其中的典型用法就是叠加在采样点位置,实时检查 CubeCamera 周围环境采样是否正确。

把该示例改造为 WebGPU 渲染路径时,可对照 webgpu_lightprobe.htmlwebgpu_lightprobe_cubecamera.html——这两者都改用 LightProbeHelperGPU,从而对比同一套调试流程在两个渲染后端下的写法差异。

常见问题与调试建议

  1. 球体没有颜色 / 全黑:确认 lightProbe.sh.coefficients 是否已被写入有效数据(空探针 new LightProbe() 的 SH 系数全为 0,自然全黑);确认 intensity 不为 0。创建探针后务必调用 LightProbeGenerator.fromCubeTexture(...) 或类似手段填充系数。
  2. 导入路径错误:WebGLRenderer 用 helpers/LightProbeHelper.js,WebGPURenderer 用 helpers/LightProbeHelperGPU.js,两者不可混用。
  3. helper 不随探针移动:位置同步发生在每帧 onBeforeRender,需保证 helper 已在场景中且场景正常进入渲染循环。
  4. 忘记释放资源:移除 helper 时应一并调用 dispose(),尤其在高频创建/销毁探针的编辑器或调试面板场景中。
  5. 可视化与真实光照不一致:记住 helper 球面表示的是“辐照度除以 π × 强度”的出射辐射率近似,且探针位置不影响任何照明计算,调整位置只会改变可视化位置本身。

相关 API 一览

综上,LightProbeHelper 的定位非常纯粹:把抽象的一组球谐系数渲染成一个肉眼可读的辐照度球。理解它的构造参数、onBeforeRender 的自动同步机制,以及 WebGL/WebGPU 双入口差异,你就能在光照调试与探针质量评估中获得稳定、可靠的可视化反馈。

登录后查看全文
热门项目推荐
相关项目推荐