::     ::

Intro

This is a list of 2D SDF primitives in L-norm. While the L-norm produce very different distance fields to regular Euclidean SDFs, they are still fields and can be used to do raymarching, rasterization or collision detection. And just like Euclidean metric, the L-norm has its own set of advantages and disadvantages when realing with SDFs.

For example, L-norm work great or axis aligned algorithms, such as acceleration structure traversal (grids, or AABB trees or space partitioning). They are also able to represent many SDFs with simpler equations than the Euclidean metric, or even provide closed form analytic solutions where one doesn't exist in Euclidean metrix. For example, Euclidean SDFs of ellipses and quadratic bezier curves need solving cubic equations, which can be slow and unstable, but in L-norm they are simple quadraic equations. In fact, in genreal, any shape that can be raycasted accepts an exact SDF in L-norm even when it might not have an analytic Euclidean SDF at all; and the computiation usually boils down to a combination of bounding box checks and one or two simple raycasts in the (±1,±1) directions.

On the other hand, the biggest drawback to L-norm SDFs is that rotations of primitives cannot be trivially computed, and require complicated implementations, as far as I can tell. One can often rotate the primitive and apply a distance correction factor of max(abs(cos(α)+sin(α)), abs(cos(α)-sin(α))), but the handling of the rotated primitive's bounding box in the general case seems complicated from what I've seen so far. Luckily, scaling and translating work just fine.

Anyway, here is the list of primitives I've derived so far. It did the work on a single night of obsesive play (including borrowing an idea from Shadertoy user "oneshade" for the max(p,(p-r).yx) trick). So the list is not exhaustive and some of the implementations can definitely be improved (the segment and arc SDFs in particular). When I do another such math-night session I'll hopefully improve them and update the reference code here. Regardless, you can find all of the primitives below in this Shadertoy playlist too: https://www.shadertoy.com/playlist/XXccDH.

Primitives

float sdBox( in vec2 p, in vec2 rad ) { p = abs(p)-rad; return max(p.x,p.y); }

float sdCircle( in vec2 p, in float r ) { p = abs(p); p = max(p,p.yx-r); float a = 0.5*(p.x+p.y); float b = 0.5*(p.x-p.y); return a - sqrt(r*r*0.5-b*b); }

float sdEllipse( in vec2 p, in vec2 r ) { p = abs(p); p = max(p,(p-r).yx); float m = dot(r,r); float d = p.y-p.x; return p.x - (r.y*sqrt(m-d*d)-r.x*d)*r.x/m; }

float sdLine( in vec2 p, vec2 a, vec2 b ) { vec2 pa = p-a, ba = b-a; float s = (ba.x*ba.y>0.0)?1.0:-1.0; float h = clamp( (pa.y+s*pa.x)/(ba.y+s*ba.x), 0.0, 1.0 ); vec2 q = abs(pa-h*ba); return max(q.x,q.y); }

float sdRoundBox( in vec2 p, in vec2 ra, in float rb ) { p = abs(p) - ra; p = max(p,p.yx-rb); float a = 0.5*(p.x+p.y); float b = 0.5*(p.x-p.y); return a - sqrt(rb*rb*0.5-b*b); }