Skip to content

Repository files navigation

Caustikon

Allocation-free geometric optics primitives for .NET.

Caustikon handles what happens when a ray meets a dielectric boundary: its transmitted direction, reflected power, and the change in refractive index with wavelength. It provides vector refraction, exact Fresnel power reflectance, Schlick's approximation, and three-term Cauchy and Sellmeier dispersion models.

Scalar and span APIs use caller-owned storage. The library targets .NET 8 and .NET 10, with no external runtime dependencies. The API is pre-1.0 and may change between releases.

Six wavelengths refracted through an N-BK7 prism

The prism example calculates the ray paths and prints the numerical results. The spectral spread is to scale; colors identify wavelengths, not transmitted power.

Use from source

Caustikon is not published to NuGet.org. With Git and .NET SDK 10.0.400 installed, run the prism example from a new checkout:

git clone https://github.com/levvs-one/caustikon.git
cd caustikon
New-Item -ItemType Directory -Force artifacts
dotnet run --project examples/Prism --configuration Release -- artifacts/prism.svg

This prints six wavelength/index/deviation rows and writes artifacts/prism.svg, which you can open in a browser. It does not replace the checked-in diagram.

The repository selects .NET SDK 10.0.400 and permits later patches in the same 10.0.4xx feature band. The examples need only the .NET 10 runtime included with that SDK. Running the tests for both targets also requires the .NET 8 runtime; a second SDK is not necessary.

Refract a ray

To use the library in your own application, create a sibling console project from the repository root:

dotnet new console --output ../RayDemo --framework net10.0
dotnet reference add src/Caustikon/Caustikon.csproj --project ../RayDemo/RayDemo.csproj

Replace ../RayDemo/Program.cs with:

using System.Numerics;
using Caustikon;

Vector3 incident = Vector3.Normalize(new Vector3(0.5f, -0.8660254f, 0f));
Vector3 normal = Vector3.UnitY;

RefractionKind kind = Dielectric.RefractUnit(
    incident,
    normal,
    nIncident: 1f,
    nTransmitted: 1.5f,
    out Vector3 transmitted);

switch (kind)
{
    case RefractionKind.Refracted:
    case RefractionKind.CriticalAngle:
        float cosIncident = -Vector3.Dot(incident, normal);
        FresnelPower power = Dielectric.Fresnel(cosIncident, 1f, 1.5f);
        Console.WriteLine($"{kind}: T = {transmitted}, R = {power.Unpolarized}");
        break;
    case RefractionKind.TotalInternalReflection:
        Console.WriteLine("No transmitted ray; all incident power is reflected.");
        break;
    case RefractionKind.InvalidInput:
        throw new ArgumentException("Check unit vectors, normal orientation, and positive finite indices.");
}

Run dotnet run --project ../RayDemo --configuration Release from the repository root. For these inputs the result is Refracted, with a transmitted direction near (0.3333333, -0.9428090, 0) and unpolarized reflectance near 0.04152.

The direction and normal convention is strict:

  • incident points along ray travel, toward the interface.
  • normal points back into the incident medium.
  • Their dot product must be nonpositive. A positive dot product is invalid.
  • transmitted points away from the interface into the transmitted medium.
  • nIncident and nTransmitted are positive phase refractive indices measured under comparable conditions.

RefractUnit returns a status instead of hiding the critical-angle boundary. Refracted and CriticalAngle return a unit direction. TotalInternalReflection and InvalidInput return Vector3.Zero.

Evaluate a dispersion model

The public wavelength unit is the nanometer. Use the wavelength convention of the coefficient source, including its air or vacuum reference. Coefficient names state the micrometer powers used by the equations.

using Caustikon;

var glass = new Sellmeier3(
    b1: 1.03961212,
    c1Um2: 0.006000699,
    b2: 0.231792344,
    c2Um2: 0.0200179144,
    b3: 1.01046945,
    c3Um2: 103.560653,
    minimumWavelengthNanometers: 365d,
    maximumWavelengthNanometers: 2325.4d);

DispersionStatus status = glass.EvaluateNanometers(
    wavelengthNanometers: 587.6d,
    out double refractiveIndex);

if (status is DispersionStatus.Success)
{
    Console.WriteLine(refractiveIndex);
}
else
{
    Console.Error.WriteLine($"The model cannot evaluate this wavelength: {status}");
}

The coefficients come from the SCHOTT N-BK7 datasheet. At 587.6 nm, the result is approximately 1.51679844, consistent with the datasheet's rounded 1.51680. SCHOTT's catalogue relation gives refractive index relative to air at room temperature; see TIE-29, section 2.3 for its conventions.

The example chooses a 365-2325.4 nm interval within the tabulated values. This is a caller-selected range. Every model carries an inclusive wavelength interval and reports OutsideModelRange beyond it.

API map

Area Scalar API Batch API
Refraction Dielectric.RefractUnit Shared or per-lane refractive indices
Exact dielectric reflectance Dielectric.Fresnel Shared or per-lane refractive indices
Normal-incidence reflectance Dielectric.NormalReflectance Per-lane refractive indices
Schlick reflectance Dielectric.Schlick Shared R0 or shared refractive indices
Cauchy dispersion Cauchy3.EvaluateNanometers Wavelength, result, and status spans
Sellmeier dispersion Sellmeier3.EvaluateNanometers Wavelength, result, and status spans

Batch overloads write into spans supplied by the caller. Span lengths must match. Permitted in-place operations and overlap restrictions are specified in the buffer contract.

CriticalBoundary is a complete batch example: six wavelengths reach a glass-to-air interface at the same angle, with both refraction and total internal reflection in the result. It shows buffer setup, status handling and per-wavelength indices, and checks every batch result against the scalar API.

Numerical contracts

  • FresnelPower.S and P store power reflectances, not field amplitudes. Unpolarized computes their arithmetic mean.
  • Dielectric.Fresnel takes cosIncident in [0, 1]. It returns unit reflectance at and beyond the critical boundary.
  • Dielectric.Schlick(cosIncident, normalReflectance) evaluates the approximation only. It cannot infer total internal reflection from R0 and the cosine.
  • Cauchy3 evaluates n = A + B / wavelength^2 + C / wavelength^4 with wavelength in micrometers.
  • Sellmeier3 evaluates n^2 = 1 + sum(Bi * wavelength^2 / (wavelength^2 - Ci)) with wavelength in micrometers.
  • A non-successful dispersion evaluation writes double.NaN.
  • Constructors reject nonfinite coefficients and invalid wavelength intervals. Sellmeier3 also rejects negative resonance coefficients and intervals containing an active positive resonance pole. A term with Bi = 0 is inactive.

The full status and boundary rules are in docs/conventions.md.

Measured cost

The refraction benchmark reported zero managed allocations in all 16 scalar and span cases. On an Intel i5-4670 with .NET 10, a million air-to-glass interactions took 27.70 ms in a caller-written scalar loop and 31.64 ms through the span API. These are single-machine measurements, with inputs and buffers prepared before timing. The full report records the method, uncertainty, limitations, and reproduction command.

Scope

Caustikon models homogeneous, isotropic, nonabsorbing dielectric media and phase refractive index. It does not model absorption, complex refractive index, birefringence, polarization state propagation, thin-film interference, diffraction, surface roughness, lens geometry, ray-scene intersection, or rendering. It is intended to be embedded in systems that own those concerns.

Build and test

dotnet restore Caustikon.sln --locked-mode
dotnet build Caustikon.sln -c Release --no-restore
dotnet test --project tests/Caustikon.Tests/Caustikon.Tests.csproj -c Release -f net8.0 --no-build
dotnet test --project tests/Caustikon.Tests/Caustikon.Tests.csproj -c Release -f net10.0 --no-build
dotnet pack src/Caustikon/Caustikon.csproj -c Release --no-build --output artifacts/package

See CONTRIBUTING.md for the acceptance rules used for numerical changes and benchmarks.

References

License

MIT - Copyright (c) 2026 levvs-one.

About

Real optical glass for design, games, 3D and .NET: 1646 catalog glasses and nine liquids with dispersion, absorption, temperature and colour; a GPU renderer, a UI glass shader and an optical bench, every number cited.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages