Files
AnyImage/openspec/changes/standardize-blender-object-coordinates/design.md
nan 4035f5c9e4 Refactor geometry generation to standardize Blender object coordinates
- Unified the canonical local frame for generated geometries (Plane, Depth Plane, Relief Plane, Cutout, Depth Cutout) to align with Blender's object coordinate system, setting X as horizontal, Z as up, and +Y as depth.
- Removed the `Depth Axis` parameter from `O Image Cutout` and `O Image Depth Cutout`, simplifying the geometry generation process.
- Updated the depth direction defaults to use +Y instead of -Z for `O Image Relief Plane` and Depth Symmetry.
- Adjusted the geometry nodes to ensure thickness, projection, and shell displacements operate along the Y-axis.
- Implemented new capabilities for handling object space normals to align with the new canonical frame.
- Enhanced the Depth Symmetry functionality to mirror across the canonical Y-axis and perform a final rotation to maintain the correct orientation.
- Added tests to validate the new coordinate system and ensure compatibility with existing assets.
2026-09-15 13:33:37 +08:00

5.3 KiB
Raw Blame History

Context

当前 Plane、Depth Plane、Relief Plane、Cutout 与 Depth Cutout 的生成网格沿 Image Empty 局部坐标:图片平面为 XY,图片向上为 +Y,深度向纸内为 -Z。Cutout 的 BaseShape 位于 Z=0,Geometry Nodes 沿 Z 做厚度、投影和对称。这个约定与 Blender 常规对象坐标不一致:Blender 对象通常 up=Z、front=-Y。

为了把侧视对象最终摆成“左右对称轴为 X、up 为 Z”,现有实现给 Cutout 节点加入 Depth Axis,把输出整体转到 +X。该参数把最终摆放轴与对称轴耦合,还要求 Object Space Normal 额外补偿,使用户在四个 Cutout 入口间看到不一致的最终方向。

本次变更把生成几何的 canonical local frame 统一到 Blender 对象坐标,并删除 Depth Axis。

Goals / Non-Goals

Goals:

  • 为所有生成几何定义并实现同一套 Blender canonical local frame。
  • 让 Cutout BaseShape、Plane、Depth Plane、Relief Plane 的未修改网格都位于 XZ 平面,Y=0。
  • 让 Depth Symmetry 在 canonical 坐标中沿 Y 镜像,最后绕 Z 转到 X,保持 up=Z。
  • 让 Object Space Normal 随 canonical frame 和 Depth Symmetry 最终朝向一致。
  • 删除 O Image Cutout 与 O Image Depth Cutout 的 Depth Axis 接口和实现。

Non-Goals:

  • 不改变 Image Empty 自身坐标或 Blender 相机/Server 模型输出坐标。
  • 不为用户新增轴向选择项。
  • 不自动迁移用户工程中已保存的旧节点组或已生成对象。

Decisions

1. 新 canonical frame 由创建阶段直接生成

不添加“全局坐标变换”节点,而是在 Python 建网和节点构建阶段直接按新坐标生成。

旧 Image Empty local → 新 canonical object local

X   → X
Y   → Z
-Z  → +Y
+Z  → -Y

结果:

X = 图片右
Z = 图片 up
+Y = 图片纸内 / depth
front = -Y
BaseShape / Plane 未修改网格位于 XZ 平面,Y=0

备选方案是在所有 modifier 前统一旋转,但会让 BaseShape、UV、metric 和节点内部轴反复转换,且不能消除 Depth Axis 类补偿。直接生成更直接,符合“少就是多”。

2. 所有节点深度轴从 Z 改为 Y

厚度、投影、Shell、Balloon、Relief 位移和 Depth Symmetry 镜像均改为 Y 轴语义:

  • Image Plane 的网格/立方体尺寸使用 X/Z,厚度沿 Y。
  • Depth Plane 与 Relief Plane 的投影位移沿 Y。
  • Cutout 与 Depth Cutout 的 front/back 方向由 -Y/+Y 表达。
  • Depth Direction 默认值从 (0, 0, 1) 改为 (0, 1, 0)。

3. Depth Symmetry 作为两阶段坐标语义

O Image Cutout Symmetry 保持“先 canonical 对称、后最终摆放”:

canonical step:
  mirror plane = XZ, mirror axis = +Y
  keep one Y sign, mirror to the other side

final step:
  rotate around Z so +Y → +X
  keep +Z as up

该 yaw 旋转同时会把 canonical front -Y 变成 -X。这是本变更明确接受的最终朝向。

删除 O Image Cutout 与 O Image Depth Cutout 的 Depth Axis。Depth Symmetry 的最终朝向只存在于 O Image Cutout Symmetry。

4. Object Space Normal 使用明确的几何属性契约

材质仍通过 Geometry 属性把 Object Space Normal 从生成坐标转换到最终渲染坐标:

  • canonical frame 转换进入 Object Normal 的解释路径。
  • Depth Symmetry 的 front/back 反射改为 canonical Y 反射。
  • 最终 yaw 旋转由 O Image Cutout Symmetry 写入最终朝向属性,材质读取同一属性。
  • 删除旧 Depth Axis 对应的 CUTOUT_Z_TO_X 补偿路径。

普通 Plane / Depth Plane / Relief Plane 的 Object Normal 也按新 canonical frame 验证,不再依赖 Cutout 的旧轴补偿。

Risks / Trade-offs

  • 影响面很大,几乎覆盖所有生成几何和节点资产 → 使用既有 uv run node-group build 与全量测试闭环;不保留兼容层。
  • BaseShape 和 Plane 世界位置可能出现符号/方向回归 → 对每个入口比较源 Image Empty 的世界 bounds 和生成网格 world-space 包围盒。
  • Object Space Normal 在渲染中的表现可能不易从网格位置直接推断 → 保留/扩展材质渲染测试,覆盖 front、back、wall 和最终朝向。
  • Depth Symmetry 最终 front 从 -Y 变为 -X,可能影响后续用户操作 → 在 spec 中明确该行为,不提供旧行为。
  • 旧 .blend 内嵌节点和已生成对象无法自动迁移 → 运行时继续从 O_AnyImage.blend 资产加载,不扫描用户工程。

Migration Plan

  1. 先在 Python 建网层实现新 canonical frame,并同步 Cutout BaseShape、Plane、Depth Plane、Relief Plane 的顶点/UV/法线。
  2. 重写 Geometry Nodes 的轴语义并删除 Depth Axis。
  3. 调整 Depth Symmetry 与 Object Normal 属性契约。
  4. 更新测试、tools/nodes/check.py 和资产构建;运行 uv run node-group build。
  5. 运行全量测试并检查旧 Depth Axis、旧轴常量与旧 world-space 方向引用。

回滚策略为从版本控制恢复本次变更前的源码与 O_AnyImage.blend,不涉及数据迁移。

Open Questions

  • 最终 yaw 旋转选择 +Y → +X 还是 +Y → -X:在实现时用一个小型 orientation test 固定,保持 front 变化方向可预测。
  • 普通 Plane / Depth Plane 的 Object Normal 转换放在材质节点内还是图片加载阶段:设计倾向保留材质属性契约,实现时按节点复杂度和测试成本选择。