# PBR、HDR 与后处理

本文记录 Hilo3D 当前 PBR 材质、glTF layered materials、opaque scene
texture 和内置后处理的生产合同。实现仍遵循唯一共享路径：

`shared renderer -> Render Graph -> portable RHI -> WebGPU/WebGL 2`

WebGPU 和 WebGL 2 不维护两套材质或后处理算法；全部 raster shader 仍以 GLSL ES
3.00 为唯一人工源码，WebGPU 产物经 Naga 生成。

## 旧效果不稳定的原因

旧 PBR 的主要问题并不是某一个参数，而是照明、颜色空间和帧编排没有形成完整链路：

| 问题                                                               | 可见结果                                                       |
| ------------------------------------------------------------------ | -------------------------------------------------------------- |
| 较老的 GGX geometry term、简单 Lambert diffuse、无多次散射能量补偿 | 粗糙金属偏黑，高光在粗糙度变化时能量不稳定                     |
| clearcoat 与 base layer 混合不完整                                 | 清漆像额外加亮，而不是有 Fresnel 遮挡的第二层介质              |
| lighting、gamma encode 和材质级 tone mapping 混在同一 shader       | HDR 值过早被压缩或截断，Bloom 拿不到真实高亮                   |
| point/spot light 存在远距离能量下限，range 边界不平滑              | 灯光在远处不自然地残留，边界容易出现亮度断层                   |
| scene color 不可供透明材质读取                                     | transmission/volume 无法看到已渲染的不透明背景                 |
| 示例自行组织多张 RenderTarget 和分离 Gaussian blur                 | Bloom 尺寸、能量、resize、双后端与资源生命周期都不属于引擎合同 |

环境光照仍需要应用提供预过滤 specular environment、diffuse irradiance/SH 和 BRDF
LUT。引擎不会把任意普通图片假装成已卷积的 IBL；缺少这些输入时，材质只能获得场景中的直接光和 ambient
light。

## 当前 PBR 光照

共享 PBR shader 使用：

- perceptual roughness 到 GGX alpha 的平方映射；
- height-correlated Smith-GGX visibility；
- Schlick Fresnel 和 Burley diffuse；
- dielectric F0 从 IOR 推导，默认 IOR 1.5 对应约 4% 正入射反射率；
- 基于 Belcour/Barla 近似的薄膜干涉 Fresnel：以纳米厚度和薄膜 IOR 做解析光谱积分，并用最大 RGB 反射率约束 base
  layer 能量；
- anisotropic GGX distribution/visibility，并从 tangent direction、rotation 与强度组合各向异性轴；
- split-sum specular IBL、粗糙表面的能量补偿、specular AO 与 horizon occlusion；
- 带独立 roughness/normal 的 dielectric clearcoat layer，base layer 按 clearcoat Fresnel 衰减；
- rectangular area
  light 保留 LTC 积分，并拆分 diffuse/specular 贡献以正确参与 transmission 和 clearcoat 分层；
- inverse-square punctual light attenuation、近距离稳定下限、平滑有限 range 和平滑 spot cone；
- 线性 HDR attachment 输出；当目标是 `rgba16float`/`rgba32float` 时，材质级 gamma encode 和旧
  `useHDR` tone mapping 自动跳过，显示变换只在后处理末端发生。

## glTF layered material 扩展

`GLTFParser` 原生解析以下扩展，并把所有引用纹理加入资源预加载集合：

- `KHR_materials_anisotropy`
    - `anisotropyStrength`
    - `anisotropyRotation`
    - `anisotropyTexture`：RG direction、B strength
- `KHR_materials_clearcoat`
    - factor、roughness、normal 及三张可选纹理
    - clearcoat normal texture 的 `scale`
- `KHR_materials_transmission`
    - factor 与 R-channel texture
- `KHR_materials_volume`
    - thickness factor、G-channel texture、attenuation distance/color
- `KHR_materials_ior`
    - volume refraction 使用的 index of refraction；没有扩展时为 1.5
- `KHR_materials_iridescence`
    - factor、thin-film IOR、最小/最大纳米厚度
    - R-channel intensity texture 与 G-channel thickness texture

标准定义为有限区间的 factor 会在加载时校验，而不是把无效资产静默 clamp。材质 shader 仍会做最终数值保护，避免应用运行时修改导致 NaN/Inf 扩散。

## Opaque scene texture

`ForwardRenderPipelineFactory({ opaqueTexture: true })` 将前向场景拆成两个 renderer list：

```text
opaque scene pass
  -> exact scene-color copy
  -> transparent scene pass (u_opaqueTexture)
```

copy 是 Render Graph 中显式声明的 `TextureCopyPass`，不是同一 attachment 的读写 feedback。透明 PBR
draw 在 graph prepare 阶段获得 frame-lifetime scene texture bind
group；普通材质不承担该 binding。WebGPU 使用固定 group 3 texture/sampler pair，custom uniform
block 从 group 3 binding 2 开始；WebGL 2 使用同一 reflection 计划映射 combined
sampler。同一 transparent pass 内的所有 transmission shader variant 共用 renderer-owned canonical
group 3 layout，避免不同 glTF 材质排列各自创建 native layout 后无法绑定同一 opaque scene texture。

Transmission 使用投影后的 screen-space refraction
ray；volume 根据折射方向修正光程，再用 Beer-Lambert attenuation 处理吸收。它是屏幕空间实现，因此：

- 只能折射 opaque copy 中已经存在的内容；
- 屏幕外 ray 会 clamp 到可见边界；
- 不解决多层透明物体互相折射、离屏内容、色散或真实多次内部反射。

这些限制是显式的最低实现边界，不会通过 backend 私有 framebuffer 抓取绕过 Render Graph。

## 内置 Bloom

`Bloom` 是 `after-transparent` forward feature，在线性 `rgba16float` scene color 上执行：

1. soft-knee threshold 与 firefly clamp；
2. 半分辨率 13-tap Karis 加权预滤波；
3. 有界 half-resolution 13-tap downsample pyramid；
4. 从最粗层向上执行 9-tap tent upsample，并用 `scatter` 控制跨尺度扩散；
5. tone mapping 之前在线性 HDR 中 additive composite。

Karis 权重会除以累计权重，因此恒定高亮保持能量，不会因为 firefly 抑制而把整张 Bloom 直接 tone-map。Pyramid
level 根据输出尺寸、`maxLevels` 和 `minResolution` 每帧确定，所有 transient texture 由 Render
Graph 分配和回收。

## Color Uber

`ColorUber` 是最终的 `after-post-process` display transform。一个 fullscreen pass 按顺序完成：

- exposure；
- LMS white balance；
- color filter 与 contrast；
- channel mixer；
- lift/gamma/gain；
- hue shift 与 saturation；
- PBR Neutral、ACES fitted、Reinhard 或 no tone mapping；
- aspect-correct vignette；
- 精确 linear-to-sRGB；
- display-space 8-bit dithering。

默认 tone mapper 是 Khronos PBR Neutral，避免高亮压缩时过度 hue shift。Bloom 必须在 Color
Uber 之前，不能在 sRGB/tone-mapped 结果上模糊。

## 推荐用法

完整 HDR 管线使用 `PostProcessRenderPipelineFactory`：

```ts
const stage = await Hilo3d.Stage.create({
    backend: 'auto',
    camera,
    container,
    renderPipeline: new Hilo3d.PostProcessRenderPipelineFactory({
        bloom: {
            threshold: 1,
            knee: 0.5,
            intensity: 0.8,
            scatter: 0.7
        },
        colorUber: {
            exposure: 0,
            toneMapping: 'pbr-neutral',
            dithering: true
        },
        opaqueTexture: true
    })
});
```

该 factory 固定 attachment-zero scene color 为 `rgba16float`，默认启用 Bloom、Color Uber 和 opaque
texture。需要自行组合时，也可以把 `Bloom`/`ColorUber` 作为 `ForwardRenderPipelineFactory.features`
使用；调用方必须保证 Bloom 输入仍是线性 HDR 格式，并让 Color Uber 位于所有 HDR effect 之后。

三个示例覆盖基础参数矩阵、直接 layered material API 和真实 glTF 资产路径：

- [`pbr2.html`](../examples/pbr2.html) 使用单层 30-sample
  matrix 展示 metallic/roughness 响应，并在窄视口缩放、平移 scene grid，避免 UI 遮挡材质样本；
- [`pbr_layered_materials.html`](../examples/pbr_layered_materials.html)
  可实时开关 anisotropy、clearcoat 与 transmission，画面同时使用 volume attenuation、opaque
  texture、Bloom 和 Color Uber；
- [`gltf_material_extensions.html`](../examples/gltf_material_extensions.html) 可切换 Khronos glTF
  Sample Assets 中的 Anisotropy Barn Lamp、Clearcoat Wicker 和 Dragon
  Attenuation，以及带动画、薄膜干涉和体积折射的 Iridescent Dish with
  Olives。模型许可与 SHA-256 记录在
  [`examples/models/KhronosPBR/ATTRIBUTION.md`](../examples/models/KhronosPBR/ATTRIBUTION.md)。

共享 example 环境不再读取旧的 6 张低分辨率 diffuse bake 和 6 张 cloud cube face。当前
[`studioEnvironment.ts`](../examples/shared/studioEnvironment.ts) 从连续 cube
direction 函数生成 neutral studio、warm/cool softbox、平滑 diffuse reference 和带 mip
chain 的 specular reference；Skybox 与 image-release 专项页面把同一环境编码为 runtime PNG data
URL，以继续验证 loader 与 CPU image release，而不保留另一套旧图片。它是 deterministic example
reference，不替代应用在生产中提供经过正式卷积的 HDR IBL。

## 验证要求

改动 PBR、Bloom、Color Uber 或 scene texture ABI 时至少需要：

- WebGL 2 GLSL compile/link；
- Naga GLSL -> WGSL 转换；
- 真实 WebGPU shader module/pipeline；
- Render Graph dependency/copy validation；
- WebGL 2 与 WebGPU 的 transmissive PBR + post-processing browser render；
- public API report、TypeDoc、类型测试和 example build。
