计算机图形学 E01:同一个球的网格与距离场
一个单位球可以保存为三角形,也可以保存成函数 f(p)=‖p‖−1。两种表示都能产生球形画面,但它们的误差来自不同位置:网格先离散曲面,距离场沿射线反复查询。增加三角形和增加查询预算,分别改变什么?把球压成椭球后,同一个函数还能给出安全步长吗?
本实验复用第 17、18 篇的射线和 BVH,用解析球交点检查实际等值面网格与 sphere tracing。薄板和非均匀缩放另设反例。结果保留步数耗尽、薄结构消失,以及旧三角形求交核在共享边上的数值异常。
零集合与距离是两个要求
隐式曲面只要求 f(p)=0 描述边界。球内负、球外正,可以表达内外关系;函数值的大小却未必是到曲面的距离。将 f 乘以 100,零集合没有改变,直接拿函数值推进射线就可能跨过整个物体。
真实有符号距离 d(p) 的绝对值,是 p 到边界上最近点的欧氏距离,符号区分内外。单位球的最近边界点沿径向,因此 d(p)=‖p‖−1。中心取 −1,球面取 0,点 (2,0,0) 取 1,这三个手算值进入检查器。
球面外一点的距离为 d,半径 d 的开球内没有边界。单位方向射线从该点前进 d,不会穿过边界。这是 sphere tracing 的步长依据。安全距离下界也能使用,但必须非负且不超过真实外部距离;仅有“形状看起来相同”不能建立这个条件。
设射线 r(t)=o+t v,‖v‖=1,从外部开始,则更新为:
本实现要求有限 t 区间、归一化方向和外部起点。方向长度若为 2,同样的 t 增量对应两倍世界距离,需要另行除以方向长度。内部起点的负距离也不能直接套用这个只向前推进的循环。
停止状态分成命中、超过有限区间、预算耗尽和跨入负值四种。d≤ε 是近似命中,d<−ε 是越界反例。预算耗尽既不说明命中,也不说明没有交点,不能涂成普通背景后删掉统计。
网格来自标量格点
网格路线先在 [−1.25,1.25]³ 上采样球 SDF。每轴 n 个格子有 n+1 个格点,总数为 (n+1)³。这里保存 double 标量,不保存“体素占据为真”的二值图;后者已经丢掉边界插值所需的数值。
每个立方格子沿固定体对角线划成六个四面体,再根据四个顶点的正负号提取零集合。这是 marching tetrahedra,不是 marching cubes 的 256 个立方体案例表。相邻格子保持同一划分规则,避免共享面的划分不一致。
对符号不同的一条边,端点为 p₀、p₁,函数值为 f₀、f₁,线性插值的零点为:
一个四面体若只有一个顶点在内部,三个过零边形成一个三角形;两内两外产生一个四边形,再按固定连接切成两个三角形。全内或全外不产生面。球 SDF 沿一般边并非线性,所以这个插值点通常不在真实球面上。
代码以全局格点边编号复用顶点,零值端点另用单格点键;三角形绕序根据四面体内线性场梯度确定。球网格逐边检查恰有两面使用、方向相反,并检查 V−E+F=2。三角形数量正确不保证闭合;这三项检查能发现重复边、孔洞或反向面。
网格随后转为已有 Primitive,交给已有 BVH 查询。提取器没有复制相机、着色器或另一整套加速器。
固定同一批射线再比较
图像为 128×128 正交样本,世界 Y 向上,文件行序向下;射线从 z=3 沿 −Z 发出,t∈[0,6]。XY 范围半宽 1.25,固定样本相位偏移为 (0.00317,0.00611)。两种表示使用同一批射线,没有随机数。
对球投影内部 x²+y²≤1,前表面的独立参考为:
参考与第 17 篇二次方程求交的最大差约 1.29×10⁻¹⁴。参考命中 8,229 个像素。法线 RGB 是诊断编码 0.5+0.5n,不是光照或 sRGB 反射率;误差另由浮点深度检查,不能靠法线图接近来接受几何。
| 每轴格数 | 三角形 | 标量数据字节 | 顶点与索引字节 | 掩码不同像素 | 共同命中深度 RMSE |
|---|---|---|---|---|---|
| 12 | 2520 | 17576 | 60528 | 53 | 0.0193005 |
| 24 | 10416 | 125000 | 250032 | 10 | 0.00527486 |
| 48 | 41400 | 941192 | 993648 | 1 | 0.00148203 |
顶点与索引按每顶点三个 double、每三角形三个 uint32 计算。这是有效载荷,不包含 vector 容量、map 节点、BVH、分配器和进程内存。隐式公式只有少量参数,但查询也需要代码、射线与暂存状态,不能把公式字符数当作完整运行内存。
三组网格都闭合,顶点最大径向误差依次约 0.014565、0.003693、0.000964;深度最大误差依次约 0.127949、0.058250、0.035117。轮廓附近入射角接近切线,较小径向误差可能对应较大的沿射线交点差,所以最大深度误差下降慢于总体 RMSE。
对 sphere tracing 固定 ε=10⁻⁶,仅改变每条射线的最大场查询次数:
| 预算 | 已命中 | 已判无交点 | 未收敛 | 未收敛中的参考命中 | 总场查询 |
|---|---|---|---|---|---|
| 16 | 5583 | 6865 | 3936 | 2646 | 181234 |
| 64 | 8046 | 8072 | 266 | 183 | 234937 |
| 256 | 8226 | 8146 | 12 | 3 | 246215 |
三组没有跨入负值,没有假命中,也没有已判无交点却实际命中的像素。256 次仍有 12 条未收敛,其中 3 条实际与球相交。靠近轮廓时,距离下降很慢;在球外擦过的射线也可能长时间贴近表面,再离开。
256 预算的已收敛共同命中最大深度误差约 3.35×10⁻⁵,RMSE 约 2.29×10⁻⁶。这不包括三个尚未得到交点的样本。距离残差 ε 约束到曲面的最近距离,不能一般地推出沿任意射线的深度误差≤ε。
共享边求交异常也属于观察结果
最初使用没有相位偏移的像素中心射线。12、24、48 格分别有 5、5、9 个样本跳过前面的共享边并命中后表面,最大深度差接近 1.992。网格的成对边、Euler 特征数和绕序检查都通过;逐三角形的独立 long double 求交找到了前表面。
这些样本说明旧 double 三角形求交核对共享边的浮点舍入不够稳健。换成暴力查询仍使用同一个核,不能构成独立证据。异常 CSV 保存像素、生产命中面、解析前交点和独立高精度面编号,原始失败运行也保留。
正式比较固定一个非对齐相位,以测量表示误差,未修改共享求交 API。这个选择没有修复共享边问题,也没有证明任意相机都正确。原中心相位的 256 次距离场查询另有 16 条未收敛,全部是解析不相交样本;它们与网格背面异常是两类问题。
非均匀缩放为什么不能直接推进
将单位球沿 X 压成半轴 0.5,其他半轴保持 1,变换 A=diag(0.5,1,1)。f(A⁻¹p) 的零集合确实是目标椭球,但它的数值仍按原空间的度量计算。
点 p=(1,0,0) 到椭球最近点 (0.5,0,0) 的距离为 0.5,原函数给出 1。沿 −X 推进 1 已经不安全。均匀缩放 s 时,s f(p/s) 是真实距离;各轴不同比例不能只挑一个平均尺度乘回去。
最小奇异值 σ_min(A) 给出外部安全下界:
理由是任意向量 q 都满足 ‖Aq‖≥σ_min‖q‖,再对原曲面上的点取距离下确界。当前对角变换的 σ_min=0.5。一般位置的 b 可能严格小于真实距离,因此查询次数增加;它不因此变成椭球真实 SDF。
从 (1,0.8,0) 沿 −X 发射,解析进入 t=0.7。安全下界用 19 次查询得到 t≈0.699999984;未修正函数第一步到 t≈1.154066,进入内部并报告 overshoot。从 (2,0,0) 发射,真实进入 t=1.5;安全版本两次查询命中,错误版本越过整个物体,最终报告 miss。只检查是否出现负值,无法捕捉后一个错误。
比格距更薄的结构会消失
薄板占据 z∈[0.1,0.15],其 SDF 为 |z−0.125|−0.025。格距 h=0.25、z 起点为 0 时,采样层 z=0 与 0.25 的值都为 0.1;两层之间却有负值。四面体顶点全正,提取结果为零个三角形。
同一 128² 批垂直射线真实都应命中 z=0.15。粗相位输出 0 次命中;把格点起点移到 −0.125 后,中间层采到负值,输出 256 面、16,384 次命中。起点不变而格距减半为 0.125,输出 1,024 面、16,384 次命中。两种恢复的最大深度差约 1.11×10⁻¹⁶,因为板面处的分段距离在这些边上恰好线性。
此处的板无限延伸,提取网格只覆盖有限 XY 范围,测试射线落在网格内部。网格的外周不是完整物体的封闭边界,不能套用球的闭合检查。
一次细化能恢复当前板的位置,不保证任意薄结构都能恢复。符号采样没有见到内部时,插值算法无法恢复未提供的信息。需要提高分辨率、利用场的距离下界自适应细化或直接查询隐式场;把提取后的表面做细分,不能凭空产生消失的薄板。
练习与自检
练习一。球半径从 1 均匀放大到 3,点 (5,0,0) 的函数值和真实距离怎样计算?若仅计算 ‖p/3‖−1,会有什么步长差异?
答案:d=3(‖p/3‖−1)=2。未乘回去得到 2/3,是外部距离下界,步长保守但较慢;当均匀缩小到 s<1 而仍不乘 s,则可能高估距离而越界。
练习二。厚度 w=0.04 的板中心位于 z=0.1,格距 0.2、起点 0。端点符号如何?细化已有提取网格能否恢复它?
答案:端点 d(0)=d(0.2)=0.08,均正,不产生过零边。网格为空,表面细分没有输入面。重新采样到内部或使用有可靠界的自适应策略,才增加必要信息。
练习三。某批射线有 100 条,90 条收敛且最大深度误差 10⁻⁶,另外 10 条耗尽预算。是否可以报告所有射线精度 10⁻⁶?
解题要点:只能报告共同收敛集合的误差,并列出 10 条未知状态。若解析参考中有真实命中,需额外报告未收敛中的命中数;不能把它们当作正确背景。
复跑与出处
在 examples/computer-graphics/ 中运行:
1 | |
程序输出实际网格顶点/面 CSV、逐射线状态、变换步进、薄板采样、JSON 和 PPM。首次报告保存在本文素材目录:完整数值、球逐像素结果、共享边异常。配套源码为 implicitE01.hpp 与 implicitE01_check.cpp;真实命令、独立复算和文件 SHA 见 writing-plans/computer-graphics/evidence/E01-cpu.txt。没有 GPU 执行或性能测量,场查询次数不是耗时。
- Hart:Sphere Tracing,Definition 1、Definition 3、Theorem 1,以及 Uniform Scaling、Linear Deformation:真实距离、距离下界和安全推进条件。
- Lorensen、Cline 1987:Marching Cubes,§4、印刷页 164–165:标量符号与边插值。本文用固定四面体划分,不声称实现论文的立方体案例表。
系列入口 · 上一篇:SAH 分割 · 下一篇:透明叠面与历史颜色。






