标签为“3D渲染”的页面如下
2026-04-18
在 Metal 引擎中实现透视阴影贴图
前言 有光的地方就有影子。但如果你写过渲染引擎,就会知道——光照模型(比如 Blinn-Phong)默认是不会产生阴影的。它只告诉你"这个点被光照了多亮",但不会告诉你"这个点有没有被别的东西挡住"。
KisekiEngine 之前已经实现了 Blinn-Phong 点光源光照,场景里有立方体、有地面、有光照明暗,但就是没有阴影。一个物体浮在地面上,下面却没有影子,看起来就像悬浮在空中一样不真实。
要加阴影,最经典的方案就是 Shadow Mapping(阴影贴图)。它的核心思想只需要一句话:
站在光源的位置看一眼,记下每个方向上最近的深度;然后在正常渲染时,把每个片元投影回光源视角,比较一下深度——如果片元比记录的深度更远,说明它被什么东西挡住了,那它就在阴影里。
接下来,我们一步一步把这个想法变成 Metal 代码。
整体架构:双 Pass 渲染 整个实现的核心是在同一个 Command Buffer 里做两个 Render Pass:
┌─────────────────────────────────────────────────┐ │ Command Buffer │ │ │ │ ┌─────────────────────┐ ┌──────────────────┐ │ │ │ Shadow Pass │ │ Main Pass │ │ │ │ │ │ │ │ │ │ 光源视角渲染深度 │──▶│ 正常渲染 + │ │ │ │ → Shadow Map │ │ 采样 Shadow Map │ │ │ │ (1024×1024) │ │ → 阴影判定 │ │ │ └─────────────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────┘ Pass 1(Shadow Pass):把光源当成一台摄像机,用透视投影渲染整个场景,但只写深度,不输出颜色。结果存到一张 1024×1024 的深度纹理里,这就是 shadow map。 Pass 2(Main Pass):用正常的摄像机渲染场景。在片段着色器里,把每个片元的世界坐标投影到光源空间,去 shadow map 里查一下深度,判断它是不是在阴影里。 下面按实现顺序逐步讲解。
继续阅读