首页
/ three.js AmbientLight 环境光:原理、参数与渲染管线实现详解

three.js AmbientLight 环境光:原理、参数与渲染管线实现详解

2026-09-06 13:24:43作者:冯梦姬Eddie

在 three.js 场景照明体系中,AmbientLight(环境光)是最基础、使用最广泛的光源类型——它为场景中所有对象提供均匀的全局基础照明,模拟漫射光的整体氛围。本文基于官方 API 文档 docs/pages/AmbientLight.html.md 与仓库源码,完整讲解 AmbientLight 的构造参数、继承关系、类型标志,并深入其在 WebGL 渲染管线中的真实实现路径,帮助你从「会用」进阶到「理解其原理」。

一、AmbientLight 是什么:全局均匀照明

官方文档对 AmbientLight 的定义有两句关键描述:

  1. This light globally illuminates all objects in the scene equally(该光源对场景中的对象施加全局均匀的照明);
  2. It cannot be used to cast shadows as it does not have a direction(由于它没有方向,因此无法用于投射阴影)。

这两点决定了环境光的定位:它不描述任何「光从哪来」,而是给整个场景铺一层底色,让没有直接光照(平行光、点光、聚光灯)的区域不会陷入纯黑。因此实际项目中,AmbientLight 通常与带方向的 DirectionalLightPointLight 等配合使用——环境光负责「保底亮度」,方向光负责塑造立体感和阴影。

最简用法如下(与官方文档示例一致):

const light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );

这里传入的 0x404040 是一个偏暗的灰白色,文档注释明确称之为 "soft white light"(柔和白光)。由于环境光没有方向和衰减概念,scene.add() 后它的位置属性(position/rotation)对渲染结果没有任何影响。

二、构造参数:color 与 intensity

构造函数签名(继承自文档 docs/pages/AmbientLight.html.md 的 Constructor 一节):

new AmbientLight( color : number | Color | string, intensity : number )
参数 类型 默认值 说明
color number | Color | string 0xffffff(白色) 光源颜色,支持十六进制数、THREE.Color 实例或 CSS 颜色字符串
intensity number 1 光照强度

从基类源码 src/lights/Light.js 可以看到这两个参数的落地方式:

class Light extends Object3D {

	constructor( color, intensity = 1 ) {

		super();

		this.isLight = true;
		this.type = 'Light';
		this.color = new Color( color );   // 支持 number / Color / string
		this.intensity = intensity;        // 默认 1

	}
	// ...
}

三个要点:

  • color 会被归一化为 THREE.Color 对象:无论你传 0x404040'#404040' 还是 new THREE.Color(...),实例上最终持有的是一个 Color 对象,因此后续可以调用 light.color.setHSL() 等方法动态调整颜色;
  • intensity 的默认值是 1:由构造函数参数默认值 intensity = 1 保证;
  • colorintensity 的乘积才进入着色器:这一点在下文的渲染管线分析中会得到验证。

三、类实现:AmbientLight 到底「轻」在哪里

AmbientLight 的完整实现只有 40 行,位于 src/lights/AmbientLight.js

import { Light } from './Light.js';

class AmbientLight extends Light {

	constructor( color, intensity ) {

		super( color, intensity );

		/**
		 * This flag can be used for type testing.
		 */
		this.isAmbientLight = true;

		this.type = 'AmbientLight';

	}

}

export { AmbientLight };

实现极其精简,原因正是环境光的物理模型本身不含任何空间属性——没有位置、没有方向、没有衰减、没有阴影,全部行为都由基类 Lightcolor/intensity 覆盖。文档给出的继承链在代码中完全对应:

EventDispatcher → Object3D → Light → AmbientLight

其中 Object3D 提供场景图能力(可 scene.add()、可变换),Light 提供光照属性,AmbientLight 仅追加两个类型标识:

标识 默认值 用途
.isAmbientLight true(readonly) 类型测试标志。文档明确说明 "This flag can be used for type testing"
.type 'AmbientLight' 字符串类型名,序列化与内部渲染调度均依赖它

这种「布尔标志 + 字符串 type 双重标识」是 three.js 中所有内建类型的统一约定,例如渲染器判断一个光源是否为环境光时,用的就是 light.isAmbientLight 检查(见下文)。

从 Light 基类继承的公共能力

由于 AmbientLight 完全继承 Light,以下能力自动可用:

  • copy( source ):深拷贝光源属性。src/lights/Light.jscopy() 会执行 this.color.copy( source.color )this.intensity = source.intensity,即克隆一个环境光时颜色和强度都会被完整复制;
  • toJSON( meta ):序列化时将 color 转为十六进制、intensity 原样输出,即 data.object.color = this.color.getHex()。这保证了 AmbientLight 可以被 GLTFExporter 等导出流程完整保存;
  • 作为 Object3D 的变换、可见性(visible)、层(layers)等场景图能力。

四、渲染管线深挖:环境光如何变成最终像素

这是 AmbientLight 实现中最值得理解的部分——它并没有像其他光源那样拥有独立 uniform,而是被累加进一个共享的 ambientLightColor 均匀变量。整个链路分三步:

1. WebGLLights:按帧累加所有环境光

src/renderers/webgl/WebGLLights.js 的每帧光源状态更新中,渲染器遍历场景中所有光源,遇到环境光时执行 RGB 三通道累加:

if ( light.isAmbientLight ) {

	r += color.r * intensity;
	g += color.g * intensity;
	b += color.b * intensity;

} else if ( light.isLightProbe ) {
	// ...
}

这段代码印证了两个事实:

  • 颜色与强度是相乘关系color * intensity 先算好,再参与累加。因此 new AmbientLight( 0xffffff, 0.5 )new AmbientLight( 0x808080, 1 ) 的渲染结果一致;
  • 场景中可以叠加多个 AmbientLight:多个环境光的效果是 RGB 分量相加(而非替换),累加后的总量最终写入 ambientLightColor uniform。

值得注意的还有 src/renderers/webgl/WebGLLights.js 中的 UniformsCache:它为 SunLightSpotLightPointLightHemisphereLightRectAreaLight 等分别缓存了方向、距离、锥形参数等 uniform,唯独没有 AmbientLight 的分支——因为环境光不产生任何光源级 uniform,只贡献一个三通道向量,这与它「无方向」的物理特性完全吻合。

2. 片元着色器:getAmbientLightIrradiance

累加结果通过 uniform vec3 ambientLightColor 传入着色器,声明位于 src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js

uniform vec3 ambientLightColor;

同文件中定义了环境光的「辐照度」提取函数:

vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {

	vec3 irradiance = ambientLightColor;

	return irradiance;

}

可以看到其实现是恒等映射——环境光的辐照度就是累加后的颜色值本身,不随法线方向、光源位置变化。这正是「全局均匀照明」在着色器层面的体现。

3. 光照计算入口:lights_fragment_begin

src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js 的片元光照主循环中,环境光辐照度作为第一项被取出,与其余光源的 directLight 结果一起参与最终出射辐射度计算:

vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );

也就是说,对于 PBR 材质(MeshStandardMaterial 等),环境光贡献的是漫反射辐照度项,随后按材质 albedo 着色——这也是为什么环境光下物体表面看不到高光、看不到方向性明暗,只有与材质颜色相乘后的「底色」。

五、单元测试佐证

仓库为 AmbientLight 提供了专门的 QUnit 测试 test/unit/src/lights/AmbientLight.tests.js,测试覆盖的点与文档描述逐项对应:

测试项 验证内容
Extending object instanceof Light === true,确认继承自 Light
Instancing 可无参构造 new AmbientLight()
type object.type === 'AmbientLight'
isAmbientLight 标志为 true
Standard light tests 通过 runStdLightTests(定义于 test/unit/utils/qunit-utils.js)对无参、仅颜色、颜色+强度三种构造形式做标准化验证(含 color/intensity 默认值、copy、toJSON 等)

测试中实际构造了三种形式:

const parameters = { color: 0xaaaaaa, intensity: 0.5 };

lights = [
	new AmbientLight(),                                  // 全默认
	new AmbientLight( parameters.color ),                // 自定义颜色
	new AmbientLight( parameters.color, parameters.intensity ) // 颜色 + 强度
];

这也可以作为三种典型构造写法的参考。此外,test/unit/three.source.unit.js 将该测试文件注册进了源码级测试入口,可通过仓库自带的 QUnit 测试流程运行验证。

六、实战要点小结

  1. 典型配比:环境光强度通常远低于方向光。官方示例使用 0x404040(约 25% 亮度)而非白色,目的就是避免「灰白平光」洗掉物体立体感;
  2. 无阴影是设计使然:环境光没有方向,castShadow 对其无效,阴影应交由 DirectionalLight/PointLight/SpotLight 承担;
  3. 多环境光可叠加:从 WebGLLights.js 的累加逻辑可以确认,场景内多个 AmbientLight 的颜色会相加;
  4. 序列化友好toJSON 输出十六进制颜色与强度,环境光可随场景导出;
  5. 类型判断:在自定义渲染逻辑中,用 light.isAmbientLight(布尔标志)而非字符串 type 做类型测试是 three.js 的惯例,两者在 AmbientLight 源码 中同时被赋值。

至此,从 API 文档的参数说明,到 src/lights/AmbientLight.js 的类实现、src/lights/Light.js 的基类继承,再到 WebGLLights.js 与片元着色器中的渲染路径,AmbientLight 的完整技术图景已经闭环:一个极简的类、一条累加进全局 uniform 的管线、一个恒等辐照度的着色器函数——简洁正是它能做到「全局均匀照明」的原因。

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