第 4 课:形状与图案(SDF)

返回教程目录 · 打开本课练习

一个数决定一个形状

按钮底板、选中框、图标轮廓和程序化花纹,经常需要随着大小和颜色变化而不换图片。带符号距离场把每个位置映射为一个数 d:d < 0 在形状内,d = 0 在边界,d > 0 在形状外。片元着色器只要知道当前片元的 vUv,就能给每个片元算出 d,再决定颜色。

本课沿用第 3 课的 UV:平面左下 (0,0),右上 (1,1)。p = vUv - 0.5 将原点移到画面中心。本课预览区域为正方形,因此 UV 的 x/y 相同长度在屏幕上占相同像素。若图案贴到长方形物体上,要按物体表面的实际宽高调整距离;若图案覆盖全屏宽画布,可按第 3 课的方法修正画布比例。

从距离到可见颜色

正方形的近似带符号距离可写:

vec2 p = vUv - vec2(0.5);
float d = max(abs(p.x), abs(p.y)) - 0.28;
float fill = 1.0 - smoothstep(-0.004, 0.004, d);
vec3 color = mix(background, foreground, fill);

max(abs(x),abs(y)) 是到中心的切比雪夫距离。边界是 d=0,所以半宽为 0.28。smoothstep 把边界附近的数值变成 0–1 遮罩;过渡区 0.008 UV 在 560 像素的正方形预览中约为 4.5 像素。它是固定 UV 宽度的柔边,不是真正按像素自动调整的抗锯齿。之后可用屏幕导数 fwidth(d) 得到更稳定的像素级边宽。

若需要外描边,可在 d 接近 0 的一小段范围内单独着色,例如 1.0 - smoothstep(0.01,0.02,abs(d))。改变 d 的常数项会整体扩张或收缩形状,这比手动改每个顶点容易。

形状的组合公式

圆角矩形先把坐标折到第一象限:q = abs(p) - (halfSize - radius)。length(max(q,0.0)) 算角外的圆弧距离,min(max(q.x,q.y),0.0) 补上内侧的负距离,最后减去圆角半径。半径增大,角变圆;如果半径大于半尺寸,几何意义会改变,因此普通圆角矩形应限制它。

两个距离场可以组合:min(dA,dB) 表示并集,max(dA,dB) 表示交集,max(dA,-dB) 表示从 A 中挖去 B。第 3 题用横竖两条矩形的 min 组成十字,第 8 题用两个圆和一个倒三角组成爱心。这里的正负号关系决定内部和外部:如果组合后颜色反了,先把 d 输出为灰度或临时画出 d<0 的区域,再检查 min/max。

第 7 题使用三条边的有向半平面。每条边在内部返回负值,max 要求三条约束都成立,因此得到三角形。这个函数的零等值线与内部判断正确,但在顶点外侧的数值不是精确欧氏最短距离。需要严格等宽的远距离描边或形态运算时,应换成精确多边形 SDF;本课只用它判断填充和近边缘柔化。

重复、曲线与鼠标选择

fract(vUv*5.0) 把整张 UV 重新折回每个格子的 0–1 范围,局部再减去 0.5,即可在 5×5 格子中心放圆点。floor(vUv*5.0) 给出格子的行列编号,能让半径或颜色按行列变化。fract 用于格内位置,floor 用于格子身份;把二者搞反,往往会得到只有一颗圆点或每个格子完全相同的结果。

正弦波线用 abs(vUv.y - curveY) 表示当前片元与曲线的竖直差值。它足够绘制细线,但当曲线倾斜很大时,并不等于到曲线的最短距离。若需要沿曲线严格等宽的描边,要计算真正的最近点或使用几何线段。

第 9 题在 JS 中把指针 X 坐标分成八段,传给 uShape。这是一个 uniform,因此一次 draw call 内所有片元选择同一个图案。少量静态分支便于初学者读懂;如果日后有几十种复杂图案,分支、着色器体积和移动端性能都值得测量,也可以拆成多个材质或预先生成图案纹理。

数据如何到达屏幕

main.js 建立一个平面。Three.js 把每顶点的 position 与 uv attribute 送入顶点 Shader;顶点 Shader 负责位置变换,并把 uv 放进 vUv。光栅化阶段在平面内部插值 vUv。片元 Shader 对每个片元计算形状距离、遮罩和颜色。只有第 9 题需要 JS 根据指针更新 uShape;前八题的公式完全写在片元 Shader 中。颜色最终由 Three.js 转到输出颜色空间。

常规 UI 如果只要一个静态圆角矩形,CSS 往往更简单;需要把图案贴到 3D 物体上、连续变形、和其他材质效果组合时,SDF 更灵活。避免在渲染循环里新建材质或几何体;本课只更新 uniform,公共运行时在页面离开时释放资源。

常见错误与验证

练习

难度 题目 可见验收
⭐ 中心正方形 居中的方形
⭐⭐ 圆角矩形距离场 可调圆角
⭐⭐ Min 组合十字 横竖矩形并集
⭐⭐ 正弦波距离线 一条起伏波浪线
⭐⭐⭐ Fract 重复 5×5 圆点 25 颗等大圆点
⭐⭐⭐ 行列变化的圆点半径 圆点大小各异
⭐⭐⭐ 三个半平面构成三角形 正确的三角形内外判断
⭐⭐⭐⭐ 圆与三角形组合爱心 爱心轮廓
⭐⭐⭐⭐ 指针切换八种形状 指针左右移动切换图案