top of page

SpongeBob Characters with Signed Distance Fields

Updated: Sep 11, 2024

Signed Distance Fields (SDFs) allow us to create shapes with equations. Here, we'll see how. (plus, how I used them to make SpongeBob's Hash Slinging Slasher).


Reference Art

Typically, explicit geometry tells us exact points representing geometry, and where they're located.


  1. Explicit Equation: The explicit equation for a sphere is:

This equation explicitly defines the relationship between the coordinates x, y, and z of points on the sphere and the radius r.


2. Implicit Equation: The implicit equation for a sphere is the same as the explicit equation:


This equation describes the sphere as the set of points (x, y, z) that satisfy the equation f(x, y, z)=0.


3. Parametric Equations: Parametric equations represent the coordinates of points on the sphere in terms of one or more parameters. For a sphere, we typically use spherical coordinates (θ,ϕ), where θ represents the azimuthal angle (longitude) and ϕ represents the polar angle (latitude). The parametric equations are:


These equations completely describe a sphere in three-dimensional space.


EXPLICIT VS IMPLICIT


This article primarily focuses on the difference between explicit and implicit shapes.


Explicit: 'Here are the points within the shape.'

Implicit: 'Here is the equation for the shape. What points satisfy this equation?'


In terms of rendering, explicit geometry tells us exactly where vertices are located-- we can render what we are told. Meanwhile, implicit geometry tells us the shape's equation, and it's up to us to find points that satisfy that equation so we can render them.


  1. Explicit Geometry:

  2. In the context of vertex shaders, explicit geometry refers to defining each vertex of the geometry explicitly.

  3. For a sphere, this means calculating the position of each vertex individually based on the sphere's mathematical equations (like the parametric equations mentioned earlier).

  4. Each vertex is processed separately in the vertex shader, and its position is determined directly.

  5. Explicit geometry offers precise control over the shape and appearance of the object but can be computationally expensive, especially for complex shapes with many vertices.

  6. Implicit Geometry:

  7. Implicit geometry, on the other hand, involves defining the shape of an object implicitly using mathematical functions or equations.

  8. In the context of a sphere, implicit geometry might involve using a procedural or mathematical function to determine whether a given point in space is inside or outside the sphere.

  9. In the vertex shader, instead of explicitly calculating the position of each vertex, a function is used to determine properties like position, normal, and color based on its relationship to the implicit surface.

  10. This approach can be more efficient for certain shapes or effects because it can generate complex geometry on the fly without explicitly defining every vertex.

  11. However, it may be more challenging to control the precise shape and appearance of the object compared to explicit geometry.



Raymarching with SDFs


Suppose we have a scene where a shape's equation is defined.


In order to find points that solve our shape's equation, we cast a ray into the scene and test for hits at each step. (aka - does my current point solve the equation?)


We write our code in a fragment shader, which runs on a per-pixel basis.


Fragment Shader

March along ray ---> Check For Intersection ---> Compute Surface Color ---> Return Color.


Now, since each pixel runs its own instance of the fragment shader, the combined result of ALL fragment shaders is a completed image. Let's walk through the steps...


March along ray:

Our fragment shader has a ray. This ray has a view vector (aka - where it's pointing). For each iteration, we advance slightly along this view vector, (deeper into the scene). After each step, we check for an intersection - i.e. have we hit the shape?


An optimization: Instead of advancing by a fixed step size, compute the distance, X, of the closest surface point. This point may or may not be our intersection point. If so, we hit the shape, or we pass by another surface point on our way. But fortunately, we will never pass our intersection point! That looks like this:

Check for Intersection:

Our current position along our ray can be represented by (x, y, z) coordinates. If we plug these points into our equation, what do we get?


Note that an SDF is a special type of Implicit equation which returns the following:

  • Inside the shape: ( -1 )

  • Perfect Hit, ON the shape ( 0 )

  • Outside the shape: ( +1 )


(So while every SDF is an implicit equation, not every implicit equation is an SDF.) We can think of an SDF as a yes or no as to whether we've actually hit our shape.


Compute Color:

We've hit our shape! Now that our SDF has returned 0, we can plug our current (x, y, z) coordinates into computation of our surface color. We can implement adjusted versions of our SDFs that return a color instead of yes/no, telling us instead what albedo and material properties exist at that point. We then return this color as the final output of our fragment shader.



Note that we can combine equations and functions to create not just single shapes, but entire scenes, (like this one I made in ShaderToy).



While a bit more painstaking than explicit geometry - (you have to adjust your equations, then run the code to see if your output looks correct) - you can still create some pretty cool scenes. If anything, it's a testament to all that you can do with math.


Now, for the Hash Slinging Slasher.


I chose this character mainly because it's a unique/challenging combination of shapes. For mathematical reference, a good baseline of SDF functions can be found at https://iquilezles.org/articles/distfunctions/.


You'll notice some important/challenging features like a large nose, large nostrils, glasses, protruding teeth, purple sweater, fins, an asymmetrical pose, and... a spatula. (As opposed to creating a symmetrical/mirrored image, I wanted to capture the asymmetry of this frame.)


A summary of the base components as follows:


  • Body - Rounded Cylinder + Capped Cylinder

  • Arms - Capped Torus

  • Legs - Cylinders

  • Feet - Flattened Ellipsoid

  • Sweater - Cylinders + Capped Torus

  • Teeth - Rectangular Prisms

  • Eyes - Spheres (+ smaller spheres)

  • Glasses - Torus + Capsules

  • Back Fins - Rhombus

  • Spatula - Cylinders + Prisms (with smaller prisms subtracted)

  • Eyebrows - Capped Torus


For blending these components, I used various degrees of smooth blending functions to interpolate between 2 given shapes at each step. (As a general rule, I used more interpolation for pieces intended to be molded together, such as the head and torso, and minimal interpolation for clear-cut boundaries, such as the holes in the spatula.)


With SDFs, each function must also map to a corresponding color function with the same transformations as the associated shape. For example, this ensures that we have a mathematical definition of not only where the eye shape starts, but also where the eye color starts. If we define these functions incorrectly, we'd essentially end up with a muddy map of colors that don't lay on the shapes correctly.


Another reason to choose this particular character is that under the cover of darkness, he appears to be rather sinister. This gave me the opportunity to create another set of color functions, mapped to the same shapes, and toggle "night mode."


Night mode + Krusty Krab environment map

You might also notice that there's an outline on the edges, giving the model a cartoon look. This is a filter I added to the shader to mimic SpongeBob's art style.


Toon Shader: If the normal and view vector are within ~10 degrees of perpendicularity, I set the color to be a %-darker hue. This gives us the impression of a darker "outline" on the shape.


Here it is on the original, too:


Finally, dividing the region and applying replication to our model, we can create some pretty interesting layouts.



And if we apply mathematical rotations, translations as a function of time...



Comments


bottom of page