第 19 章练习:基础二维 SDF 图形

对应教程:19 基础二维 SDF 图形
起始示例:圆、矩形、圆角矩形和胶囊形,需要修改时使用示例源码。

练习 1:矩形角外为什么用二维长度

矩形半尺寸为 b=(0.40,0.25),使用教程中的 sdBox。

**目标:**计算 p=(0,0)、p=(0.50,0) 和 p=(0.50,0.35) 的有符号距离。对角外点另算 max(q.x,q.y),比较它与真正距离。

**完成标准:**能说明中心为何为负,角外为何需要 length(max(q,0)),以及近似公式怎样低估角外距离。

练习 2:用已有圆距离做圆环

**目标:**给第 19 章示例新增“圆环”形状。把 uRadius 当作圆环中心线半径,把现有圆角控件的数值当作圆环的半宽;圆环中心空洞应为形状外部。

**要求:**先使用 sdCircle 的有符号距离,再用它到零边界的距离构造新 SDF;为避免内半径消失,把半宽限制在小于 uRadius 的范围。修改控件文字,使它在圆环模式下也有意义。

**完成标准:**填充模式下出现中间透明于形状色的孔;符号与等距线模式中,内外两条边界都显示为 d=0,孔中央为正值。

练习 3:把胶囊形改成竖向

**目标:**在形状列表新增“竖向胶囊形”。复用已有 sdCapsule,使线段端点为 (0,-uHalfSize.y) 与 (0,uHalfSize.y),半径仍由 uRadius 控制。

**要求:**不要通过拉伸水平胶囊的最终距离值实现;使用原函数对真正的竖向线段求最近点。

**完成标准:**图形的总宽为 2*uRadius、总高为 2*(uHalfSize.y+uRadius);端部等距线仍呈圆弧,修改矩形半高控件会改变中间直段长度。

参考解答


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

第 19 章练习参考解答

对应练习:基础二维 SDF 图形。

练习 1:矩形角外为什么用二维长度

b=(0.40,0.25)。中心 p=(0,0) 时 q=(-0.40,-0.25),外部长度项为 0,内部项为 min(max(-0.40,-0.25),0)=-0.25。p=(0.50,0) 时 q=(0.10,-0.25),距离为 0.10。角外 p=(0.50,0.35) 时 q=(0.10,0.10),距离为 √(0.10²+0.10²)≈0.1414;只用 max(q.x,q.y) 会得到 0.10。角外到最近角点的位移同时包含 x、y 两个分量,必须取二维长度。

练习 2:用已有圆距离做圆环

在 HTML 的 #shape 加 <option value="4">圆环</option>,把圆角控件文字改为“圆角半径 / 圆环半宽”。在片元 shader 的形状选择中,把原胶囊形的最终 } else { ... } 整块替换为:

// 片元阶段;sdCircle 返回到中心线圆周的有符号距离。
} else if ( uShape == 4 ) {
  float halfWidth = min( uCorner, uRadius * 0.8 );
  d = abs( sdCircle( p, uRadius ) ) - halfWidth;
} else {
  vec2 a = vec2( -uHalfSize.x, 0.0 );
  vec2 b = vec2( uHalfSize.x, 0.0 );
  d = sdCapsule( p, a, b, uRadius );
}

新分支接在 uShape == 2 的圆角矩形分支后,胶囊形仍由最终的 else 处理。圆环的两条零边界分别在半径 uRadius-halfWidth 与 uRadius+halfWidth;两者之间的 d 为负。中心点 d=uRadius-halfWidth>0,所以孔显示外部色。若半宽等于零,圆环退化为一条零宽圆线;调大控件可看见实体圆环。保留原有的填充、等距线和描边显示代码,无需为圆环重写颜色。

练习 3:把胶囊形改成竖向

在 HTML 的 #shape 加 <option value="5">竖向胶囊形</option>。若已经完成练习 2,可将形状分支的末尾写成:

} else if ( uShape == 4 ) {
  float halfWidth = min( uCorner, uRadius * 0.8 );
  d = abs( sdCircle( p, uRadius ) ) - halfWidth;
} else if ( uShape == 5 ) {
  vec2 a = vec2( 0.0, -uHalfSize.y );
  vec2 b = vec2( 0.0, uHalfSize.y );
  d = sdCapsule( p, a, b, uRadius );
} else {
  vec2 a = vec2( -uHalfSize.x, 0.0 );
  vec2 b = vec2( uHalfSize.x, 0.0 );
  d = sdCapsule( p, a, b, uRadius );
}

此代码替换原示例从圆角矩形之后到原胶囊形 else 结束的部分;前面的圆、矩形、圆角矩形分支保持不变。如果只做练习 3,删去 uShape == 4 分支即可。竖向线段长度为 2*uHalfSize.y,上下各扩张半径 uRadius,所以总高为 2*(uHalfSize.y+uRadius)、总宽为 2*uRadius。当 uHalfSize.y 改变时,中间直段变长或变短,半圆端部半径不变。