【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 光照中的关键向量

介绍了光照计算中必须用到的向量。

第八章 光照-image

  • 表面法线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)$$,其中θLn的夹角。

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 镜面光照

光照物体,观察者看到的时,入射光引起漫反射和菲涅耳效应。菲涅耳效应又会分为镜面反射和折射

第八章 光照-diagram

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);金属的反射率远高于非金属,且无漫反射(折射光被金属完全吸收)。

表面粗糙度

微表面模型:宏观上平整的表面,在微观尺度上由无数微小的镜面(微面元)组成,每个微面元有独立的法线(微观表面法线);入射光线经过这些微表面的反射,反射出来的事一个镜面瓣;粗糙度越高,微表面法线与宏观表面法线的偏差越大,反射光越分散,高光范围越大、亮度越低。

第八章 光照-image-1

  • 半角向量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 光照模型的概述

将环境光、漫反射、高光反射三部分整合,给出了本章最终使用的完整光照方程:

最终反射到相机的光强 = 环境光 + 漫反射 + 高光反射

\[LitColor = c_a + c_d + c_s \]

\[= A_L⊗m_d + max(L·n,0)·B_L⊗(m_d + R_F(α_h)·\frac{(m+8)}{8}·(n·h)^m) \]

8.9 材质的实现

结合现有渲染管线框架,实现了一个简单的材质系统。

第八章 光照-diagram-1

  • 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的结构体定义、光照计算

第八章 光照-diagram-2

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),将漫反射与高光值离散化,实现明暗硬过渡,掌握非真实感渲染的基础方法。


核心知识点列表

  1. 法线必须用逆转置矩阵进行空间变换,变换后必须重新归一化;

  2. 完整光照模型 = 环境光 + 漫反射 + 镜面反射,三者分别对应间接光、体反射、界面反射;

  3. 漫反射遵循朗伯余弦定律,与视角无关;高光反射由菲涅尔效应 + 微表面粗糙度共同决定,与视角强相关;

  4. 三种主流光源:平行光(无衰减、成本最低)、点光源(距离衰减)、聚光灯(距离 + 角度衰减、成本最高);

  5. 材质核心参数:漫反射反照率、菲涅尔 R0、粗糙度,决定了物体的最终质感;

  6. C++ 与 HLSL 的结构体必须严格遵循内存打包规则,避免布局不匹配导致的渲染 bug;

  7. 实时渲染采用局部光照模型,通过环境光低成本近似间接光照效果;

  8. 逐像素光照比逐顶点光照精度更高,是当前实时渲染的主流方案。


难点笔记

光照这个题目,暂时没什么感悟。书中内容比较基础,模型也是比较古早,估计要跟PBR、前向/延迟渲染、Lumen联系才有更多想法。

posted @ 2026-04-22 22:49  斌伯  阅读(8)  评论(0)    收藏  举报