【DX12龙书】第八章 光照
本章是 Direct3D 12 实时渲染的光照核心章节,完整讲解了基于物理近似的局部光照模型,从光与材质交互的底层物理原理、法线向量的核心作用,到环境光 / 漫反射 / 高光反射的数学模型,再到平行光、点光源、聚光灯三种主流光源的工程实现,最终落地到 D3D12 的 C++ 材质系统、HLSL 着色器代码、可交互光照 Demo,完整构建了实时渲染的光照管线。核心目标是通过光照计算,让 2D 屏幕上的三角形网格呈现出真实的 3D 体积感、材质质感与光影层次。
各小节内容概况
8.1 光照与材质的交互
本节是光照基础入门,通过有无光照的球体对比,明确光照是塑造 3D 物体体积感的核心;讲解了光与材质交互的物理本质,区分了实时渲染采用的局部光照模型与离线渲染的全局光照模型。
8.2 法向量
计算法向量
三角形面法线计算:先计算三角形两条边的向量 u、v,通过叉乘得到垂直于平面的向量,再归一化得到单位面法线,公式:n = normalize(u × v)。
顶点法线平均算法:对于网格中的任意顶点,将共享该顶点的所有三角形的面法线求和,再归一化,得到该顶点的近似法线;可通过三角形面积加权平均,提升法线精度。
变换法向量
普通的模型变换矩阵如果包含非等比缩放 / 剪切变换,会破坏法线与切线的正交性,导致法线方向错误,光照计算失真。
法线的正确变换矩阵:必须使用模型变换矩阵的逆转置矩阵,即NormalMatrix = (A⁻¹)ᵀ,其中 A 为模型的变换矩阵。
-
变换法线时需剔除矩阵中的平移分量(法线是向量,不受平移影响);
-
法线变换后必须重新归一化,避免缩放导致的长度变化。
-
特殊情况:若变换矩阵为正交矩阵(如纯旋转),逆转置矩阵等于原矩阵,可直接用原矩阵变换法线,无需额外计算。
8.3 光照中的关键向量
介绍了光照计算中必须用到的向量。

-
表面法线
n:描述表面点的朝向; -
光线入射方向
I:光子从光源到表面点的运动方向; -
光线向量
L:与I方向相反,从表面点指向光源,用于 Lambert 余弦定律计算; -
观察向量
v(to-eye 向量):从表面点指向相机 / 眼睛,描述视线方向; -
反射向量
r:入射光关于表面法线的对称反射方向; -
半角向量
h:光线向量L与观察向量v的角平分线方向,用于高光计算。 -
反射向量数学公式:
r = I - 2(n·I)n,HLSL 中可直接使用reflect内置函数计算。
上述所有向量均需为单位向量。
8.4 LAMBERT余弦定律
讲解了光照的核心物理定律 —— 朗伯余弦定律。
-
辐射通量:光源每秒发射的光能量;
-
辐照度:单位面积接收的辐射通量,直接决定表面的亮度。
-
朗伯余弦定律核心:表面接收的辐照度,与光线向量
L和表面法线n夹角的余弦值成正比。数学公式:$$E₂ = E₁·cosθ = E₁·max(n·L, 0)$$,其中
θ为L与n的夹角。
8.5 漫反射光照
漫反射的物理本质:光线射入物体表面内部,与材质发生散射、吸收后,剩余能量从入射点均匀向所有方向反射出去。所以其效果与观察视角无关。
-
漫反射反照率(Diffuse Albedo):描述表面对不同颜色光的反射比例,取值范围
[0,1],符合能量守恒(未反射的光被材质吸收)。 -
漫反射公式:$$c_d = max(L·n, 0) · B_L ⊗ m_d$$,其中\(B_L\)为入射光颜色强度,\(m_d\)为漫反射反照率,
⊗为分量级颜色乘法。
8.6 环境光照
环境光是模拟真实世界中经过多次反射、均匀分布在场景中的间接光,避免局部光照模型中物体背光面完全纯黑的不真实效果。
计算公式:$$c_a = A_L ⊗ m_d$$,其中\(A_L\)为环境光颜色强度,\(m_d\)为漫反射反照率。
8.7 镜面光照
光照物体,观察者看到的时,入射光引起漫反射和菲涅耳效应。菲涅耳效应又会分为镜面反射和折射

Fresnel效应
-
光线入射到介质交界面时,反射光的比例由材质的折射率、光线入射角共同决定;
入射光线被反射的百分比,0≤\(R_F\)≤1,能量守恒,光的\(R_F\)被反射,1-\(R_F\)被折射
入射角越大(视线与反射方向越接近),反射比例越高,当入射角接近 90° 时,几乎所有光都被反射。【真实案例】低头看脚下的池水,能看到池底(反射弱、折射强);看向远处的水面,只能看到天空的倒影(反射强、折射弱)。 -
Schlick 近似公式:$$R_F(θ_i) = R_F(0°) + (1 - R_F(0°))(1 - cosθ_i)^5$$,其中\(R_F(0°)\)是光线垂直入射时的反射率,是材质的固有属性。
-
常见材质\(R_F(0°)\)基准值:水 (0.02)、玻璃 (0.08)、塑料 (0.05)、金 (1.0,0.71,0.29)、银 (0.95,0.93,0.88);金属的反射率远高于非金属,且无漫反射(折射光被金属完全吸收)。
表面粗糙度
微表面模型:宏观上平整的表面,在微观尺度上由无数微小的镜面(微面元)组成,每个微面元有独立的法线(微观表面法线);入射光线经过这些微表面的反射,反射出来的事一个镜面瓣;粗糙度越高,微表面法线与宏观表面法线的偏差越大,反射光越分散,高光范围越大、亮度越低。

-
半角向量
h的作用:只有微法线与半角向量h一致的微面元,才能将光线反射到相机中,因此高光计算的核心是统计符合条件的微面元比例。 -
粗糙度数学建模:使用幂函数\(ρ(θ_h)=cos^m(θ_h)=(n·h)^m\)描述微面元法线的分布,其中\(θ_h\)是半角向量
h与宏观法线n的夹角,m为高光指数;m越大,表面越光滑,高光越集中、越亮;m越小,表面越粗糙,高光越分散、越暗——m控制了镜面瓣的宽窄。 -
能量守恒归一化:加入归一化因子\(\frac{(m+8)}{8}\),保证无论高光范围如何变化,反射的总光能量守恒,避免粗糙度调整时出现能量异常。
-
完整高光公式:\(c_s = max(L·n, 0) · B_L ⊗ R_F(α_h) · \frac{(m+8)}{8} · (n·h)^m\),其中\(α_h\)是光线向量
L与半角向量h的夹角。
8.8 光照模型的概述
将环境光、漫反射、高光反射三部分整合,给出了本章最终使用的完整光照方程:
最终反射到相机的光强 = 环境光 + 漫反射 + 高光反射
8.9 材质的实现
结合现有渲染管线框架,实现了一个简单的材质系统。

-
C++类
Material结构体核心成员:材质名称、常量缓冲区索引、纹理 SRV 堆索引、脏标记NumFramesDirty;着色器核心参数:DiffuseAlbedo(漫反射反照率)、FresnelR0(垂直入射菲涅尔反射率)、Roughness(粗糙度,归一化[0,1]范围)、材质变换矩阵。-
粗糙度归一化到
[0,1],0 为绝对光滑,1 为绝对粗糙,光泽度 = 1 - 粗糙度; -
金属材质的
DiffuseAlbedo应设为 0(折射光被完全吸收),可根据艺术效果微调为低值; -
材质参数需基于真实物理值,可按需艺术化调整。
-
-
GPU 端材质常量缓冲区
MaterialConstants结构体 -
多材质渲染方案:将网格按材质分为多个子集,每个子集使用对应的材质参数,执行单独的绘制调用。
8.10 平行光源
-
定义:模拟距离极远的光源,所有光线均为平行状态,仅需定义光线方向,无需定义光源位置。
-
核心特性:无距离衰减,光线强度不随传播距离变化;光线方向全局统一,不随表面位置变化。
8.11 点光源
-
定义:从空间中一个点向所有方向均匀发射光线的光源。
-
核心特性:光线方向随表面位置变化,需逐像素计算;光强随距离增加而衰减,超出最大范围后光强为 0。
-
光线向量计算:
L = normalize(Q - P),其中 Q 为点光源位置,P 为表面点位置。 -
衰减:
-
物理衰减规律:真实世界中光强随距离的平方反比衰减,\(I(d) = \frac{I_0}{d^2}\),需要 HDR + ToneMapping 实现;
-
实时渲染线性衰减模型:\(att(d) = saturate(\frac{falloffEnd - d}{falloffEnd - falloffStart})\),其中
falloffStart为开始衰减的距离,falloffEnd为衰减到 0 的最大距离; -
着色器只计算在衰减范围内的点光源的光照,有助优化;
-
8.12 聚光灯光源
-
定义:从空间中一个点,沿指定方向向锥形范围内发射光线的光源。
-
核心特性:同时具备距离衰减与角度衰减,只有在锥形照射范围内的表面才能接收到光;锥形中心光强最高,向边缘逐渐衰减到 0。
-
角度衰减模型:\(k_{spot}(φ) = max(cosφ, 0)^s = max(-L·d, 0)^s\),其中 φ 是
-L与聚光灯方向d的夹角,s为聚光指数;s越大,锥形范围越小,光束越集中;s越小,锥形范围越大,光束越分散。 -
聚光灯完整光照计算:\(B_L⊗att(d)⊗k_{spot}(φ)\)。
8.13 光照的具体实现
在C++和HLSL,实现了Light的结构体定义、光照计算

8.14 光照DEMO
讲解了基于 D3D12 的可交互光照 Demo 的完整实现,基于前一章的水波 Demo 扩展,实现了带光照的地形与水波渲染,支持键盘控制太阳(平行光)的方向,实时查看光照效果变化。
-
新的顶点结构体:包含位置
Pos与法线Normal两个 float3 成员,对应 HLSL 的VertexIn结构体。 -
程序化地形法线计算:
地形函数\(f(x,z) = 0.3z·sin(0.1x)+0.3x·cos(0.1z)\)
偏导数\(\frac{∂f}{∂x} = 0.3z·cos(0.1x)+0.3x·cos(0.1z)\)
\(\frac{∂f}{∂z} = 0.3z·sin(0.1x)+0.3x·sin(0.1z)\)
叉乘得到表面法线,公式:\(n(x,z) = (\frac{∂f}{∂x}, 1, \frac{∂f}{∂z})\),计算后归一化。
水波法线计算:使用有限差分法近似计算偏导数,得到每个顶点的法线。 -
光源方向的球面坐标表示:用球坐标
(ρ,θ,φ)表示太阳的方向,ρ 固定为 1(单位向量),通过键盘调整 θ(方位角)和 φ(极角)。每帧更新流程:键盘输入修改球面坐标→转换为笛卡尔坐标得到光源方向→更新 Pass 常量缓冲区→拷贝到 GPU,实现光照实时变化。 -
为支持材质系统,修改了 D3D12 的根签名,新增了一个材质常量缓冲区的根描述符。
8.15 小结
-
光照渲染核心流程:定义场景光源与物体材质,通过光照方程,结合表面法线、相机位置,计算出每个像素的最终颜色,替代传统的顶点颜色指定。
-
法线核心要点:顶点法线的计算与平均、法线的逆转置矩阵变换、光栅化插值、逐像素归一化。
-
三种光源的核心特性与适用场景:平行光(太阳)、点光源(灯泡)、聚光灯(手电筒)。
-
光照三个核心分量:环境光(间接光近似)、漫反射(体反射,视角无关)、镜面反射(菲涅尔 + 粗糙度,视角相关)。
-
微表面模型与菲涅尔效应的物理意义与工程实现。
8.16 练习
-
练习 1:修改平行光为红色脉冲光,通过正弦函数控制光强随时间变化,实现氛围灯光效果。
-
练习 2:修改材质的粗糙度参数,观察高光效果的变化,理解粗糙度对材质质感的影响。
-
练习 3:实现影视常用的三点光照系统(主光、补光、背光),用三个平行光模拟,提升物体的轮廓与立体感。
-
练习 4:替换三点光照为点光源,为每个物体添加独立的点光源,掌握点光源的工程使用。
-
练习 5:替换为聚光灯,实现向下照射的射灯效果,掌握聚光灯的参数调整。
-
练习 6:实现卡通渲染(Toon Shading),将漫反射与高光值离散化,实现明暗硬过渡,掌握非真实感渲染的基础方法。
核心知识点列表
-
法线必须用逆转置矩阵进行空间变换,变换后必须重新归一化;
-
完整光照模型 = 环境光 + 漫反射 + 镜面反射,三者分别对应间接光、体反射、界面反射;
-
漫反射遵循朗伯余弦定律,与视角无关;高光反射由菲涅尔效应 + 微表面粗糙度共同决定,与视角强相关;
-
三种主流光源:平行光(无衰减、成本最低)、点光源(距离衰减)、聚光灯(距离 + 角度衰减、成本最高);
-
材质核心参数:漫反射反照率、菲涅尔 R0、粗糙度,决定了物体的最终质感;
-
C++ 与 HLSL 的结构体必须严格遵循内存打包规则,避免布局不匹配导致的渲染 bug;
-
实时渲染采用局部光照模型,通过环境光低成本近似间接光照效果;
-
逐像素光照比逐顶点光照精度更高,是当前实时渲染的主流方案。
难点笔记
光照这个题目,暂时没什么感悟。书中内容比较基础,模型也是比较古早,估计要跟PBR、前向/延迟渲染、Lumen联系才有更多想法。

浙公网安备 33010602011771号