26 lines
5.5 KiB
Markdown
26 lines
5.5 KiB
Markdown
# Signed Distance Function
|
|
|
|
Signed distance function (SDF, also signed distance field) is a [function](function.md) that for any point in space returns its distance to the closest point of some geometric shape, and also the information whether that point is outside or inside that shape (if inside, the distance is negative, outside it's positive and exactly on the surface it is zero -- hence *signed* distance function). SDFs find use in elegantly representing some surfaces and solving some problems, most notably in computer [graphics](graphics.md), e.g. for smoothly rendering upscaled [fonts](font.md), in [raymarching](raymarching.md), implementing [global illumination](gi.md), as a 3D model storage representation, for [collision detection](collision_detection.md) etc. SDFs can exist anywhere where [distances](distance.md) exist, i.e. in 2D, 3D, even non-[Euclidean](euclidean.md) spaces etc. (and also note the distance doesn't always have to be Euclidean distance, it can be anything satisfying the [axioms](axiom.md) of a distance metric, e.g. [taxicab distance](taxicab.md)).
|
|
|
|
Sometimes SDF is extended to also return additional information, e.g. the whole [vector](vector.md) to the closest surface point (i.e. not only the distance but also a direction towards it) which may be useful for specific algorithms.
|
|
|
|
**What is it all good for?** We can for example implement quite fast [raytracing](raytracing.md)-like rendering of environments for which we have a fast SDF. While traditional raytracing has to somehow test each ray for potential intersection against all 3D elements in the scene, which can be slow (and complicated), with SDF we can performs so called [raymarching](raymarching.md), i.e. iteratively stepping along the ray according to the distance function (which hints us on how big of a step we can make so that we can potentially quickly jump over big empty areas) until we get close enough to a surface which we interpret as an intersection -- if the SDF is fast, this approach may be pretty efficient ([Godot](godot.md) implemented this algorithm to render real-time global illumination and reflections even in GPUs that don't support accelerated raytracing). Programs for rendering 3D [fractals](fractal.md) (such as Mandelbulber) work on this principle as well. SDFs can also be used as a format for representing shapes such as [fonts](font.md) -- there exists a method (called multi-channel SDF) that stores font glyphs in bitmaps of quite low-resolution that can be rendered at arbitrary scale in a quality almost matching that of the traditional vector font representation -- the advantage over the traditional vector format is obviously greater simplicity and better compatibility with GPU hardware that's optimized for storing and handling bitmaps. Furthermore we can trivially increase or decrease weight (boldness) of a font represented by SDFs simply by adjusting the rendering distance threshold. SDFs can also be used for [collision detection](collision_detection.md) and many other things. One advantage of using SDFs is their **generality** -- if we have an SDF raymarching algorithm, we can plug in any shape and environment just by constructing its SDF, while with traditional raytracing we normally have to write many specialized algorithms for detecting intersections of rays with different kinds of shapes, i.e. we have many special cases to handle.
|
|
|
|
**How is an SDF implemented?** Well, it's a [function](function.md), it can be implemented however we wish and need, it depends on the use case, but we probably want it to be **fast** because algorithms that work with SDFs commonly call it often. SDF of simple mathematical shapes (and their possible combinations such as unions, see. e.g. [CSG](csg.md)), e.g. spheres, can be implemented very easily (SDF of a sphere = distance to the sphere center minus its radius); even the already mentioned 3D [fractals](fractal.md) have functions that can be used to quickly estimate the distance towards their surface. Other times -- e.g. where arbitrary shapes may appear -- the function may be [precomputed](precomputing.md) into some kind of N dimensional [array](array.md), we might say we use a precomputed [look up table](LUT.md). This can be done in a number of ways, but as a simple example we can imagine raymarching mirror reflections with which we can subdivide the 3D scene into a grid and into each cell we store the SDF value at its center point (which here may be computed by even a relatively slow algorithm), which will allow for relatively fast search of intersections of rays with the surface (at any point along the ray we may check the SDF value of the current cell which will likely provide information for how big a step we can make next).
|
|
|
|
```
|
|
. . . . . . . . 3 2 2 2 2 2 2 2
|
|
. . . . . . . . 3 2 1 1 1 1 1 1
|
|
. . . X X X X X 2 2 1 0 0 0 0 0
|
|
. . . X X X X X 2 1 1 0-1-1-1 0
|
|
. . X X X X X X 2 1 0 0-1-2-1 0
|
|
. . X X X X X X 2 1 0-1-1-2-1 0
|
|
. . X X X X X X 2 1 0-1-1-1-1 0
|
|
. . X X X X X X 2 1 0 0 0-1-1 0
|
|
. . . . X X X X 2 1 1 1 0 0 0 0
|
|
. . . . . . . . 2 2 2 1 1 1 1 1
|
|
```
|
|
|
|
*Shape (left) and its SDF (right, distances rounded to integers).*
|
|
|
|
SDFs in computer graphics were being explored a long time ago but seem to have start to become popular since around the year 2000 when Frisken [et al](et_al.md) used adaptive SDFs as an efficient representation for 3D models preserving fine details. In 2007 [Valve](valve.md) published a paper at [SIGGRAPH](siggraph.md) showing the bitmap representation of SDF shapes that they integrated into their [Source](source_engine.md) engine. |