three.js Light 光照抽象基类解析:继承体系、核心属性与资源释放机制
【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js
Light是 three.js 中所有光源的抽象基类,AmbientLight、DirectionalLight、PointLight、SpotLight等一切具体光源类型都继承自它。本文以 docs/pages/Light.html.md 文档为骨架,结合 src/lights/Light.js 及其实子类的源码实现,系统讲解 Light 的类继承关系、构造参数语义、color/intensity/isLight 等核心属性的底层实现,以及 dispose() 沿继承链级联释放 GPU 资源的真实机制,帮助你建立“从抽象基类到具体光源”的完整认知。
Light 是什么:位于事件体系顶端的光照抽象层
three.js 的光照系统从Light抽象类开始。它定义了两条最基本的信息——颜色(color)与强度(intensity),具体光源类型只需在此基础上添加方向、衰减范围、阴影等自身特征即可。
从文档页开头给出的继承链可以看到 Light 在整个对象模型中的位置:
EventDispatcher → Object3D → Light也就是说Light继承自 src/core/Object3D.js,因此每个光源实例天然具备三维空间中position、rotation、scale、parent/children等变换与场景图能力(光源可以作为节点加入场景并参与层级变换)。而 Object3D 继承自EventDispatcher,意味着光源上也可以派发与监听事件(例如 Object3D 的dispose事件)。
在实际源码中,Light类定义于 src/lights/Light.js,其中src/lights目录下直接派生出的具体光源包括:AmbientLight(环境光)、DirectionalLight(平行光/方向光)、HemisphereLight(半球光)、PointLight(点光源)、RectAreaLight(矩形面光源)、SpotLight(聚光灯)以及LightProbe(光照探针),另有webgpu子目录存放 WebGPU 后端的光照相关实现。
需要特别说明:
Light是抽象基类,不应直接实例化。文档将其构造函数标注为 abstract,实际使用中应实例化其具体子类,例如new THREE.AmbientLight()、new THREE.PointLight()。
构造函数与参数语义
new Light( color : number | Color | string, intensity : number ) // 抽象,请勿直接调用从 src/lights/Light.js 的构造函数实现可以看到两个参数的完整语义:
| 参数 | 类型 | 默认值 | 语义 |
|---|---|---|---|
color | number \| Color \| string | 0xffffff(白色) | 光源颜色,支持十六进制数值、THREE.Color实例或 CSS 颜色字符串 |
intensity | number | 1 | 光源强度/亮度数值 |
源码中颜色参数通过this.color = new Color( color )统一转换为THREE.Color实例存储(这就是.color属性类型为 Color 的原因,同时也解释了为何构造与属性都接受三种格式的输入);强度则直接原样保存为.intensity。
以AmbientLight为例,它在 src/lights/AmbientLight.js 中的构造函数只是简单透传这两个参数给父类,再补上自己的类型标记:
constructor( color, intensity ) { super( color, intensity ); this.isAmbientLight = true; this.type = 'AmbientLight'; }可见具体光源类型普遍遵循“先 super(color, intensity) 完成基类初始化,再登记自身 isXxxLight 标记与 type”的模板,这与Light基类自身在构造时执行的this.isLight = true; this.type = 'Light';一脉相承。
核心属性详解
.color : Color
光源颜色。默认0xffffff,构造时被包装为Color实例。
在源码中通过new Color( color )创建,因此可直接调用Color的全部方法操作颜色,例如light.color.setHSL( ... )、light.color.copy( otherColor )等。颜色是影响最终渲染表现最直接的属性:例如冷暖光源的区分、氛围色、乃至借助彩色光源制造特殊材质反光效果,都建立在其之上。
.intensity : number
光源强度,默认1。
需要特别注意的是,不同光源类型的 intensity 单位并不统一。以 src/lights/PointLight.js 为例,其构造注释明确写明 intensity 以坎德拉(candela, cd)计量;而同一个文件里提供的power存取器则基于“各向同性点光源:光通量(lm) = 4π × 光强(cd)”的物理关系,在瓦特级物理光效语义下换算流明:
get power() { // 光通量 (lm) = 4 π × 光强 (cd) return this.intensity * 4 * Math.PI; } set power( power ) { this.intensity = power / ( 4 * Math.PI ); }因此,当你在调光时把某类型光源强度设为1,其真实视觉亮度可能与其他类型光源完全不同——这正是“为什么 0.5 的 PointLight 看起来比 0.5 的 AmbientLight 亮得多”的根源。光源的物理光效还涉及衰减:PointLight默认distance = 0(不限距离)、decay = 2(平方反比衰减),源码注释明确指出在物理正确渲染语境下不应改动decay的默认值。
.isLight : boolean(只读)
类型测试标记,恒为true。源码中在构造器内直接赋值this.isLight = true;。
isXxx系列布尔标记是 three.js 全库统一的鸭子类型判断手段(isMesh、isCamera、isPointLight等同理)。你可以在自己的代码里用light.isLight === true判断某个对象是否为光源,而无需instanceof;子类则各自额外带出isAmbientLight、isPointLight等更细粒度的标记,与.type字符串(如'Light'、'PointLight')形成互补:标记面向程序逻辑判断,type面向序列化与工具链识别。
dispose():资源释放的级联链路
文档在方法部分给出的dispose()说明为:“释放该实例占用的 GPU 相关资源;当实例不再使用时务必调用”。
深入源码会发现一个值得澄清的实现细节:抽象基类 Light 自身并没有覆盖 dispose()。src/lights/Light.js 只实现了构造、copy与toJSON;Light上可调用的dispose()实际继承自 src/core/Object3D.js:
dispose() { // @fires Object3D#dispose this.dispatchEvent( { type: 'dispose' } ); }即基类层面的dispose只是派发一个'dispose'事件,让外部监听者(例如场景管理逻辑或渲染器)得以感知对象生命周期。
真正会占用 GPU 资源的,是具体光源的阴影子系统,因此其子类通过重写 dispose() 完成级联释放:
- PointLight.dispose():
super.dispose()(向上级联触发基类事件)后调用this.shadow.dispose(); - DirectionalLight.dispose() 与 SpotLight.dispose() 结构完全相同;
- LightShadow.dispose() 则负责真正释放阴影贴图相关资源——分别对
this.map(阴影深度渲染目标)与this.mapPass(PCF 软阴影二次处理通道)调用各自的dispose()。
由此可以总结出三条实用结论:
- 无阴影的光源(如 AmbientLight、HemisphereLight)几乎没有需要手动释放的 GPU 资源,因为它们不持有阴影贴图渲染目标;
- 启用
castShadow的 PointLight/DirectionalLight/SpotLight 会持有阴影贴图 RenderTarget,在大量动态创建与销毁场景中应记得调用光源的dispose()来回收; dispose()事件沿继承链向上逐级派发,意味着你可以在Object3D层面统一监听'dispose'事件做资源记账或调试,光源释放时会触发该事件。
补充说明:dispose()只负责光源自身(及其阴影)占用的资源。光源关联的几何体、材质与纹理若与其他对象共享,仍需按 src/materials/Material.js 等各自的规则分别释放,这与 Object3D 中“几何体/材质/纹理可能被多个 3D 对象共享,必须单独释放”的注释是一致的。
copy 与 toJSON:克隆与序列化
虽然文档正文没有展开,但 src/lights/Light.js 中的两个方法补齐了基类能力的完整拼图,值得一并理解:
.copy( source, recursive )—— 先调用super.copy()复制 Object3D 的变换、层级等状态,再复制光源特有的两个核心字段:
this.color.copy( source.color ); this.intensity = source.intensity;也就是说克隆一个光源时,color(对象引用深拷贝)与 intensity(值拷贝)是基类保证被继承的语义,而distance、decay、shadow等则由各子类在自身的copy()中追加处理(见 PointLight.copy())。
.toJSON( meta )—— 在 Object3D 序列化结果之上补写光源字段:
data.object.color = this.color.getHex(); data.object.intensity = this.intensity;序列化时颜色以 16 进制数值形式输出。这也解释了color属性为何同时接受 number 输入——JSON 格式本身就以数字表达颜色。整套toJSON输出可供ObjectLoader/ 编辑器 editor/index.html 场景持久化使用。
具体光源速览与选型参考
作为全文收束,下表汇总 src/lights 下各直接子类的核心特征(均基于对应源码实现),供选型参考:
| 光源类 | 核心文件 | 特征 | 可否投阴影 |
|---|---|---|---|
AmbientLight | src/lights/AmbientLight.js | 全局均匀照亮所有物体,无方向无位置概念 | 否(源码注释明确说明) |
DirectionalLight | src/lights/DirectionalLight.js | 平行光,从无穷远照射,自带DirectionalLightShadow | 是 |
PointLight | src/lights/PointLight.js | 单点向四周发射(模拟裸灯泡),带distance/decay/power,自带PointLightShadow | 是 |
SpotLight | src/lights/SpotLight.js | 锥形光束(聚光灯光斑效果) | 是 |
HemisphereLight | src/lights/HemisphereLight.js | 天空-地面双色半球光,常用于模拟环境渐变光照 | 否 |
RectAreaLight | src/lights/RectAreaLight.js | 矩形面光源,适合柔光/影棚光 | 是 |
LightProbe | src/lights/LightProbe.js | 光照探针,记录空间光照信息 | — |
一个典型的入门组合是“环境光 + 主光源”:
// 场景级均匀补光(无方向、不投阴影) const ambient = new THREE.AmbientLight( 0x404040 ); // 柔和白光补暗部 scene.add( ambient ); // 点光源:带颜色、强度与位置 const light = new THREE.PointLight( 0xff0000, 1, 100 ); light.position.set( 50, 50, 50 ); scene.add( light );示例同时展示了 Light 的两个核心参数(color、intensity)与继承自 Object3D 的position配合使用的典型形态。若需要阴影,记得额外设置light.castShadow = true,并在释放资源时调用light.dispose()。
小结
Light作为 three.js 全部光源的共同祖先,用最精简的两个字段(color + intensity)定义了光照的本质,并通过isLight标记、copy/toJSON、级联的dispose()协议,把通用行为固化在了继承链上。理解这个抽象基类,就能以“每类光源 = 基础颜色强度 + 各自的分布与衰减 + 各自的阴影配置”的统一视角去把握整个 three.js 光照体系。更细的用法差异(如距离衰减曲线、阴影 mapSize 配置、物理光照单位换算),建议进一步阅读 AmbientLight、PointLight、DirectionalLight、SpotLight 等各具体光源文档页与对应源码。
【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考