2.2 调光参数:强度、色温与衰减 本节摘要:选对灯只是派岗,拧好三颗旋钮——强度、色温、衰减——灯才有手感。本节解释物理光照模式下强度单位为何反直觉,给出色温的情感区间速查表,并用一次"仓库与展厅双氛围"复刻案例演示调光流程,承接 2.1 的布光结果。 强度为什么"越调越不对" 不少人从旧版教程转过来都撞过同一堵墙:照抄旧代码里 intensity 等于 1 的灯,新版本里画面暗得像停电。原因在渲染器的光照模式:新版 Three.js 默认启用物理光照,点光源与聚光灯的亮度按距离平方衰减,强度数值的量纲也随之变化——近处看着正常、远处必然偏暗,靠拉大强度补救,近处又立刻过曝。这堵墙的正确翻越方式不是换回旧模式,而是理解衰减曲线,让强度与距离配套着调。
本节摘要:选对灯只是派岗,拧好三颗旋钮——强度、色温、衰减——灯才有手感。本节解释物理光照模式下强度单位为何反直觉,给出色温的情感区间速查表,并用一次"仓库与展厅双氛围"复刻案例演示调光流程,承接 2.1 的布光结果。
不少人从旧版教程转过来都撞过同一堵墙:照抄旧代码里 intensity 等于 1 的灯,新版本里画面暗得像停电。原因在渲染器的光照模式:新版 Three.js 默认启用物理光照,点光源与聚光灯的亮度按距离平方衰减,强度数值的量纲也随之变化——近处看着正常、远处必然偏暗,靠拉大强度补救,近处又立刻过曝。这堵墙的正确翻越方式不是换回旧模式,而是理解衰减曲线,让强度与距离配套着调。
调光的顺序也有讲究:先定主光强度,定死之后不再动;再铺底光补阴影面;最后才调点光聚光这类"戏光"。反过来先调戏光,主光一变全部返工——这和装修时先定主灯再配台灯是一个道理。
色温(用十六进制颜色或 Color 对象表达)是最廉价的情绪杠杆,一张速查表胜过千字描述:
| 氛围意图 | 主光色温 | 底光色温 | 典型场景 |
|---|---|---|---|
| 清晨工作室 | 0xfff2e0 | 0xbfd8ff | 产品展示、仪表盘 |
| 正午室内 | 0xffffff | 0xdfe8f0 | 后台大屏、工具类页面 |
| 黄昏仓库 | 0xffd9b0 | 0x6f7d99 | 叙事场景、游戏过场 |
| 冷调展厅 | 0xf0f4ff | 0x8898aa | 高端产品、数字孪生 |
| 舞台夜场 | 0xff9c6b | 0x241f33 | 演出、活动宣传页 |
规律有三条:主光与底光的色温拉开差距,画面立刻有层次;暖主光配冷底光比全暖更耐看,因为对比制造了体积;色温的戏份在中间调,饱和度别拉满——贴着 0xffeee0 这类"几乎白"的颜色调,氛围是渗出来的,不是刷上去的。
背景:客户在沙盘评审会上提了新要求:同一场景要能一键切换"白天理货"与"夜间巡检"两种模式,灯光不得重新写,只许调参。
操作:把所有灯光集中登记在一个配置对象里,切换模式时只改参数不换灯。
// 灯光登记表:模式切换只动参数,不动灯的岗位 const lightRig = { hemi: new THREE.HemisphereLight(0xffffff, 0x888888, 0.6), sun: new THREE.DirectionalLight(0xffffff, 1.2), spot: new THREE.SpotLight(0xffffff, 30, 25, Math.PI / 9, 0.4, 1.5) }; Object.values(lightRig).forEach(l => stage.scene.add(l)); // 两套调光方案:白天理货 / 夜间巡检 const presets = { day: { hemi: { sky: 0xdfe8f0, ground: 0x9a9084, intensity: 0.8 }, sun: { color: 0xffffff, intensity: 1.6 }, spot: { color: 0xffffff, intensity: 0, visible: false } // 白天不需要射灯 }, night: { hemi: { sky: 0x2b3550, ground: 0x1a1712, intensity: 0.35 }, // 夜晚底光压很低 sun: { color: 0x8ea4c8, intensity: 0.5 }, // 月光:冷、弱 spot: { color: 0xffe0b0, intensity: 45, visible: true } // 巡检灯:暖、亮 } }; function applyPreset(name) { const p = presets[name]; const { hemi, sun, spot } = lightRig; hemi.color.set(p.hemi.sky); hemi.groundColor.set(p.hemi.ground); hemi.intensity = p.hemi.intensity; sun.color.set(p.sun.color); sun.intensity = p.sun.intensity; spot.color.set(p.spot.color); spot.intensity = p.spot.intensity; spot.visible = p.spot.visible; } applyPreset('day');
结果:调用 applyPreset('night') 的一瞬间,沙盘从明亮车间沉入夜色——只剩一束暖色巡检灯扫在货箱上,底光微弱得刚好保住轮廓。同一套灯、零个新对象,两种氛围切换成本只是一次函数调用。
解读:这套登记表模式的真正价值在工程层面。灯光散落在各处时,"氛围"是不可控变量——谁也说不清改了主光会不会毁掉射灯;集中登记后,氛围变成可版本管理的数据。另外注意夜间方案里可见性的用法:白天直接关掉聚光灯而不是把强度调成 0,因为强度为零的灯仍会走进渲染管线,visible 关闭才真正省下开销——调光调到后期,性能与美术开始共用同一份参数表。
变式:给模式切换加过渡——每帧向目标强度插值(第 6 章 6.2 的缓动手法),灯光渐变而不是硬切,"黄昏逼近"的效果免费获得。进阶变式:随系统时间自动选模式,早上打开页面是理货模式,晚上自动切巡检——灯光配置表配上一个简单的时间函数,场景就有了"作息"。
点光源与聚光灯的衰减由 decay 参数控制,物理模式默认为 2(平方反比),这是真实世界的规律:距离翻倍,亮度跌到四分之一。它带来的直接推论是——强度数值必须和照射距离配套标定。一个实用的标定流程:先把灯放在目标位置,把 decay 保持 2,从强度 10 起步成倍上调,直到目标物件达到想要的亮度;如果强度拉到几百才亮,说明灯离得太远,把灯挪近一半,强度立刻省四分之三。
| 调整动作 | 视觉效果 | 性能代价 |
|---|---|---|
| 挪近灯光 | 亮度平方级上升 | 无变化 |
| 调大 intensity | 亮度线性上升 | 无变化 |
| 调小 decay | 远处更亮、更假 | 无变化 |
| 加灯数量 | 光源覆盖更广 | 每盏灯都是全体材质的着色开销 |
表里最后一行是本章埋给第 8 章的伏笔:Three.js 里每盏灯都会让场景中所有受光材质多算一遍光照,灯的数量是全局成本,不是局部成本。十个点光源的低端设备实测掉帧,往往比高面数模型还狠——能用一盏方向光解决的,就别用三盏点光硬堆。
旋钮已校准,还差最后一道手续。下一节上阴影:为什么开了 castShadow 影子还是不出勤,四道手续与一台阴影相机如何逐一过关。