Collision detection
A detailed description of how differentiable collision detection works in Torch Lens Maker.
Definitions
Parametric ray
A ray is represented parametrically as the set of all points
is an origin point is a unit direction vector
Therefore a ray is a infinite line in space. The ray can be 2D or 3D depending on the number of dimensions being simulated. The only change is to the dimension of P and V.
Technically, the ray origin
Implicit surface
An implicit surface is defined with a function:
- In 2D,
- In 3D,
Mixed 2D / 3D
Note how the 2D axis variables are
For collision detection, a surface must also define the gradient of F:
- In 2D,
- In 3D,
Sag functions
As long as the above conditions are met, the meaning of the values of
This is only defined within the domain of the sag function, which is typically the diameter of a lens. Outside of that domain, another distance function must be used.
TODO document the band distance outline function here.
Newton's method
To compute the intersection of a ray with a surface, we are looking for the unknown
We call the quantity on the left side
In reality there can be zero, one or more roots. In practice we are going to solve it iteratively and either:
- converge to a single solution
- fail to converge (indicating no collision)
We can solve the intersection equation using Newton's method with the update step:
Developing with the multivariate chain rule we get:
where "
If
In practice, even when there is a unique root, this method can oscillate strongly instead of converging, in cases where there is a non differentiable point in F (like in the band distance outline function). To mitigate this, a damping parameter
TODO document the max_delta version of Newton's method
Gradient descent
A problem with the Newton's Raphson formulation above arises in cases where there is no root. For collision detection, it is necessary to detect the absence of collision to be able to know if rays should be refracted and sent to the next optical element in the stack, or dropped as "non colliding" rays. On top of collision points, we must also produce a mask indicating which ray do not collide with the surface.
Thankfully, using the implicit surface function
However, a big problem is that the algorithm produces an undefined update step at any minimum where
A better formulation instead, is to attempt to minimize the distance to the root, which can be expressed by defining a new function
Developing:
The new problem formulation is that we want to find the value of
The straight forward gradient descent solution to that minization problem is to take a step in the direction of the gradient of H, which is:
Leading to the update step:
where
Gauss-Newton
The formulation above is essentially a least square problem with a single residual term. We can also use the Gauss-Newton method to solve it.
First, we assume that
Plugin this into the equation for
For our update step, we want to find the value of
Solving for
Levenberg–Marquardt
Starting from the previous equation for
which yields the update step:
When
Having a stricly non zero minimum value on
Beam search
TODO document boundary radius domain initialization
Every optimization method above requires an initial guess for the iteration over t values.
- initial guess strategies: t=0, t=collision with X/Y axis, t=collision with bounding sphere
- bounding sphere diameter = bound on step size
differentiable last step: can use different method than main steps
Differentiable collision detection
TODO Wang 2022
Surface of revolution
2D shape definition
A 2D shape is defined in the
X is the optical axis, and R is the meridional axis, aka the perpendiular to the optical axis.
Additionally, for working with lenses, we always have
Rotational symmetry
We want to create the corresponding 3D surface by rotation around the X axis. This means that the R axis from before, is now really the axis of the meridional plane. (A meridional plane is a plane that contains the optical axis X).
So
The definition of the 3D surface of revolution is that the intersection of every meridional plane with it is the 2D surface:
Often the form
Generic form of for surfaces of revolution
We have:
Therefore:
However, for some curves
Sag surface and diameter band surface
In optics, it is convenient to define a surface with what is sometimes called a sag function. This is a function that defines the offset from the meridional axis, as a function of the radial coordinate:
This suffers a problem however, when using a function that's undefined outside of a fixed domain, typically the diameter of the surface (or the diameter of the supporting surface in the case of a half-sphere). For example, when trying to define a half circle of diameter
TODO
the resulting implicit function
A related problem happens even with surfaces where the sag function is well defined beyond the domain of the diameter. For example, consider the parabola function
To address both theses issues, a partial implicit surface is defined, called a diameter band surface. This is essentially the function:
or in meridional coordinates form:
where
To simplify computation to only the positive half plane, it's useful to introduce an absolute value:
The derivatives are:
This can be generalized to 3D and yield:
with derivatives:
The gradient is undefined at
This surface can be combined with any other sag function to create an implicit surface that's well defined everywhere and behaves nicely for optimization, even when starting from outside of the diameter.
The implicit form of a surface defined by a sag function is easily derived:
And in 3D, applying rotational symmetry around the X axis:
However it's typical for the expressions for
Collision detection with a 3D transform
Surfaces are defined on a local reference frame so that
Let's assume our 3D transform
Let's consider some points
Given a parametric 3D ray:
which is useful because we can use any local solver by applying the inverse transform to the rays, and using the
In the common case,
Note that this applies even if the surface is not defined implicitly: we can find collisions with the transformed surface by applying the above inverse transform to the rays and calling the local collision detection code.
Another thing we need to do is convert vectors from the surface local frame, to the global frame, typically surface normals.
A vector
So to transform the vector under the affine transformation, we can take the difference of its transformed endpoints:
So after simplifying we get:
Adding anchors
Similarly as above, it can be useful to add a translation step before the rotation, to model an "anchor". The anchor is the point on the shape that attaches to the global frame. So, our full transform is now four steps:
- A translation
to account for the anchor - A scale
- A rotation
- A translation
to position the shape in the global frame
The inverse transform is:
When
And so we can compute "inverse transformed" rays:
Direct transform of vectors is:
Forward kinematic chain
TODO document using sucessive transforms to make a forward kinematic chain
Iterative collision detection for implicit surfaces
Summary of the overall algorithm.
First, a bit of nomenclature:
- "algorithm" refers top one instance of Newton, Gradient Descent or Levenberg-Marquardt with associated configuration like number of iterations, maximum step size, damping parameter, etc.
- "method" refers to the overall collision detection procedure, which includes three phases which each use a single algorithm.
Step 1: Initialization
Initialize t values. Different initialization methods are available. During the optimization, each ray can be associated with multiple t values so that the search can progress from multiple starting values of t in parallel. This is akin to particle optimization, but here the search is quite simple it's one dimensional. Each of these is called a "beam" in the source code.
So ultimately tensors in the code can have three dimensions:
- N, the number of rays
- H, the number of iteration steps
- B, the number of beams per ray
Step 2: Coarse phase
Run a fixed number of steps of algorithm A, with B beams for each ray. The goal here is to have at least one beam within a close distance to the global minimum.
Step 3: Fine phase
Starting from the best beam of the coarse phase, run a fixed number of steps of algorithm B with a single beam. The goal here is to refine the solution to a high degree of precision.
Step 4: Differentiable step
Run a single step of algorithm C. The goal here is to provide differentiability during torch backwards pass. Every step except this one is run under torch.no_grad()
.