Skip to content

Latest commit

 

History

History
204 lines (133 loc) · 6.58 KB

File metadata and controls

204 lines (133 loc) · 6.58 KB

好的,我已经将“使用 InstancedBufferAttribute 存储 AO”这一关键技术决策补充到了设计文档中。

这一修改确保了 AO 数据(光影)与 instanceColor(材质/生物群系染色)完全解耦,为后续草地变色等功能预留了完美的兼容性。

以下是更新后的完整 plan.md 内容:

# 使用极简 AO(Top-Column AO)方案设计文档

## 1. 背景与问题

当前体素引擎使用基于 0fps 的 **逐面 / 逐顶点 Ambient Occlusion** 计算方式:

-   每个方块计算 6 个面
-   每个面计算 4 个顶点
-   每个顶点查询 3 个邻居

该方案在视觉质量上表现优秀,但在 **Web / Three.js / Chunk 实时生成** 场景下存在明显问题:

-   CPU 开销极高
-   Chunk 生成时产生明显卡顿
-   AO 逻辑复杂,维护成本高
-   对 chunk 边界和跨 chunk 访问要求高

因此需要一种 **更简单、可扩展、性能友好** 的 AO 方案。

------------------------------------------------------------------------

## 2. 设计目标

本方案的目标是:

-   将 AO 计算复杂度降低一个数量级
-   每个体素仅计算 **一个 AO 值**
-   AO 不再区分面或顶点
-   AO 结果稳定、可缓存、适合 chunk 系统
-   在牺牲少量细节的前提下,保留足够的空间层次感

------------------------------------------------------------------------

## 3. 极简 AO 核心思想

### 3.1 基本原则

-   **每个体素视为一个整体**
-   AO 仅由该体素 **正上方一层(Y+1)** 的遮挡决定
-   忽略侧向和斜向的逐面遮挡
-   AO 值只反映"顶部环境遮蔽(Top Occlusion)"

该方法在工程上等价于:

> **Column-based AO / Height-based AO**

------------------------------------------------------------------------

### 3.2 遮蔽规则定义(新版:轴向优先)

采用 **“轴向优先 + 角落补充”** 的策略,进一步减少判断次数。

对于体素 `(x, y, z)`,检查其上方 `y+1` 层的遮挡情况:

1.  **优先检查十字轴向**:上、下、左、右(4 个邻居)。
2.  **条件补充角落**:仅当轴向遮挡不足 3 时,才检查对角方向(4 个角落)。
3.  **阈值截断**:AO 最大值为 3。

示意(Y+1 层俯视):

      C   A   C
      A   X   A    →  A (Axis) 优先检查
      C   A   C    →  C (Corner) 仅在需要时补充

------------------------------------------------------------------------

### 3.3 AO 数值范围

  遮蔽方块数   AO 等级   说明
  ------------ --------- ----------
  0            0         完全明亮
  1            1         轻微遮蔽
  2            2         中等遮蔽
  ≥3           3         最强遮蔽

最终映射到渲染使用的 0--255 区间,或直接在 Shader 中作为 0.0-3.0 的浮点数使用。

------------------------------------------------------------------------

## 4. 算法流程

### 4.1 单个方块 AO 计算流程

1.  若方块不可见 / 透明 → 不计算 AO
2.  初始化遮蔽计数 `occ = 0`
3.  **阶段一(轴向)**:检查 `(x±1, y+1, z)``(x, y+1, z±1)`
    * 每发现一个实体,`occ++`
4.  **阶段二(角落)**:若 `occ < 3`,继续检查对角 `(x±1, y+1, z±1)`
    * 每发现一个实体,`occ++`
    * 一旦 `occ >= 3`,立即停止
5.  返回 AO 值 `min(3, occ)`

------------------------------------------------------------------------

### 4.2 推荐伪代码

```js
function computeBlockAO(container, x, y, z) {
  let occ = 0
  const oy = y + 1

  // 1. 轴向优先 (Axis First) - 命中率最高,大概率直接填满
  if (container.isSolidFast(x + 1, oy, z)) occ++
  if (container.isSolidFast(x - 1, oy, z)) occ++
  if (container.isSolidFast(x, oy, z + 1)) occ++
  if (container.isSolidFast(x, oy, z - 1)) occ++

  // 2. 角落补充 (Corner Supplement) - 仅在遮挡不足时计算
  // 这种写法避免了不必要的内存访问
  if (occ < 3) {
    if (container.isSolidFast(x + 1, oy, z + 1)) occ++
    if (container.isSolidFast(x - 1, oy, z + 1)) occ++
    if (container.isSolidFast(x + 1, oy, z - 1)) occ++
    if (container.isSolidFast(x - 1, oy, z - 1)) occ++
  }

  return Math.min(3, occ)
}

5. 数据结构与存储实现

5.1 存储方案:InstancedBufferAttribute

为了在 InstancedMesh 中高效传递 AO 数据,且不干扰植物/树叶的染色逻辑,必须使用自定义 Attribute

  • Attribute 名称: aAo
  • 数据类型: Float32Array (每个实例 1 个 float)
  • 绑定方式: geometry.setAttribute('aAo', new THREE.InstancedBufferAttribute(aoArray, 1))

5.2 为什么不复用 instanceColor

  1. 冲突避免instanceColor 已被用于植物(草、树叶)的生物群系染色(如 0x5B8731)。若将 AO 混合进去,会导致逻辑耦合,难以独立调整光影强度。
  2. 动态性分离:AO 是基于地形动态计算的(0-3),而颜色往往是静态配置或基于 Perlin Noise 的,两者分离更利于维护。

5.3 Shader 适配流程

  1. Vertex Shader: 接收 attribute float aAo,将其通过 varying float vAo 传递给 Fragment Shader。
  2. Fragment Shader: 读取 vAo,计算遮蔽因子 occlusion = 1.0 - (vAo * strength),并以此调整 csm_DiffuseColor

6. 与现有 Chunk 系统的适配性

6.1 优点

  • 分支预测友好:大部分地形(平地)在轴向判断阶段就会结束。
  • 内存优化:每个方块仅增加 4 字节显存开销。
  • 批处理友好:可在 Chunk 生成 Mesh 的阶段一次性计算并填入 Buffer。

6.2 推荐使用场景

  • Web / Three.js 体素引擎
  • 多 biome / 大世界
  • Chunk 实时加载
  • 非写实风格体素世界

7. 视觉效果评估

7.1 优势

  • 风格化强烈:这种计算方式产生的阴影更接近早期 Minecraft 的经典感觉。
  • 地表层次感明显。
  • 计算开销几乎可以忽略不计。

7.2 局限

  • 洞穴侧壁 AO 减弱。
  • 对于复杂的非封闭结构,阴影可能比 9 宫格循环检测略少,但在视觉上很难察觉差异。

8. 可扩展方向(非必须)

  • 地表使用极简 AO,洞穴使用旧 AO
  • AO 与高度图结合(shader 计算)
  • AO 结果缓存到 height-column 级别

9. 总结

极简 AO(轴向优先版)配合 InstancedBufferAttribute 是一种 极致压榨性能且架构清晰 的方案:

  • 逻辑极简:甚至不需要 for 循环
  • 架构解耦:光影与材质颜色互不干扰
  • 性能极高:平均内存访问次数减半,显存占用极低

推荐在当前项目中作为默认 AO 实现。