SDFs

03.28.2024
BLOG

I’m having a realization: When working with SDFs, you at some point convert from an SDF to a mask. It’s often implicit, but the second you do it, you can no longer perform operations across that boundary. ie. you cannot do mask - sdf and expect to get a mask or an sdf - it’ll end up being a weird combination of the two (that may or may not be useful to you).

Example

Say I want to draw a rectangle’s border. Drawing a rectangle is easy:

float
sdf_box(vec2 pos, vec2 center, vec2 half_dim)
{
  vec2 p  = pos - center;
  vec2 pp = abs(p) - half_dim;
  float d = length(max(pp, 0.0)) + min(max(pp.x, pp.y), 0.0);  
  return d;
}

void 
mainImage( out vec4 fragColor, in vec2 fragCoord)
{
  vec2 uv = fragCoord/iResolution.x;
  float d = sdf_box(fragCoord, vec2(600, 325), vec2(100, 100));
  if (d < 0) fragColor = vec4(1.0);
  fragColor = vec4(0.0,0.0,0.0,1.0);
}

d in this case, is an sdf - that is, d contains a value that denotes a distance from the edge of the box, with negative values being inside the box and positive values being outside.

We can turn d into a mask by inverting and clamping it. ie.

float box_mask = clamp(-1 * d, 0.0, 1.0);
fragColor = float4(box_mask, box_mask, box_mask, 1.0);

Now I can use this information to more easily construct my box outline.

void 
mainImage( out vec4 fragColor, in vec2 fragCoord)
{
  vec2 c = vec2(600, 325);
  vec2 hd = vec2(100,100);
  float ot = 10.0;
  float outer_d = sdf_box(fragCoord, c, hd);
  float outer_box_mask = clamp(-1.0 * outer_d, 0.0, 1.0);
  float inner_d = sdf_box(fragCoord, c, hd - vec2(ot,ot));
  float inner_box_mask = clamp(-1.0 * inner_d, 0.0, 1.0);
  float outline_mask = outer_box_mask - inner_box_mask;
  fragColor = vec4(outline_mask, outline_mask, outline_mask, 1.0);
}