# RenderPipeline API（后处理管线）

`viewer.renderPipeline` 管理渲染后处理链，内置 Bloom、SSGI、SSR、TRAA 支持，并提供自定义后处理覆盖机制。

## 渲染节点图

```
Stage 1: pass(scene, camera) + MRT（按需配置 output/emissive/normal/SSR G-buffer/velocity）
    ↓
Stage 2: 后处理链
    ├─ 内置模式: SSGI → SSR + Temporal Reproject + Denoise → Bloom
    └─ 自定义模式: outputComposer(scenePass) → Node
    ↓
Stage 3: Output Effects（仅默认/内置模式，插件/扩展追加效果）
    ↓
Stage 4: TRAA 时间抗锯齿
    ↓
Stage 5: Overlay Passes（blendColor 叠加 ViewerHelper 等）
    ↓
outputNode = 最终结果
```

## 属性

| 属性 | 类型 | 说明 |
| :--- | :--- | :--- |
| `scenePass` | `PassNode` | 场景渲染通道节点，`pass(scene, camera)` 的返回值。 |
| `needsUpdateOutputNode` | `boolean` | 标记输出节点需要在下一帧重建。 |

## Bloom

基于 emissive MRT 通道的泛光效果。

```typescript
// 开启 Bloom
viewer.renderPipeline.enableBloom({ strength: 1.5, radius: 0.4, threshold: 0.2 });

// 运行时更新参数
viewer.renderPipeline.updateBloom({ strength: 2.0 });

// 关闭 Bloom
viewer.renderPipeline.disableBloom();
```

### `BloomConfig`

| 属性 | 类型 | 默认值 | 说明 |
| :--- | :--- | :----- | :--- |
| `strength` | `number` | `1` | 泛光强度 |
| `radius` | `number` | `0` | 泛光扩散半径 |
| `threshold` | `number` | `0` | 亮度阈值，低于此值不产生泛光 |

## SSGI（屏幕空间全局光照）

> 仅支持 `PerspectiveCamera`，使用 `OrthographicCamera` 时会自动跳过并输出警告。

SSGI 分别生成 AO 与 GI 纹理，并按 `scene.rgb × AO + diffuse.rgb × GI` 合成最终颜色。AO 只压暗遮蔽区域，GI 只增加间接漫反射；背景不参与 SSGI。`Viewer` 默认的 reversed depth 和显式配置的标准深度均受支持，应用侧无需转换 depth texture。

[在线示例：默认开启的纯 AO](https://u-space-phi.vercel.app/examples/test_ao.html)

```typescript
// 开启 SSGI
viewer.renderPipeline.enableSSGI({ aoIntensity: 1, giIntensity: 10 });

// 运行时更新参数（直接修改 uniform，不重建节点图）
viewer.renderPipeline.updateSSGI({ giIntensity: 20, radius: 8 });

// 关闭 SSGI
viewer.renderPipeline.disableSSGI();
```

### `SSGIConfig`

| 属性 | 类型 | 默认值 | 范围 | 说明 |
| :--- | :--- | :----- | :--- | :--- |
| `sliceCount` | `number` | `1` | 1–4 | 半球切片数 |
| `stepCount` | `number` | `12` | 1–32 | 每切片采样步数 |
| `aoIntensity` | `number` | `1` | 0–4 | AO 对遮蔽项的对比度/压暗强度 |
| `giIntensity` | `number` | `10` | 0–100 | 加到漫反射项上的间接光强度 |
| `radius` | `number` | `12` | 1–25 | 世界空间采样半径 |
| `thickness` | `number` | `1` | 0.01–10 | 物体厚度（世界单位） |

## SSR（屏幕空间反射）

SSR 使用 stochastic ray marching 生成反射，随后通过运动向量执行 Temporal Reproject，并使用 Recurrent Denoise 进行时空降噪。完整链路需要连续帧积累，启用后应将 `viewer.frameloop` 设为 `'always'`。

> 仅支持 `PerspectiveCamera`，使用 `OrthographicCamera` 时会自动跳过并输出警告。

[在线示例：SSR 时空降噪](https://u-space-phi.vercel.app/examples/test_ssr.html)

```typescript
viewer.frameloop = 'always';

viewer.renderPipeline.enableSSR({
  quality: 0.25,
  maxDistance: 0.4,
  temporal: { maxFrames: 16 },
  denoise: { radius: 1.5, strength: 0.725 },
});

viewer.renderPipeline.updateSSR({
  intensity: 1.2,
  denoise: { radius: 1 },
});

viewer.renderPipeline.disableSSR();
```

### `SSRConfig`

| 属性 | 类型 | 默认值 | 说明 |
| :--- | :--- | :----- | :--- |
| `resolutionScale` | `number` | `1` | SSR 计算分辨率比例，范围 0–1 |
| `quality` | `number` | `0.25` | Ray marching 采样质量，范围 0–1 |
| `mirrorBias` | `number` | `0.5` | 将 stochastic GGX 射线收束到镜面主瓣 |
| `maxDistance` | `number` | `0.4` | 最大反射追踪距离 |
| `intensity` | `number` | `1` | 反射强度 |
| `thickness` | `number` | `0.1` | 命中厚度容差 |
| `maxLuminance` | `number` | `35` | HDR 反射亮度上限 |
| `stepExponent` | `number` | `3` | Ray marching 步长分布指数；修改会重编译 SSR 材质 |
| `binaryRefine` | `boolean` | `false` | 是否使用二分子步细化命中；修改会重编译 SSR 材质 |
| `screenEdgeFade` | `number` | `0.2` | 屏幕边缘淡出宽度 |
| `screenEdgeFadeBlack` | `boolean` | `true` | 边缘和未命中区域是否淡出到黑色；修改会重编译 SSR 材质 |
| `temporal` | `SSRTemporalReprojectConfig` | 见下表 | 时域重投影参数 |
| `denoise` | `SSRDenoiseConfig` | 见下表 | 循环降噪参数 |

### `SSRTemporalReprojectConfig`

| 属性 | 类型 | 默认值 | 说明 |
| :--- | :--- | :----- | :--- |
| `maxFrames` | `number` | `16` | 历史累积最大帧数 |
| `clampIntensity` | `number` | `0.25` | 历史颜色方差裁剪强度 |
| `flickerSuppression` | `number` | `1` | 时域闪烁抑制强度 |
| `hitPointReprojection` | `boolean` | `true` | 是否启用镜面命中点视差重投影 |

### `SSRDenoiseConfig`

| 属性 | 类型 | 默认值 | 说明 |
| :--- | :--- | :----- | :--- |
| `lumaPhi` | `number` | `0.75` | 亮度边缘停止权重 |
| `depthPhi` | `number` | `20` | 深度边缘停止权重 |
| `normalPhi` | `number` | `0.3` | 法线边缘停止权重 |
| `roughnessPhi` | `number` | `100` | 粗糙度边缘停止权重 |
| `radius` | `number` | `1.5` | 空间降噪半径 |
| `alphaPhi` | `number` | `5` | SSR 射线长度边缘停止权重 |
| `strength` | `number` | `0.725` | 历史融合强度 |
| `adapt` | `number` | `0.5` | 自适应历史融合系数 |
| `smoothDisocclusions` | `boolean` | `true` | 是否平滑遮挡解除区域 |
| `flickerSuppression` | `number` | `1` | 降噪阶段闪烁抑制强度 |
| `adaptiveTrust` | `number` | `1` | 历史可信度自适应强度 |

SSR 会增加 `ssrDiffuse`、`ssrNormal` 和 `velocity` MRT 通道；其中法线/粗糙度与漫反射/金属度分别打包到两个 8-bit 附件中。SSGI 单独启用时使用 `ssgiDiffuse`，与 SSR 同时启用时则复用 `ssrDiffuse.rgb`，避免为相同的漫反射数据重复分配 MRT 附件。它可以和 SSGI、Bloom、TRAA 组合，但同时启用仍会增加 MRT 带宽和时域 pass 数量。

默认的 `scene.environment` 通常是 PMREM 纹理，不能直接作为 SSRNode 的等距柱状 HDR 输入。本封装只叠加屏幕空间命中结果，不修改全局 `PhysicalLightingModel`。

### Reversed depth 兼容

`Viewer` 默认启用 Three.js `reversedDepthBuffer`。SSGI 会在 clear depth 为 `0` 时跳过背景像素；SSR 封装会为 SSR、Temporal Reproject 和 Recurrent Denoise 三个内部全屏 pass 补充相同的 reversed-depth 背景像素早退。两者都保留原始 reversed depth 供位置重建、ray marching 和历史深度比较使用。应用侧无需关闭 reversed depth，也不需要转换 depth texture。

如果通过 `rendererOptions: { reversedDepthBuffer: false }` 使用标准深度，兼容层不会改写 Three.js 原有节点图。

`stepExponent`、`binaryRefine` 和 `screenEdgeFadeBlack` 属于编译期 SSR 参数，运行时修改会重编译内部 SSR 材质；其余数值参数直接更新 uniform。

### 性能建议

`resolutionScale` 只缩放 SSR ray-marching target；Temporal Reproject 和 Recurrent Denoise 仍跟随 renderer 的完整 drawing-buffer 尺寸。在 Retina 屏幕或重型场景中，可在创建 Viewer 时使用 `pixelRatio: 1`：

```typescript
const viewer = new Viewer({
  el: document.getElementById('app')!,
  pixelRatio: 1,
});
```

建议从默认 `maxDistance: 0.4` 开始调节，并谨慎启用每帧更新的大尺寸动态阴影。`frameloop = 'always'` 仍是时域重投影和循环降噪正确收敛的必要条件。

## TRAA（时间抗锯齿）

基于运动向量的时间分辨率抗锯齿。

```typescript
// 开启 TRAA
viewer.renderPipeline.enableTRAA();

// 关闭 TRAA
viewer.renderPipeline.disableTRAA();
```

## 自定义后处理（OutputComposer）

使用 `setOutputComposer` 可完全覆盖内置后处理链，接收 `scenePass` 返回自定义的 TSL 节点。自定义 composer 会保持最终输出语义，不再叠加插件注册的 output effects。

```typescript
import { luminance, vec4 } from 'three/tsl';

// 灰度滤镜
viewer.renderPipeline.setOutputComposer((scenePass) => {
  const color = scenePass.getTextureNode();
  const gray = luminance(color.rgb);
  return vec4(gray, gray, gray, 1.0);
});

// 恢复默认（清除自定义后处理）
viewer.renderPipeline.setOutputComposer(null);
```

## Output Effects

插件可使用 `addOutputEffect` 在默认/内置后处理链之后追加效果，而不占用业务侧的 `setOutputComposer()`。如果业务设置了自定义 composer，output effects 会被跳过，由 composer 自己负责完整输出。

```typescript
const effect = (scenePass, inputNode) => inputNode;
viewer.renderPipeline.addOutputEffect(effect);
viewer.renderPipeline.removeOutputEffect(effect);
```

如果 effect 已经调用了 `renderOutput()`，可设置 `effect.includesOutputTransform = true`，管线只会在该 effect 实际生效时关闭默认输出变换。

如需额外 MRT 输出通道，可用 `addMRTChannel` / `removeMRTChannel` 注册，不会污染核心依赖。

## Overlay Passes

用于叠加辅助渲染层（如 ViewerHelper），始终在后处理链之后执行。

```typescript
viewer.renderPipeline.addOverlayPass(overlayNode);
viewer.renderPipeline.removeOverlayPass(overlayNode);
```
