第 20 章练习:SDF 变换与组合

对应教程:20 SDF 的变换与组合
起始示例:变换与组合对照,需要修改时使用示例源码。

练习 1:只凭符号预测组合结果

对三个采样点,已知 (dA,dB) 分别为 P=(-0.12,0.08)、Q=(-0.05,-0.04)、R=(0.07,-0.03)。

**目标:**分别算出并集 min(dA,dB)、交集 max(dA,dB)、差集 max(dA,-dB) 的值,并判断每点是否在结果内部。

**完成标准:**Q 对并集和交集在内部,却对差集在外部;能解释数值的正负与逻辑条件相对应,但数值大小不一定是组合后到边界的精确距离。

练习 2:让 B 绕 A 的中心公转

**目标:**让 B 矩形的中心围绕 A 圆的中心 (-0.17,0) 随角度控件旋转,同时 B 本身仍按相同角度旋转。角度为 0° 时,B 的中心应仍等于控件给出的 uCenterB。

**要求:**保持 sdBox 不变;先计算旋转后的 B 中心,再对查询点做第 20 章的逆变换。等比缩放的距离乘回系数仍需保留。

**完成标准:**改变角度时,B 中心沿 A 周围移动,蓝色 B 原轮廓与金色组合边界同步改变;仅 B 模式也可观察到中心位置变化。

练习 3:新增对称差组合

**目标:**加入“A 与 B 只属于其中一个”的对称差模式。A、B 重叠处应挖空,两者各自独有的部分保留。

**要求:**复用已有 dA、dB,先分别构造 A \ B 和 B \ A,再取两者并集;不要只根据颜色遮罩拼接。

**完成标准:**单独处于 A 或 B 的区域为内部,重叠区域及两者外部都为外部。切换到等距线后能指出组合场的距离幅值不保证处处精确。

参考解答


<a id=“reference-answers”></a>

第 20 章练习参考解答

对应练习:SDF 变换与组合。

练习 1:只凭符号预测组合结果

点 dA dB 并集 min 交集 max 差集 max(dA,-dB)
P -0.12 0.08 -0.12,内 0.08,外 -0.08,内
Q -0.05 -0.04 -0.05,内 -0.04,内 0.04,外
R 0.07 -0.03 -0.03,内 0.07,外 0.07,外

Q 同时属于 A、B,所以并集与交集包含 Q;差集把 B 的内部剔除。表中数值由表达式计算得到,但组合中某些原边界会被其他形状遮住,返回值的绝对值不一定等于到结果边界的真实距离。

练习 2:让 B 绕 A 的中心公转

在片元 shader 的 main() 中,把原来的 localB 两行替换为:

// 片元阶段;uCenterB 是角度为 0 时的 B 中心。
vec2 centerA = vec2( -0.17, 0.0 );
vec2 centerB = centerA + rotate2D( uAngle ) * ( uCenterB - centerA );
vec2 localB = rotate2D( -uAngle ) * ( p - centerB ) / uScale;
float dB = sdBox( localB, vec2( 0.23, 0.17 ) ) * uScale;

uCenterB-centerA 是从 A 指向 B 的初始位移;把这段位移旋转后加回 A 中心,得到公转轨道上的新中心。随后再把当前查询点减新中心、反向旋转,保证 B 自身也转动。uAngle=0 时旋转矩阵是单位矩阵,新中心回到 uCenterB。若只旋转 p 却不旋转中心位移,得到的不是绕 A 公转。

练习 3:新增对称差组合

在 HTML 的 #operation 下拉框追加 <option value="6">对称差</option>。把片元 shader 形状选择末尾的平滑并集 else 改为以下两个分支,前面的模式 0~4 保持不变:

// 片元阶段;dA、dB 已在同一二维坐标中计算。
else if ( uOperation == 5 ) {
  d = smoothUnion( dA, dB, uBlend );
} else {
  float onlyA = max( dA, -dB );
  float onlyB = max( dB, -dA );
  d = min( onlyA, onlyB );
}

这里的 onlyA 是 A \ B,onlyB 是 B \ A;min 把两者取并集。原有 JavaScript 已把下拉框值作为 uOperation 传给材质,添加选项即可使用模式 6。重叠区域中两个差集都为正,所以结果为正;只在一个形状内时恰有一个差集为负,结果为负。布尔场给出正确符号和边界,但仍需按正文所述判断距离幅值是否精确。