Skip to content

Signed Distance Functions (SDFs) ​

Signed distance functions in general represent geometry by providing a function that e.g. maps a point in R³ onto the closest scalar distance to the surface of the geometry [29]. This representation technique has already been used extensively for ray tracing applications in the field of computer graphics. For a quick introduction into SDFs the website of Inigo Quilez is referred to.

In the field of ray-based optical simulations, mathematical surface definitions, e.g. in the form of analytical equations for the surface sag or NURBs, are common [24, p. 44 ff]. However, they are also costly to implement since no "one-size-fits-all" algorithm exists which could be implemented for the intersect3d function. This is why for this package, SDFs have been chosen to represent curved surfaces. This is due to the fact that for exact SDFs

  1. the point of intersection can be calculated exactly with reasonable convergence criteria

  2. the normal vector at the point of intersection can be calculated exactly using automatic differentiation

  3. the ray marching algorithm works for any shape that provides a SDF

  4. the ray marching procedure can be computed very efficiently

The viability of SDFs for radiation transfer simulations has already been demonstrated in the literature [30]. For optical ray tracing this is a new application to the best of our knowledge. Representing lens shapes using the exact shape SDFs provided by Inigo Quilez is straight forward by combining cylinder and cut-sphere SDFs using boolean addition and subtraction. Further, custom iterative algorithms have been developed by O. Kliebisch in order to tie in (rotationally symmetrical) aspherical lenses into the ray marching formalism.

In order to implement new SDFs, the AbstractSDF interface should be used.

BeamletOptics.AbstractSDF Type

Provides a shape function based on signed distance functions. See https://iquilezles.org/articles/distfunctions/ for more information.

Implementation reqs.

Subtypes of AbstractSDF should implement all reqs. of AbstractShape as well as the following:

Functions

  • sdf(::AbstractSDF, point): a function that returns the signed distance for a point in 3D space
source

The following shapes are currently implemented:

julia
julia> BeamletOptics.list_subtypes(BeamletOptics.AbstractSDF);
└── BeamletOptics.AbstractSDF
    ├── BeamletOptics.AbstractCompositeSDF
    │   ├── BeamletOptics.DifferenceSDF
    │   └── BeamletOptics.UnionSDF
    ├── BeamletOptics.AbstractLensSDF
    │   ├── BeamletOptics.AbstractAsphericalSurfaceSDF
    │   │   ├── BeamletOptics.ConcaveAsphericalSurfaceSDF
    │   │   └── BeamletOptics.ConvexAsphericalSurfaceSDF
    │   ├── BeamletOptics.AbstractCylindricalSurfaceSDF
    │   │   └── BeamletOptics.AbstractAcylindricalSurfaceSDF
    │   │       ├── BeamletOptics.AconcaveCylinderSDF
    │   │       └── BeamletOptics.AconvexCylinderSDF
    │   ├── BeamletOptics.AbstractSphericalSurfaceSDF
    │   │   ├── BeamletOptics.ConcaveSphericalSurfaceSDF
    │   │   └── BeamletOptics.ConvexSphericalSurfaceSDF
    │   ├── BeamletOptics.ConcaveCylinderSDF
    │   ├── BeamletOptics.ConvexCylinderSDF
    │   ├── BeamletOptics.MeniscusLensSDF
    │   ├── BeamletOptics.PlanoSurfaceSDF
    │   └── BeamletOptics.SphereSDF
    ├── BeamletOptics.BoxSDF
    ├── BeamletOptics.ConicSDF
    ├── BeamletOptics.CutSphereSDF
    ├── BeamletOptics.CylinderSDF
    ├── BeamletOptics.RightAnglePrismSDF
    └── BeamletOptics.RingSDF

At least 25 types have been found.

Ray marching algorithm ​

The algorithm works by iteratively propagating a ray from the point of origin through a scene. At each step, the SDF/s is/are evaluated at the current point along the ray. The scaral value tells the algorithm how far the current positions is from the nearest surface. This distance is guaranteed to be a safe step size: the ray can advance by that amount without “tunneling” through any geometry. The process repeats until one of two conditions is met: (1) the distance becomes smaller than a threshold (the ray has hit a surface), or (2) a maximum number of steps or maximum distance is exceeded (the ray escapes without hitting anything). A visualization of this process is provided below. A ray propagates from the left to the right end of the scene. The step size is visualized as the radius of a sphere for each iteration.

Surface normals can be estimated numerically by sampling the SDF gradient around the hit point or by generating the automatic derivative of the SDF for exact descriptions. BMO uses both procedures, depending on the SDF type.

Composite SDFs ​

In order to make use of the dispatch capability of Julia, boolean composite SDFs allow users to easily combine SDFs using the + and - operators. Both share a common field layout and pivot-aware kinematics, defined by the AbstractCompositeSDF interface:

BeamletOptics.AbstractCompositeSDF Type

Generic supertype for boolean composite SDFs, i.e. SDFs that combine two or more AbstractSDF operands into a single shape. Owns the field layout and pivot-aware kinematics shared by all boolean composites; concrete subtypes only need to define the boolean combination semantics (sdf, normal3d, thickness).

Implementation reqs.

Subtypes of AbstractCompositeSDF should implement all reqs. of AbstractSDF as well as the following:

Fields

  • dir::SMatrix{3, 3, T, 9}: the composite's own orientation matrix

  • transposed_dir::SMatrix{3, 3, T, 9}: transpose of dir

  • pos::Point3{T}: the composite's own position, in that field order, and the struct must be mutable since every setter in AbstractShape.jl/AbstractSDF.jl assigns fields directly

Functions

  • operands: returns a Tuple of every child AbstractSDF, in any order

  • sdf, normal3d and thickness specific to the boolean combination

source

Union ​

BeamletOptics.UnionSDF Type
julia
UnionSDF{T, TT <: Tuple} <: AbstractSDF{T}

This SDF represents the merging of two or more SDFs. If the constituent SDFs do not overlap (they can and should touch) the resulting SDF should be still exact if the constituent SDFs are exact.

The intended way to construct these is not explicitely but by just adding two AbstractSDFs using the regular + operator.

julia
s1 = SphereSDF(1.0)
translate3d!(s1, Point3(0, 1.0, 0.0))

s2 = SphereSDF(1.0)

# will result in a SDF with two spheres touching each other.
s_merged = s1 + s2
source

Difference ​

BeamletOptics.DifferenceSDF Type
julia
DifferenceSDF{T, S, TT <: Tuple} <: AbstractCompositeSDF{T}

Represents the boolean subtraction of one or more tools from a base AbstractSDF, i.e. base \ (tool_1 ∪ tool_2 ∪ …). Unlike UnionSDF, the constituent SDFs are required to overlap: the tools must intersect the base for anything to be removed.

The intended way to construct these is not explicitly but by subtracting AbstractSDFs using the regular - operator:

julia
s1 = SphereSDF(1.0)
s2 = SphereSDF(0.5)
translate3d!(s2, Point3(0.5, 0.0, 0.0))

# will result in a sphere with a smaller, off-center sphere carved out of it
s_diff = s1 - s2

Non-commutativity and evaluation order

Subtraction is neither commutative nor associative, so a - b and b - a differ, and a chain like a + b - c + d must keep track of when each operand was combined. Repeated subtraction is flattened along its own chain via the identity

so a - b - c produces a single DifferenceSDF with one base and a flat tools tuple, rather than deepening the type on every chained -. Mixed +/- chains still nest (e.g. a + b - c is (a + b) - c, and ... + d afterwards wraps the whole difference in a UnionSDF) — this is what keeps their meaning, since material added after a subtraction must not be carved away by it.

Exactness

Per iquilezles.org/articles/distfunctions, opSubtraction = max(-a, b) is only a bound, not an exact SDF: it under-estimates the true distance near the seam between base and tool. This is the safe direction for sphere tracing — an under-estimate never causes tunneling, it only costs a few extra ray marching iterations near concave creases. Correctness of ray marching hinges on normal3d being resolved by explicit operand selection (as implemented here) rather than by automatic differentiation, which picks the wrong sub-shape at the seam.

source

Exactness

Boolean addition of non-overlapping, exact SDFs stays exact. Boolean subtraction, by contrast, only yields a bound: max(-a, b) under-estimates the true distance near the seam between operands. This under-estimate is the safe direction for sphere tracing — it never causes tunneling, only a few extra ray marching iterations near concave creases. For information refer to the website of I. Quilez