着色模型与光源
Shading Models & Light Sources
着色模型描述物体颜色如何随表面朝向、观察方向和光照变化。光源通过两个参数与着色模型交互:指向光源的方向 和光的颜色 。方向光、点光源、聚光灯的区别就在于这两个量如何随场景变化;距离衰减和方向衰减函数的选择同时影响画面与性能。
示例:Gooch 着色
RTR4 用 Gooch 模型的变体举例:法线朝向光源用暖色、背向用冷色,中间插值,再加一个风格化高光。 其中 ,,,,(夹到 [0,1])。
它展示了着色中最常见的操作:
- 单位向量点积(即夹角余弦)衡量两个方向的对齐程度;
- 夹取 (clamp),尤其是到 [0,1];
- 线性插值 lerp/mix;
- 反射 reflect。
带光源的一般结构
- 的来源:光线打到表面的密度与入射角余弦成正比;从背面来的光没有作用,所以夹到 0。这也是把 定义为指向光源(与光传播方向相反)的原因。
- 这个结构是物理着色模型所必需的,对风格化模型也有助于光照一致性;不适合它的模型使用不带余弦项的形式。
- 描述不受光源照射时的外观:可以是纯黑,可以是风格化的颜色,通常表示不是来自显式光源的光(天光、反弹光,见第 10、11 章)。
- 最简单的 就是 Lambert 着色模型,适用于理想漫反射的哑光表面。
光源类型
| 类型 | ||
|---|---|---|
| 方向光 | 全场景恒定 | 恒定(可被阴影衰减);可用两个嵌套盒子限定作用区域并平滑过渡 |
| 点光源 (omni) | 随距离衰减 | |
| 聚光灯 | 同上 | 距离衰减 × 方向衰减 |
- 方向光适合光源距离远大于场景尺寸的情况,例如太阳。
- 点光源和聚光灯统称精确光源 (punctual light):有位置、无尺寸。
- 计算 时显式算出距离 ,后面衰减要用。
距离衰减
平方反比:。有两个实际问题:
近处奇点
- 分母加 :,虚幻引擎用 cm。
- 或夹取最小距离 (CryEngine、Frostbite)。 有物理含义:发光体的半径。
远处永不为 0:为了高效渲染(第 20 章),光应在有限距离降到 0,且最好数值和导数同时为 0,避免硬边。虚幻与 Frostbite 使用窗口函数:
- 当衰减以较低空间频率采样(光照贴图、逐顶点光照)时, 处导数为 0 尤其重要;CryEngine 不用这两者,所以只在 到 之间改为线性衰减。
- 也可以用非平方反比的衰减函数 :《Just Cause 2》为了极低开销用 ;虚幻引擎另有可调的指数衰减模式;《古墓丽影》(2013) 用样条编辑衰减曲线。
聚光灯方向衰减
为聚光方向 与 的夹角。本影角 之外为 0(也可用于剔除),半影角 之内为满强度:
其他光源
- 可以是任意方向分布,包括实测的 IES 配光文件(《Killzone: Shadow Fall》、虚幻、Frostbite 使用)。
- 《古墓丽影》有沿世界 x、y、z 轴分别衰减的精确光源,以及随时间变化强度的曲线(闪烁火把),还有以线段为光源的胶囊光:取线段上最近点的方向作为 。
- 只要着色器拿到 和 ,任何计算方式都可以。
- 真实光源有尺寸,称为面光源;其技术分为软化阴影(7.1.2 节)和模拟对表面着色的影响(10.1 节,光滑表面上最明显)。
GEA 的视角:面光源与理想化光源(卷 II 11.3.7)
- 现实中的光源分发光表面(太阳、灯泡、发热的炉丝)和发光介质(火焰:只有足够热的区域才发光)。渲染中主要关心发光表面;烛火、爆炸这类发光介质通常可以近似为被一个假想发光表面包住的体积。表面上每一点都发光的光源叫面光源(卷 II 11.3.7,见 面光源)。
原书问题(11.3.7):GEA 说火焰"由空气混合水蒸气(足够热时还有等离子体)组成"。烛火等扩散火焰的可见光主要来自燃烧不完全产生的炽热烟炱 (soot) 颗粒的黑体式热辐射(黄色部分),蓝色底部则来自 CH、C₂ 等自由基的化学发光;水蒸气和 CO₂ 是燃烧产物,主要辐射在红外(Wikipedia "Flame")。这不影响"把火焰当作发光介质、用假想发光表面包住近似"的结论。
- 面光源在光线追踪引擎里很好处理,它们只是恰好向场景注入能量的普通表面;但在三角形光栅化等方法中较棘手,因此常用理想化模型:灯泡、LED 这样的小光源近似为点光源(全向光),从一点向各方向辐射;太阳这样的巨大光源近似为方向光,光子充满整个空间、沿同一方向平行传播。
- 理想化光源的缺点是阴影不真实:现实中光源有面积,才形成有本影和半影的软阴影;理想化光源只产生硬阴影,除非另外模糊边缘(见 软阴影 PCF 与 PCSS)。光源模型的细节 GEA 留到卷 II 12.4.2。
- 这与本页「光源类型」中 RTR4 的分类一致;本页的聚光灯和衰减函数 GEA 第 11 章没有涉及。
GEA 的视角:理想化光源模型(卷 II 12.4.2)
- 理想化光源有两种基本类型:方向光是尺寸相对场景无限大的面光源,所有光线平行,常用来模拟太阳;点光源是尺寸为零的面光源,光线从一点向外辐射,常用来模拟小灯泡和 LED(卷 II 12.4.2)。
- 每个理想光源由位置 、单位方向 和照度 描述;方向光可以看成位置在无穷远。 约定背离表面,与光子流方向相反:。 是该光源在垂直于光线的白色理想漫反射面上产生的照度,渲染程序员说的"光源颜色"就是它。
- 太阳其实是比地球大得多的面光源,表面光子向各方向飞出,但太阳太远,只有几乎平行于两天体连线的光子能到达地面,所以在地球上它的通量密度场表现得像无穷远处的无穷小点光源;换个角度说,太阳在天空中所张的立体角很小。
- 方向光: 与 在空间中处处为常数(卷 II 12.4.2.1)。
- 点光源:,照度按平方反比衰减。平方反比衰减很快,超过某个半径 后几乎为零,这个范围叫影响球;多数引擎只对影响球内的表面应用该光源,这是重要的优化(卷 II 12.4.2.2)。
- 聚光灯:限制在锥形区域内的点光源,通常有内外两个锥角:内锥内满强度,内外锥之间逐渐衰减,外锥外为零。由位置、随距离衰减的照度、中心方向、影响半径 和内外锥角 、 描述(卷 II 12.4.2.3)。本页上文 RTR4 的聚光灯与衰减函数在这里得到对应。
- 着色方程里统一用 表示照度,假设点光源和聚光灯的距离衰减已经事先算好(卷 II 12.4.2.4)。GEA 第 12.5.1.3 节示例着色器中的点光源实际用
smoothstep(outerRadius, innerRadius, distance)做距离衰减,而不是平方反比(见 着色方程与经典光照模型)。 - 原书问题(书中前后不一致):12.2.4.5 推导点光源通量密度为 ,12.4.2.2 写点光源照度却是 ,少了 。如果把 理解为发光强度(每球面度的光通量), 才是正确的照度;按书中"光通量"的说法应除以 。本页上文 RTR4 用参考距离 处的光源颜色写成 ,不涉及功率与 的换算。各向同性点光源 见 Wikipedia "Inverse-square law"(光强与照度一节)及 RTR4 第 5.2.2 节。
PBRT 的视角:物理光源接口(PBRT 4e 12.1–12.3)
为什么没有"艺术控制"
pbrt 不提供可调衰减速率、只照亮某些物体、不投射阴影等非物理控制,因为它们与基于物理的光传输不相容。书中的反例:不投影的光源周围套一层层同心球壳,忽略遮挡时每加一层,表面接收的总能量就增加,最终超过光源发出的总能量。这与上文 RTR4 记录的游戏做法(窗口函数截断、 夹取、《Just Cause 2》的非平方反比衰减)构成分歧:游戏为了有限影响范围和性能主动放弃能量守恒,离线物理渲染器则保留严格的 。
接口与类型
- 每个光源要能返回总功率 (供按功率选光源);
Type()分四类:DeltaPosition(点)、DeltaDirection(单一方向)、Area(附着在形状上)、Infinite(无几何、给逃逸光线提供亮度)。积分器需要知道哪些是 delta 光源。 SampleLi(ctx, u, λ, allowIncompletePDF)返回入射亮度、方向 、发光点和相对接收点立体角的 PDF;不包含遮挡和介质衰减。delta 光源返回 pdf = 1(亮度与 PDF 中隐含的 delta 相消),PDF_Li()对它们返回 0。L()给面光源、Le()给无穷远光源。allowIncompletePDF允许在贡献小的方向不生成样本(MIS 补偿,Karlík 等 2019)。- 光源不支持动画变换(练习 12.3)。
LightBase用InternCache对相同的DenselySampledSpectrum(覆盖可见光约 2 KB)去重,百万光源时可省大量内存。 - 光度学输入:场景文件可以用光通量(如 1000 lm 的灯泡)指定光源,解析时转换为辐射度量值;
Phi()返回的是辐射功率。
各类点光源
- 点光源:强度 ,。严格说点光源到达的是强度除以 而不是辐射亮度,pbrt 有意"滥用"术语,让所有光源共用一套接口。
- 聚光灯:位于原点朝 ,存两个角的余弦;方向衰减
SmoothStep(cosθ, cosFalloffEnd, cosFalloffStart)(三次 Hermite),与上文 three.js 的 smoothstep 写法一致,而 Frostbite 用 。功率 :代换 后衰减带积分为 ,与书一致。Warn 1983 最早提出这种聚光灯模型。 - 投影光 (
ProjectionLight):像幻灯机,用透视投影把点投到 平面;丢弃 的点(投影变换会把背后的点翻到前面);图像用最近邻而非双线性,保证与重要性采样的分段常数分布严格一致;RGB 用RGBIlluminantSpectrum转光谱,使 对应色彩空间的光源白而非常数光谱。功率在投影平面上积分,换元因子 ( 乘以 ,平面上 )。 - 配光曲线光源 (
GoniometricLight):照明工程常用的测角光度图(IES 文件 LM-63 即此类),用等面积八面体参数化存成图像,只用第一通道缩放强度;等面积映射使每个像素立体角相同,换元因子为常数 。照明工程的经验:距离超过面光源半径 5 倍时可当点光源,见 面光源 中点光源近似误差的讨论。更一般的近场光度学用 4D 光场(Ashdown 1993、Heidrich 等 1998)。 - 方向光 (
DistantLight):;只能处于真空(在吸收介质中的无穷远光源会被全部吸收)。阴影光线终点取沿入射方向 2 倍场景包围球半径处,必在场景外。功率与受照面积成正比,用场景包围球内接圆盘近似:,这是投影面积的上界。
原书问题(12.2.3,措辞)
书中说测角光度图描述点光源出射的"luminance"(亮度)的角分布。点光源的方向分布是发光强度 (luminous intensity, cd),不是亮度 (luminance, cd/m²);pbrt 实现中图像缩放的也正是强度 I()。IES LM-63 文件存的同样是坎德拉值。
LearnOpenGL 的视角:方向光 / 点光衰减 / 聚光灯锥体(Lighting / Light casters · Multiple lights)
来源:Light casters、Multiple lights;摘录 Lighting — Light casters、Lighting — Multiple lights。
方向光
用方向向量代替位置;着色里 lightDir = normalize(-light.direction)(约定 CPU 侧存「从光源指出」的方向)。旧式定点管线用 vec4 的 w==0 表示方向光、w==1 表示点光。
点光与衰减
衰减常用 。 常为 1,避免分母小于 1 时反而放大。教程转载 Ogre3D 表,按覆盖半径给 、 初值(如半径 50 → 0.09 / 0.032)。环境项是否乘衰减:单灯可保留环境,多灯叠加时通常也衰减,以免环境光堆爆。
聚光灯与手电
参数:位置、方向、切角 。比较的是 余弦(dot(lightDir, -spotDir))与预计算的 cos(cutOff),避免着色器里 acos。硬边用 theta > cutOff;软边引入内外角,强度 ,只乘漫反射与镜面,环境可保留。手电 = 聚光灯位置/方向每帧跟相机。
多光源结构
main 里累加:result = CalcDirLight(...) + Σ CalcPointLight(...) + CalcSpotLight(...)。GLSL 函数与 #define NR_POINT_LIGHTS 数组;CPU 用 pointLights[i].constant 这种带下标的 uniform 名赋值。重复的反射向量/贴图采样有优化空间,但是教学结构清晰。氛围主要靠调各灯 ambient/diffuse/specular,而非改模型。
出处
- realtime_rendering_4ed.pdf 第 5 章 Shading Basics
- Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 11 章 11.3.7 节
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 12 章 12.4.2 节
- PBRT 4e source excerpt: chapter 12 (Light Sources)
- Pharr, Jakob, Humphreys《Physically Based Rendering: From Theory to Implementation》第 4 版(MIT Press, 2023)第 12 章 12.1–12.3 节、延伸阅读,pbr-book.org/4ed/Light_Sources
- Lighting — Light casters
- Lighting — Multiple lights
- learnopengl.com/Lighting/Light-casters
- learnopengl.com/Lighting/Multiple-lights