## 1. 为什么叫 Depth Stencil?

**Depth(深度)** 和 **Stencil(模板)** 是 GPU 渲染管线中两个**独立的、但经常捆绑在一起使用的缓冲区**。它们每个“像素”都对应一个值,但含义和用途完全不同。

| 概念 | 作用 | 类比 | 典型取值范围 |
|------|------|------|-------------|
| **Depth Buffer** | 记录每个像素距离摄像机的远近,解决“谁遮挡谁”的问题 | 一张距离地图,值越小离镜头越近 | 0.0(近)~ 1.0(远) |
| **Stencil Buffer** | 记录每个像素的“标签”或“掩码”,用于控制后续绘制是否被允许 | 一张盖章纸,0=未标记,1=已标记 | 0~255 的整数 |

它们通常共享同一块显存区域(比如 32 位:24 位深度 + 8 位模板),所以常被合称为 **Depth Stencil** 或 **深度模板缓冲区**。你看到的节点名 `Set Custom Depth Stencil Value` 里的 **Custom** 是指“自定义模板值”,不是深度值。

---

## 2. Depth 和 Stencil 分别代表啥意思?

Depth(深度缓冲区)
- **核心功能**:解决**遮挡关系**(Z‑culling)。
- **工作方式**:渲染一个物体前,GPU 会比较当前像素的深度值与缓冲区里已有的深度值。如果当前像素更近,就覆盖它并更新深度;如果更远,就丢弃它(被前面的物体挡住)。
- **结果**:远处的物体会自动被近处的物体遮挡,无需排序。

Stencil(模板缓冲区)
- **核心功能**:**按像素条件性地拒绝绘制**。
- **工作方式**:你可以先往模板缓冲区里写入一个值(比如 `1`),然后在渲染其他物体时,设置一个比较条件(比如“仅当模板值等于 1 时才绘制”)。不满足条件的像素直接跳过。
- **典型应用**:镜子、平面反射、轮廓描边、体积光遮罩等。

> **Custom Stencil** 是虚幻引擎额外提供的一套独立模板通道,不干扰引擎自己用的模板值,专门给开发者自由使用。`Set Custom Depth Stencil Value` 就是写入这个自定义通道。

---

## 3. 基于 Custom Stencil 的描边原理(重点)

在 Lyra 项目中,描边**不是**通过直接改变模型材质实现的,而是通过**后处理材质**在屏幕上二次绘制轮廓。整个流程分三步:

### 步骤 1:打标签(CPU 侧)
蓝图调用 `Set Custom Depth Stencil Value`,把角色的所有 `UPrimitiveComponent` 的自定义模板值设为队伍 ID(例如红队=1,蓝队=2)。

### 步骤 2:渲染几何体(GPU 侧)
正常渲染角色模型时,GPU 不仅会输出颜色、深度,还会把每个像素的 **Custom Stencil 值** 写入一个独立的缓冲区。这个缓冲区对最终画面不可见,但它记录着“当前屏幕上每个像素属于哪支队伍”。

### 步骤 3:后处理描边(材质侧)
最后,一个全屏的后处理材质被应用。这个材质可以读取 **Scene Texture: Custom Stencil** 节点,获得当前像素的模板值。描边算法通常是这样:

```
1. 采样当前像素的 Custom Stencil 值,记为 A。
2. 采样当前像素周围四个方向(上下左右)的 Custom Stencil 值,分别记为 B1、B2、B3、B4。
3. 如果 A == 0(背景或非角色物体),但任意一个 Bi != 0(周围有队伍标记的像素),说明这个像素正好处于角色的边缘。
4. 根据 Bi 的值(队伍 ID)输出对应颜色的描边。
```

**更直观的理解**:后处理材质像一个智能边缘检测器。它对比每个像素和它的邻居:如果你没有队伍标签,但你的邻居有,那么你就是“轮廓边界”,于是给你涂上你邻居的队伍颜色。

> 这种描边方式的优点:不修改原始模型材质、性能好、边缘清晰、且天然支持不同队伍不同颜色。

---

## 总结一句话

- **Depth** = 谁在前谁在后 → 解决遮挡
- **Stencil** = 给像素贴标签 → 后处理读取标签画出描边
- **Set Custom Depth Stencil Value** = 给角色的所有网格体贴上队伍 ID 标签
- **描边原理** = 后处理材质找出“有标签的像素”和“无标签但邻居有标签的像素”,把后者涂上标签对应的颜色