three.js LightProbeHelper 完全指南:用球体可视化场景光探针的 SH 辐照度
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 中的 LightProbe 是
Light的子类,自身不发光,而是通过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();
}
即同时释放内部创建的 SphereGeometry 与 ShaderMaterial。官方文档明确指出:当实例不再需要使用时,必须调用该方法。常见的清理写法如下:
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 的可视化本质是把每个片元法线方向的“球谐辐照度”编码为颜色输出。片元着色器核心逻辑为:
- 将片元法线从视图空间反变换到世界空间(利用正交视图矩阵的性质,
normalize( ( vec4( normal, 0.0 ) * viewMatrix ).xyz )); - 调用
shGetIrradianceAt( worldNormal, sh )计算辐照度——该函数用 3 阶球谐基(band 0 的常数项0.886227,band 1 的线性项0.511664,band 2 的二次项0.429043/0.743125等系数)对 9 个系数加权求和,这一辐照度重建算法源于经典的球谐环境光照论文(源码注释中标注了 Stanford 的 envmap 论文出处); - 输出
outgoingLight = RECIPROCAL_PI * irradiance * intensity,即辐照度除以 π 得到出射辐射率,再乘探针强度; - 最后用
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(源码 提供 fromCubeTexture 与 fromRenderTarget 两个静态工厂方法)。参考官方示例 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.html 与 webgpu_lightprobe_cubecamera.html——这两者都改用 LightProbeHelperGPU,从而对比同一套调试流程在两个渲染后端下的写法差异。
常见问题与调试建议
- 球体没有颜色 / 全黑:确认
lightProbe.sh.coefficients是否已被写入有效数据(空探针new LightProbe()的 SH 系数全为 0,自然全黑);确认intensity不为 0。创建探针后务必调用LightProbeGenerator.fromCubeTexture(...)或类似手段填充系数。 - 导入路径错误:WebGLRenderer 用 helpers/LightProbeHelper.js,WebGPURenderer 用 helpers/LightProbeHelperGPU.js,两者不可混用。
- helper 不随探针移动:位置同步发生在每帧
onBeforeRender,需保证 helper 已在场景中且场景正常进入渲染循环。 - 忘记释放资源:移除 helper 时应一并调用
dispose(),尤其在高频创建/销毁探针的编辑器或调试面板场景中。 - 可视化与真实光照不一致:记住 helper 球面表示的是“辐照度除以 π × 强度”的出射辐射率近似,且探针位置不影响任何照明计算,调整位置只会改变可视化位置本身。
相关 API 一览
- LightProbe:被可视化的数据源,
sh属性为SphericalHarmonics3球谐系数集合; - LightProbeGenerator:从 cube 贴图或渲染目标生成 LightProbe 的工厂类;
- LightProbeGrid / LightProbeGridHelper:面向探针网格化光照方案的进阶配套,可用于生成探针阵列与可视化辅助;
- 源码参考:WebGL 版实现、WebGPU 版实现、LightProbe 核心类。
综上,LightProbeHelper 的定位非常纯粹:把抽象的一组球谐系数渲染成一个肉眼可读的辐照度球。理解它的构造参数、onBeforeRender 的自动同步机制,以及 WebGL/WebGPU 双入口差异,你就能在光照调试与探针质量评估中获得稳定、可靠的可视化反馈。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00