Recently I created a sketch using a flow field based on an SDF. As far as I know, SDFs aren’t typically used to define a vector field, and I had never calculated the gradient of an unknown vector field before. Typically, when I’m doing a sketch with a flow field, the vector field is defined by some spatial function. In this case, the field was static but unknown, and it was a fun mental exercise to figure out how I could calculate the gradient efficiently. I wanted to share my process!
This is the sketch I created. Note that the middle of the sketch mixes the gradient with a curl noise function; the SDF gradient isn’t that curvy!
If you prefer to read code, you can find a detailed breakdown of my algorithm in my generative art repo. For everyone else, read ahead! If you aren’t familiar with SDFs (like me a week ago), I’d highly recommend checking out Piter’s explanation in this YouTube video. The first 10 minutes or so cover the core ideas.
My goal was to be able to calculate the gradient defined by the SDF at any point in space. First a little terminology that I use:
pointrefers to a 2D point in Cartesian space, i.e. an
(x, y)coordinate pair
SDF valuerefers to the value returned from my SDF function for a given
point. It doesn’t really matter how the SDF is defined, other than it must return a single scalar number.
SDF differencerefers to the difference between the
SDF valueat some sample point and the
SDF valueat my target
sample radiusrefers to the radius of the circle from which we sample the SDF around our
point. Sampling from a circle rather than random points around our target
Here was my process:
- Sample the SDF value at three equally-spaced points around the
- Calculate the SDF differences between the sample SDF values and the SDF value at
- Interpolate between the two highest values to find the angle at which the difference is the greatest. The angle at which the SDF difference is greatest indicates the gradient, because this is the maximum difference between the
pointand the SDF values around the point.
There are a few important insights that make this algorithm work:
- The maximum difference should be our “sample radius”. In a simple example, we can take our sample points from the unit circle around
point, in which case our sample radius is
- We also know the SDF value is a simple measurement of distance. Therefore, the max difference should be equal to the radius of the circle from which the sample points were taken (which, in our example, is
- An SDF measures the distance from a
pointto a shape. Therefore, the SDF values become larger as you move away from the shape defined by the SDF, and smaller as you move towards the shape. This means the SDF difference will be negative when the SDF value is larger than the SDF value at
point. (Example: if SDF at
pointis 0.5 and SDF of our sample point is 0.75, then the diff would be -0.25.) Therefore, the gradient moving towards the shape will always be positive, because this will indicate the direction of decreasing SDF values.
Given this process overview and the insights, we can define our actual algorithm:
The detailed algorithm is as follows:
- Define the radius of the circle from which we will sample the SDF. (Let’s use
- Define the three angles at which we will sample the SDF. (Let’s use
- Sample the SDF at the three angles, and calculate the difference between the raw SDF value and the SDF value at
point. (The SDF difference should be in range [-1, 1]).
- Discard the lowest SDF difference. This indicates the point that is furthest away from the shape. The higher two values will be used to interpolate the gradient.
- Define the rate of change of the SDF diff in “units per radian”. Using a radius of 1, the rate of change will be
2/πbecause the max and min will occur on opposite sides of the circle. The difference between max and min is 2, and that change takes place in half a rotation, so the rate of change on our circle is
2/π. The units of this value are “SDF difference units per radian”.
- Choose one of the two remaining sample points, and calculate the SDF difference between the sample point and the maximum (aka the radius).
- Calculate the difference in radians between the sample point and the “maximum” point (i.e. gradient). This is done by dividing the SDF difference by the rate of change. Dimensional analysis for this calculation gives us a result of “radians”, which is what we want.
- Calculate the absolute angle, in radians, of the gradient. Since we sampled from three equally-spaced and took the top two points, the maximum value will always lie in between these two points. I don’t have a mathematical proof for this but I’m pretty sure it’s correct. Please let me know if not!
- Return a normalized 2D vector pointing in the direction of the gradient.