中EN
概念页

阴影贴图

Shadow Mapping

更新于 2026-10-15 约 11 分钟阴影阴影贴图深度偏移阴影痤疮

阴影贴图 (Williams, 1978) 先从光源位置渲染深度,再从相机渲染时把每个点到光源的距离与贴图中存的深度比较:更远就在阴影中。它开销可预测(生成约与图元数成线性、查询为常数时间),非常适合 GPU,是任意表面阴影最常用的算法。主要问题是分辨率、深度精度带来的混叠、阴影痤疮和漏光。

基本做法

  • 第一遍只需 z-buffer,关闭光照、纹理和颜色写入;存下的深度即阴影贴图(也叫阴影深度图、阴影缓冲)。第二遍从相机渲染,用纹理映射比较。
  • 光源和物体都不动时(如 CAD)可生成一次每帧复用。
  • 方向光(太阳)用正交投影,宽高要覆盖所有向视景体投影的物体;聚光灯有天然视锥;被遮挡体包围的局部点光源用六面立方体,称为全向阴影贴图,难点在两张图接缝处的伪影。Crytek 根据每个面视锥在屏幕上的覆盖决定其分辨率,全部存入图集。

缩小光源视锥

  • 只渲染能投影的物体(只接收阴影的地面不必渲染)。
  • 根据可见接收体收紧光源视锥:超出可见接收体最远距离的物体不可能投影到它们上;x、y 范围也可缩到可见接收体。这既省渲染,又让阴影贴图有效分辨率更高。近平面尽量远、远平面尽量近,以提高深度精度。

阴影痤疮与偏移

  • 自阴影混叠(surface acne / shadow acne):三角形被错误判定为遮挡自己。原因一是数值精度,二是几何上用一个点样本代表一块区域的深度,光源样本位置与屏幕样本几乎从不重合。
  • 常数偏移:比较时从接收体距离中减去一个小偏移;表面不正对光源时会失效。
  • 斜率缩放偏移:偏移与表面相对光源的倾角成正比,可用 glPolygonOffset 之类的命令实现。正对光源时斜率偏移为 0,所以要和常数偏移一起用;接近掠射时正切值极大,通常夹取上限。
  • 法线偏移 (Holbert):把接收体世界位置沿法线移动,与光方向和几何法线夹角的正弦成正比,同时改变比较的深度和贴图坐标,相当于移到表面上方的"虚拟表面"。Pettineo 建议按阴影贴图深度范围缩放;也可沿相机视线方向偏移。
  • 偏移过大导致漏光 / Peter Panning:物体像浮在表面上,例如脚下地面不再有阴影。
  • 第二深度阴影贴图:只把背面画入阴影贴图,适合不能手调偏移的系统;双面薄物体(棕榈叶、纸)、轮廓附近、物体接触处仍有问题,且更容易漏光。Sousa 等发现太阳用正面、室内光用背面效果最好。
  • 物体必须"水密"(流形且封闭),否则要正反面都画入,才能完整投影。双阴影贴图:记录离光源最近的两个表面,取其中点深度,物体足够厚时痤疮和漏光都很少,但要两张阴影贴图的开销。

稳定性

光源视体随相机变化会导致阴影逐帧轻微抖动。方向光的解决方法:强制每张阴影贴图在世界空间保持相同的纹素"光束"位置,相当于给整个世界施加一个固定的二维网格,光源投影被对齐到这个网格。

分辨率问题

  • 理想情况下一个阴影纹素约覆盖一个屏幕像素。近处许多像素对应同一纹素称为透视混叠;表面几乎平行于光线但正对观察者时称为投影混叠。提高分辨率可以缓解,但耗内存和处理时间。
  • 透视变形方法(PSM、TSM、LiSPSM):通过改变光源视角方向和投影窗口,让近处获得更高采样率,只需修改光源矩阵。光照方向与视线垂直(如头顶光)时效果最好;但光源在相机前方并对着相机("对决视锥"、"车灯前的鹿")时线性变形只会更糟,加上质量突变、相机移动时阴影"神经质"不稳定,这类方法已不再受青睐。
  • 多张阴影贴图的思路由此发展为级联阴影贴图。

不规则 z-buffer (IZB)

  • 阴影贴图的离散采样导致相机看到的位置与光源采样位置不一一对应,产生混叠和偏移问题。IZB 反过来:先从相机渲染得到可见点,把它们变换到光源视图并存入对应纹素的列表;再对遮挡体做保守光栅化,在像素着色器中测试每个点是否被三角形挡住(相当于从点向光源射线追踪,也称视锥追踪)。
  • 没有采样和偏移问题,得到完美锐利的阴影。实现要仔细:Wyman 等的最终版本比原型快两个数量级(剔除背光点、不为空纹素生成片元、让每个纹素列表短而长度相近以减少线程分歧);配合级联可缩短近处的列表。
  • 《The Division》按遮挡体距离混合 IZB 硬阴影与 PCSS 软阴影,近处用硬阴影,且只对选定的物体子集使用 IZB 以降低开销。

其他应用

  • 地形遮挡高度(CD Projekt):为太阳渲染阴影贴图后,对每个地形位置沿竖直方向固定步长上升直到看见太阳,再二分搜索,得到"仍被遮挡的最大高度",插值后既能给地形也能给树木等物体投影(《巫师》)。
  • 屏幕空间阴影:阴影贴图分辨率有限,难以表现面部等小特征(比如不该发光的鼻孔)。把相机深度缓冲当作高度场,向光源方向光线步进检查遮挡;开销大,但在过场特写中值得,已被许多引擎采用。
  • 半透明物体可再生成一张存最近深度和颜色/覆盖率的阴影贴图,用于给其后的接收体染色(不能用于半透明物体自身)。头发、烟雾等需要深度阴影贴图(每纹素存随深度变化的透光函数)、不透明度阴影贴图或自适应体积阴影贴图(像素着色器中在线有损压缩若干个不透明度/深度对,需要 UAV 和 ROV;《GRID2》中烟雾平均开销低于 2 ms/帧)。
  • 稀疏纹理可用于阴影贴图存储以提高分辨率;也有把阴影转为稀疏体素八叉树、用体素锥追踪生成面光源软阴影的方法。

总结(书中结论)

技术用途
阴影贴图任意表面阴影的最常用方法
级联阴影贴图 + SDSM大范围(户外)采样质量与精度
PCF一些柔化
PCSS 及变体接触硬化
IZB精确硬阴影
滤波阴影贴图快速软阴影,遮挡体远离接收体时(如地形)尤佳
屏幕空间阴影额外精度,代价明显

GEA 的视角:阴影贴图(卷 II 12.5.6)

  • 阴影贴图本质上是从光源视角而不是相机视角做的逐片元深度测试。分两步:先从光源视角渲染场景并保存深度缓冲,得到阴影贴图;再正常渲染场景,用它判断每个片元是否在阴影中(卷 II 12.5.6)。
  • 阴影贴图只存深度,所以通常用硬件的双倍速纯 z 模式渲染。点光源用透视投影,方向光用正交投影。注意单个透视视锥覆盖不了点光源的全方向(视场角须小于 180°),全向点光源实际要渲染 6 个面的立方体阴影贴图(或双抛物面贴图),GEA 的描述只适用于聚光灯或只朝一侧投影的情况(见本页上文 RTR4 对全向光源的讨论)。
  • 正常渲染时,为每个顶点算出光空间坐标(生成阴影贴图时的那个"观察空间"),在三角形上插值得到片元的光空间位置;把光空间 (x,y)(x,y) 转成阴影贴图的 (u,v)(u,v),比较片元的光空间 zz 与该纹素存的深度:更远就说明被更近的几何挡住,在阴影中。
  • 这与本页「基本做法」一致。GEA 没有讨论阴影痤疮与偏移、级联、稳定性等实践问题,这些见本页上文与 级联阴影贴图、软阴影 PCF 与 PCSS。

LearnOpenGL 的视角:深度贴图两遍流程与 acne / peter panning / PCF(Advanced Lighting / Shadow Mapping)

来源:Shadow Mapping;摘录 Advanced Lighting — Shadow Mapping。与上文 RTR/GEA 对照;此处补 OpenGL 落地。

两遍

  1. 从光源视角渲染,只写深度:FBO 附 GL_DEPTH_COMPONENT 纹理,glDrawBuffer/glReadBuffer(GL_NONE);方向光用正交投影 + lookAt 得到 lightSpaceMatrix;简单 VS 做 lightSpace * model * pos,空 FS 即可。
  2. 正常渲染:顶点输出 FragPosLightSpace;片元里透视除法 → NDC→[0,1] → 采 closestDepth,与 currentDepth=proj.z 比较。环境项通常不乘阴影,漫反射/镜面乘 (1-shadow)。

伪影与修复

  • Shadow acne:多片元采同一倾斜 depth texel。用 bias,可按 max(0.05*(1-dot(N,L)), 0.005) 随夹角变。
  • Peter panning:bias 过大阴影与物体脱开。深度 pass 对实心物体可 glCullFace(GL_FRONT)(用法线背面深度);单面地板不适用。
  • 视锥外过采样:CLAMP_TO_BORDER 边框深度 1.0;proj.z>1 强制非阴影。
  • PCF:邻域多点比较再平均(教程 3×3);textureSize 得 texel。更高级软阴影见 软阴影 PCF 与 PCSS。

方向光→正交;聚光/点光→透视。透视深度可视化需先线性化,比较本身用相对深度即可。

LearnOpenGL 的视角:全向阴影与深度立方体贴图(Advanced Lighting / Point Shadows)

来源:Point Shadows;摘录 Advanced Lighting — Point Shadows。

点光需六面深度。做法:GL_TEXTURE_CUBE_MAP 六张 DEPTH_COMPONENT;glFramebufferTexture 一次挂整立方体。六份 perspective(90°) * lookAt(±X/±Y/±Z)。几何着色器用 gl_Layer 把每个三角形翻成 6 个,单 pass 填满立方体(性能未必优于六次 draw,需 profiling)。

深度存为线性距离/far_plane(片元写 gl_FragDepth)。光照 pass:fragToLight = fragPos-lightPos 当方向采 samplerCube;closestDepth*=far;currentDepth=length(fragToLight);再加 bias。

立方体 PCF:可在方向上加 3D 偏移(样例多、重),或用约 20 个大致分离的偏移方向 × diskRadius;半径可随视距变软。链 软阴影 PCF 与 PCSS、曲面细分与几何着色器(gl_Layer)。

GPU Gems 的视角:全向阴影贴图(GPU Gems 1 第 12 章)

来源:Philipp S. Gerasimov《Omnidirectional Shadow Mapping》;摘录 Chapter 12. Omnidirectional Shadow Mapping — Philipp S. Gerasimov。

点光源用立方体贴图(或双抛物面两遍)存深度;相对模板阴影体更吃动画/细分网格,可做滤波软边。双抛物面省 pass、立方体质量更稳。Gems2 STALKER 实践里常把全向拆成六盏聚光以便过滤打包。

GPU Gems 的视角:透视阴影贴图的养护(GPU Gems 1 第 14 章)

来源:Simon Kozlov《Perspective Shadow Maps: Care and Feeding》;摘录 Chapter 14. Perspective Shadow Maps: Care and Feeding — Simon Kozlov。

在后投影空间做阴影图,让近处物体分到更多 texel,对抗大场景锯齿。原文三坑:灯在相机后方时的「虚拟相机」损害质量;质量强烈依赖灯光在相机空间的位置;texel 面积非均匀导致 bias 不能再是常数。本章给方向光为主的实用修补与过滤建议,是通向 LiSPSM / 级联阴影贴图(Gems3 PSSM)的历史一环。

  1. realtime_rendering_4ed.pdf 第 7 章 Shadows
  2. Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
  3. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 12 章 12.5.6 节
  4. Advanced Lighting — Shadow Mapping
  5. learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping
  6. Advanced Lighting — Point Shadows
  7. learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows
  8. Chapter 12. Omnidirectional Shadow Mapping — Philipp S. Gerasimov
  9. Chapter 14. Perspective Shadow Maps: Care and Feeding — Simon Kozlov
  10. developer.nvidia.com/gpugems/gpugems/part-ii-lighting-and-shadows/chapter-12-omnidirectional-shadow-mapping
  11. developer.nvidia.com/gpugems/gpugems/part-ii-lighting-and-shadows/chapter-14-perspective-shadow-maps-care-and-feeding

相关页面

图形学阴影基础与阴影体Shadow Basics & Shadow Volumes阴影帮助玩家判断物体的位置。精确光源只产生硬阴影,面光源产生带本影和半影的软阴影。最简单的情况是投射到平面上的投影阴影;投影纹理可把阴影扩展到曲面;阴影体用模板缓冲计数,能在任意表面得到精确硬阴影,但开销极不稳定,如今很少使用。图形学级联阴影贴图Cascaded Shadow Maps级联阴影贴图 (CSM) 把视锥沿视线方向切成几段,每段用一张紧贴它的阴影贴图,使近处获得更多阴影纹素。它实现简单、能覆盖巨大场景、没有严重的最坏情况,因此被大量应用。关键问题是深度如何划分、近平面放在哪里、怎样避免抖动和级联边界的质量突变,以及如何降低多张贴图的开销。图形学软阴影 PCF 与 PCSSSoft Shadows with PCF & PCSS百分比渐近过滤 (PCF) 在阴影贴图上取多个样本,分别与表面深度比较后混合结果,得到伪软阴影,但半影宽度恒定。PCSS 先搜索附近遮挡体的平均深度,再据此决定过滤宽度,从而在接触处硬、远处软(接触硬化)。图形学滤波阴影贴图Filtered Shadow Maps普通阴影贴图的深度不能直接过滤。滤波阴影贴图存的是深度的某种统计量(矩、指数),可以像普通纹理一样模糊、做 mipmap、用求和面积表,只取一个样本就能得到平滑的半影。代表方法有方差阴影贴图 (VSM)、卷积阴影贴图、指数阴影贴图 (ESM/EVSM) 和矩阴影贴图;共同难点是漏光。图形学深度缓冲Z-Buffer深度缓冲是几乎所有图形硬件用来解决可见性的算法:为每个像素存一个当前最近图元的深度值,新片元更近才写入颜色和深度。它简单、和图元数量成线性关系、允许大多数图元以任意顺序绘制,但不能直接处理半透明。游戏数学投影矩阵Projection Matrix投影矩阵把视景体变换到规范视体(OpenGL 为 [-1,1]^3,DirectX 的 z 为 [0,1]),在那里裁剪更高效。正交投影保持平行线平行、物体大小与距离无关;透视投影让远处变小,产生的深度与观察深度成反比,近平面位置和"反向 z"决定深度精度。图形学动态漫反射全局光照Dynamic Diffuse Global Illumination烘焙动辄数小时,拖慢美术迭代,几何在运行时变化或由用户创建时甚至无法预计算。动态漫反射 GI 方法无需预处理或预处理快到每帧可做,主要有:基于虚拟点光源的反射阴影贴图 (RSM)、光传播体积 (LPV)、体素锥追踪 (VXGI) 以及屏幕空间补充(RTR4 11.5.5–11.5.9 节)。图形学实时次表面散射Real-Time Subsurface Scattering蜡、皮肤、牛奶等高散射固体中,光在离入射点不远处重新射出;当偏移大于一个像素时就不能再用 BRDF 表示。实时方法从便宜的包裹光照、法线模糊、预积分皮肤,到纹理空间/屏幕空间扩散,再到针对大尺度散射的深度图技术(RTR4 14.6 节)。图形学大场景渲染与虚拟纹理Large Scenes & Virtual Texturing当场景数据远大于内存(如主机只有 8 GB,而游戏世界有数百 GB)时,需要按需把数据流送进内存。本页讲三类技术:虚拟纹理(把巨大的 mipmap 链切成瓦片,只让可见瓦片驻留在显存,通过页表/间接纹理寻址)、纹理转码(磁盘上用高压缩比的可变码率格式,加载时转成 GPU 块压缩格式),以及按平面瓦片组织资产的通用流送。这些方法几乎总是与剔除和 LOD 配合使用;地形部分见 地形渲染。图形学分簇着色Clustered Shading分簇着色把整个视锥(而不只是屏幕平面)划分为三维单元——簇 (cluster),按簇建立光源列表。与依赖场景几何深度的分块着色不同,簇的划分与几何无关,可只由视图和光源计算,因此对相机位置的性能波动更小、对深度不连续更稳健,并为不透明、透明、体积物体提供统一的光照方案。它同样可配合前向或延迟着色,并衍生出 z-binning、光源树等更省内存的变体。本页还汇总了各类多光源方案的对比。游戏开发 · UE5虚拟阴影贴图Virtual Shadow Maps虚拟阴影贴图 (Virtual Shadow Maps, VSM) 是 UE5 默认的阴影方法:概念上就是分辨率极高(虚拟 16k×16k)的阴影贴图,切成 128×128 的页 (Page),只为屏幕上需要着色的像素分配和渲染页,并跨帧缓存;方向光用 clipmap 覆盖大范围,软阴影由阴影贴图光线追踪 (Shadow Map Ray Tracing, SMRT) 生成。它为 Nanite 设计,与 Nanite 一起使用时最高效。图形学环境光遮蔽Ambient Occlusion环境光遮蔽 (AO) 是余弦加权后半球上未被遮挡的比例,用来在光照缺乏方向变化时仍给出形状线索(缝隙变暗)。它由 Landis 在 ILM 为《珍珠港》开发,可离线烘焙,也可在物体空间或屏幕空间动态计算(SSAO、HBAO、GTAO 等)。AO 适合漫反射和平滑环境光,不适合点光源或小面光源的可见性(RTR4 11.3 节)。综合角色渲染Character Rendering角色是游戏中最受关注、技术最集中的资产。本页按"动起来 → 表面细节 → 材质着色 → 特殊材质(皮肤、毛发、布料)→ 光照与阴影 → 性能"的顺序,把 wiki 中相关概念页串成一套角色渲染方案的地图。每一层只给出要点与取舍,细节见链接页。图形学顶点压缩与精度Vertex Compression & Precision网格数据像纹理一样可以压缩:传输用的整体压缩(Draco 等)一次性解码,渲染用的定长压缩让顶点着色器在 GPU 上逐顶点解码,从而降低内存、带宽并改善缓存。另一面是精度问题:大世界中单一坐标系会让远离原点的物体抖动,需要相对相机的变换或分段原点。图形学坐标空间Coordinate Spaces顶点从模型到屏幕要依次经过模型空间、世界空间、观察空间、裁剪空间、标准化设备坐标 (NDC)、窗口坐标。每一步变换都有明确目的,大部分可以用 4×4 矩阵表示并相乘合并。图形学曲面细分与几何着色器Tessellation & Geometry Shader曲面细分、几何着色器和流输出是顶点着色器之后的三个可选阶段。曲面细分把曲面描述按需生成合适数量的三角形;几何着色器能把图元变成别的图元,但效率不好、实际用得少;流输出把处理后的顶点写回内存以便复用。游戏美术阴影的美术功能与假阴影Shadow Visual Functions & Faking现以第 3 版 Ch3「Shadows and Occlusion」为准。 阴影与照明同等重要;occlusion 是可与常规阴影结合的柔和变暗。第 3 版新增 occlusion sandwich 等合成向手法。算法细节链图形学阴影/AO 页,本页偏美术功能与 cheat。

被这些页面引用1 篇

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原