222 lines
14 KiB
Markdown
222 lines
14 KiB
Markdown
# CultMath
|
|
|
|
CultMath is the project name. Its clean .NET package identity is
|
|
`GameCult.Math`, owned and released from the CultLib monorepo.
|
|
|
|
Clean-room math shaped for people who think in HLSL.
|
|
|
|
CultMath provides lowercase vector types, shader-style intrinsics, and native
|
|
hot-path kernels so math-heavy renderer code can move between C#, Rust, and HLSL
|
|
without turning every line into a translation exercise. It is not a
|
|
Unity.Mathematics clone. It is a small, Aquarium-born library built from public
|
|
shader semantics.
|
|
|
|
## Current Surface
|
|
|
|
- `float2`, `float3`, `float4`
|
|
- component-wise arithmetic
|
|
- scalar-to-vector splats
|
|
- every HLSL swizzle (`xyzw` and `rgba`, repeats readable, non-repeating
|
|
masks writable) and every HLSL mixed constructor, generated by
|
|
`tools/generate-swizzles.ps1`
|
|
- conversions to and from `System.Numerics`
|
|
- `Random`, a compact deterministic XORShift stream with scalar and vector draws
|
|
- `BatchMath` SoA kernels over `Span<float>` / `ReadOnlySpan<float>` using
|
|
`System.Numerics.Vector<float>` when available, including radial falloff
|
|
acceleration and semi-implicit Euler integration
|
|
- `math` intrinsics: `radians`, `degrees`, `abs`, `floor`, `ceil`, `frac`,
|
|
`min`, `max`, `clamp`, `saturate`, `lerp`, `step`, `smoothstep`, `dot`,
|
|
`cross`, `length`, `distance`, `normalize`, `reflect`, `csum`, `erf`,
|
|
`erfinv` (scalar only), exponential
|
|
`decay`/`damp`, intercept/segment helpers (`distance_to_segment` in 2D and 3D, with and without `out closest`, and `segment_closest`), Catmull-Rom, Bezier curves, and
|
|
deterministic value-noise primitives
|
|
- Site cells: `power_cells` and `multiplicative_cells` return the two nearest sites of a point as `CultSiteCells`,
|
|
`soft_gabriel_gate` gates a pair of sites by the empty-disc margin. Each is a loop over the shader steps
|
|
`cultmath_power_score` / `cultmath_multiplicative_score`, `cultmath_site_cells_empty`, `cultmath_site_cells_insert`,
|
|
`cultmath_site_cells_border`, `cultmath_power_border_step` (the exact power cell border, `power_border`) and `cultmath_gabriel_margin`; a shader loops over its own buffer.
|
|
- Weighted lattice cells: `weighted_lattice_cells` returns `float4(F1, F2, smooth, id)` over a jittered,
|
|
drifting lattice whose cell weights come from the exact power border of the body span at each cell centre, with a stretched
|
|
metric and an exact ring search over the stated range (`LatticeMaxBoost`, `LatticeMaxStretch`, `LatticeMaxSmoothing`). It composes the
|
|
public steps `lattice_site`, `lattice_weight`, `lattice_distance` and `lattice_smooth_step`. The shaders carry the four steps as
|
|
`cultmath_lattice_site`, `cultmath_lattice_weight`, `cultmath_lattice_distance` and `cultmath_lattice_smooth_step`, and the whole function as `cultmath_weighted_lattice_cells(p, time, bodies,
|
|
count, ...)` over a `float4 bodies[CULTMATH_MAX_SITES]` array (128 by default; define the macro before including to change it);
|
|
`cultmath_power_border(p, sites, count)` is the matching border. The argument checks become the shader caller's preconditions.
|
|
- `shaders/CultMath.hlsl`, a canonical HLSL mirror include for shader-side
|
|
parity. HLSL already owns `float2`, `float3`, and `float4`; the include
|
|
exposes `cultmath_*` functions for shared semantics HLSL does not provide.
|
|
It includes `shaders/CultMath.Phacelle.hlsl` (MPL-2.0),
|
|
`shaders/CultMath.Cycles.hlsl` (Apache-2.0) and
|
|
`shaders/CultMath.Interval.hlsl` and `shaders/CultMath.Affine.hlsl` from the same
|
|
directory, so ship all five.
|
|
- Cycles node semantics (Apache-2.0): `cycles_*` functions that compute what
|
|
Blender's Cycles renderer computes for the Math node operations whose results
|
|
differ from a plain intrinsic (`cycles_divide`, `cycles_power`, `cycles_modulo`,
|
|
`cycles_wrap`, `cycles_pingpong`, `cycles_smooth_min`, ...), Map Range
|
|
(`cycles_map_range_linear`, `_stepped`, `_smoothstep`, `_smootherstep`), Clamp
|
|
(`cycles_clamp_minmax`, `cycles_clamp_range`), Mix (`cycles_mix_float`,
|
|
`cycles_mix_vector` and every colour blend mode as `cycles_mix_<mode>`), RGB to BW,
|
|
and `cycles_ramp_index`, where a Color Ramp reads its table. `cultmath_cycles_*` in
|
|
HLSL and GLSL. Operands are finite floats, and the tests hold every function to
|
|
the results of Blender 5.2.2's own CPU Cycles (`tools/cycles-oracle`).
|
|
- interval arithmetic for culling empty space: `iv_*` functions
|
|
over `float2(lo, hi)` (`iv_add`, `iv_mul`, `iv_exp`, `iv_smoothstep`, ...)
|
|
and `iv_snoise_ball`/`iv_fbm_ball`, which bound `snoise` and `fbm_grad` over a
|
|
ball, and `iv_frustum_ball`, the ball around a screen tile's rays over a depth
|
|
segment, so one probe serves the whole tile (its radius carries its own
|
|
float32 rounding bound). The arithmetic functions return an interval built to
|
|
contain the function's value over their operand intervals, so a raymarch
|
|
can skip a segment whose `hi` is below its cutoff; `cultmath_iv_*` in HLSL.
|
|
The noise functions rest on empirical constants with a margin, not a proof:
|
|
`iv_snoise_ball(float3)` on `SNOISE_LIPSCHITZ` (`iv_fbm_ball` through it),
|
|
`iv_snoise_ball(float2)` on `SNOISE2_LIPSCHITZ`, and `af_snoise` on
|
|
`SNOISE_HESSIAN` and `SNOISE_LIPSCHITZ` (`af_fbm` through it); the enclosure
|
|
tests sample them. `iv_frustum_ball` and `af_frustum_ball` read no noise
|
|
constant: the ball is geometry plus a rounding widening, and the noise call
|
|
it feeds carries the constant.
|
|
The bounds compose with a consumer's own analytic envelope (a height fog, a
|
|
carved sphere): its interval over the segment proves empty space in one probe,
|
|
and the noise ball is consulted only near the surface. See `docs/design.md`,
|
|
"Intervals".
|
|
- `shaders/CultMath.glsl` (MIT), the same `cultmath_*` library in GLSL ES 3.00
|
|
for WebGL2, generated from `CultMath.hlsl` (never edited by hand), and
|
|
`shaders/CultMath.Phacelle.glsl` (MPL-2.0), generated from
|
|
`CultMath.Phacelle.hlsl`, which holds `cultmath_phacelle` alone, and
|
|
`shaders/CultMath.Cycles.glsl` (Apache-2.0), generated from
|
|
`CultMath.Cycles.hlsl`, which holds the `cultmath_cycles_*` functions. GLSL has no
|
|
`#include`: a WebGL consumer concatenates `CultMath.glsl` after its own
|
|
`#version 300 es` line and `precision highp float; precision highp int;`
|
|
(the library declares no precision, and its hashes need 32-bit integers),
|
|
then `CultMath.Phacelle.glsl` only if it calls `cultmath_phacelle` and
|
|
`CultMath.Cycles.glsl` only if it calls a `cultmath_cycles_` function, then its
|
|
own shader source.
|
|
- Span primitives (`math.Span.cs`, `cultmath_*` in HLSL and GLSL): `snoise_hessian`,
|
|
the analytic Hessian of `snoise(float3)` on the same lattice, hash and tie order as
|
|
`snoise_grad` (a symmetric `float3x3`; GLSL `mat3`), and `integral_clamp_linear(a,
|
|
b, lo, hi, t0, t1)`, the closed-form integral over `[t0, t1]` of `clamp(a + b t,
|
|
lo, hi)`; and the octave stages `span_fbm(path, stage,
|
|
octaves, lacunarity, gain)` and `span_ridged` (shader twins `cultmath_span_fbm` and `cultmath_span_ridged`,
|
|
whose callers build the `SpanStage` themselves), over a `SpanPath` and a `SpanStage` (`SpanStage.Identity`
|
|
is the plain fbm in C#), which return a `SpanNoise` (`field` as `float4(gradient, value)`,
|
|
`curvature`, `fold`) for a stretched, offset, shifted octave sum over a span. Three measured bounds describe `snoise`: `SNOISE_LIPSCHITZ` (gradient
|
|
length, `NoiseBoundTests.MeasureLipschitz`), `SNOISE_HESSIAN` (Hessian spectral
|
|
norm, `MeasureHessian`) and `SNOISE_THIRD` (third-derivative operator norm,
|
|
`MeasureThird`); each is 1.10 times a seeded maximum refined by ascent, and a
|
|
fast test pins it. They are empirical with a margin, not proofs.
|
|
- `Voronoi.SampleTones`, a C# batch surface that calls the Rust
|
|
`cultmath-core` native kernel when `cultmath_core` is available and falls back
|
|
to the managed parity path otherwise.
|
|
|
|
## Native Core
|
|
|
|
The canonical hot-path body lives in `native/cultmath-core`. It exports a C ABI
|
|
for batch kernels such as `cultmath_apollonian_voronoi_tones`.
|
|
|
|
Runtime wrappers own syntax and integration. Rust owns optimized primitive
|
|
kernel behavior and parity fixtures.
|
|
|
|
## Example
|
|
|
|
```csharp
|
|
using CultMath;
|
|
|
|
float3 normal = math.normalize(new float3(0.25f, 1.0f, -0.1f));
|
|
float rim = math.saturate(1.0f - math.dot(normal, new float3(0.0f, 0.0f, -1.0f)));
|
|
float glow = math.smoothstep(0.2f, 1.0f, rim);
|
|
float grain = math.value_noise_bicubic(math.float2(12.0f, 3.5f));
|
|
```
|
|
|
|
```hlsl
|
|
#include "CultMath.hlsl"
|
|
|
|
float3 normal = normalize(float3(0.25, 1.0, -0.1));
|
|
float rim = cultmath_saturate(1.0 - dot(normal, float3(0.0, 0.0, -1.0)));
|
|
float glow = cultmath_smoothstep(0.2, 1.0, rim);
|
|
float grain = cultmath_value_noise_bicubic(float2(12.0, 3.5));
|
|
```
|
|
|
|
## Provenance
|
|
|
|
The small spline, damping, and pursuit helpers were promoted from Aetheria's
|
|
local `AetheriaMath` surface. The value-noise family follows public shader
|
|
literature from Inigo Quilez's noise and mini-spline articles, expressed in
|
|
CultMath names with tests instead of copied project-local helper drift.
|
|
|
|
## Build
|
|
|
|
```powershell
|
|
dotnet build packages/cultmath/src/CultMath/CultMath.csproj
|
|
dotnet test packages/cultmath/tests/CultMath.Tests/CultMath.Tests.csproj
|
|
cargo test --manifest-path packages/cultmath/native/cultmath-core/Cargo.toml
|
|
cargo build --release --manifest-path packages/cultmath/native/cultmath-core/Cargo.toml
|
|
.\packages\cultmath\scripts\build-unity-package.ps1
|
|
```
|
|
|
|
## Unity package
|
|
|
|
CultMath's Unity 2021.3 surface is the tracked precompiled package at
|
|
`packages/cultmath/unity/org.gamecult.cultmath`. Unity consumes `CultMath.dll` and the numeric
|
|
`CultMath.hlsl`; it does not compile the repository's current-language C# source
|
|
tree and does not require a consumer `csc.rsp` workaround.
|
|
|
|
```json
|
|
"org.gamecult.cultmath": "https://github.com/GameCult/CultLib.git?path=/packages/cultmath/unity/org.gamecult.cultmath"
|
|
```
|
|
|
|
The repository root is not a Unity package. Build the tracked package with
|
|
`packages/cultmath/scripts/build-unity-package.ps1`; the script fresh-builds the netstandard2.1
|
|
assembly and rejects stale binaries, missing metadata, unexpected assemblies,
|
|
or leaked C# source.
|
|
|
|
Before tagging `cultmath-unity-v*`, run the import and dispatch smoke on a Windows machine with a GPU,
|
|
alone: `packages/cultmath/scripts/unity-smoke/run-unity-smoke.ps1 -UnityExe <path to Unity.exe>`. It
|
|
builds a scratch project outside the repository that references the package by a `file:` path,
|
|
dispatches a compute shader that includes `CultMath.Interval.hlsl` and `CultMath.Affine.hlsl`, requires
|
|
a deliberately broken control shader to be rejected, and deletes the project afterwards.
|
|
|
|
## Shader Tooling
|
|
|
|
CultMath keeps portable DXC outside git under `packages/cultmath/.tools/`:
|
|
|
|
```powershell
|
|
.\packages\cultmath\tools\get-dxc.ps1
|
|
.\packages\cultmath\tools\compile-hlsl.ps1
|
|
.\packages\cultmath\tools\get-spirv-cross.ps1
|
|
.\packages\cultmath\tools\compile-hlsl-spirv.ps1 `
|
|
-ShaderPath E:\Projects\Odin\crates\muninn-move-tracker\shaders\MoveSphereCandidate.comp.hlsl `
|
|
-OutputPath E:\Projects\Odin\crates\muninn-move-tracker\artifacts\shader\MoveSphereCandidate.comp.spv `
|
|
-IncludePath E:\Projects\CultLib\packages\cultmath\shaders
|
|
.\packages\cultmath\tools\compile-spirv-metal.ps1 `
|
|
-SpirvPath E:\Projects\Odin\crates\muninn-move-tracker\artifacts\shader\MoveSphereCandidate.comp.spv `
|
|
-OutputPath E:\Projects\Odin\crates\muninn-move-tracker\artifacts\shader\MoveSphereCandidate.comp.metal
|
|
```
|
|
|
|
`tools/glsl/build-glsl.sh <outdir>` (Linux, repository root) builds the library GLSL from the HLSL with dxc, SPIRV-Cross and SPIRV-Tools and writes `core.glsl`, `cycles.glsl` and `phacelle.glsl` bodies into `<outdir>`. It does not yet own the committed GLSL; `GlslLowering` still does. `GLSL_DIR=<outdir> tools/device/run-lavapipe.sh` runs that output on lavapipe and compares it with the HLSL results bit for bit instead of judging the committed files. That comparison is in strict form (dxc `-Gis` and a `NoContraction` decoration on every float result of both modules), and a consumer overrides `CULTMATH_MAX_SITES` in the built GLSL the same way as in HLSL: define it before including `core.glsl`.
|
|
|
|
`compile-hlsl.ps1` compiles both `CultMath.hlsl` copies as a `lib_6_3` library with the x64 DXC; a warning fails it too (`-WX`).
|
|
|
|
DXC emits Vulkan SPIR-V with `-spirv`. `-TargetEnv vulkan1.2` is exposed by the
|
|
script, but the current Windows DXC release compiled this shader successfully
|
|
with DXC's default Vulkan target while rejecting the dotted target-env value.
|
|
The SPIR-V script applies default Vulkan register shifts for cbuffers, textures,
|
|
UAVs, and samplers so cross-compiled Metal buffers do not alias by resource
|
|
class. SPIRV-Cross emits Metal Shading Language from SPIR-V on Windows; building
|
|
an iPad-ready `.metallib` still belongs to Apple's Metal compiler on an Apple
|
|
toolchain.
|
|
|
|
The lavapipe conforming run executes every fixture family through the HLSL (dxc) and the committed GLSL
|
|
(glslang) on Mesa lavapipe and judges the results in `GlslMirrorTests.DeviceResultsHoldFixture`. It needs a Linux
|
|
container with apt: from the repo root, `tools/stopgap/ygg-verify.sh <repo> <rev> mcr.microsoft.com/dotnet/sdk:10.0
|
|
'bash packages/cultmath/tools/device/run-lavapipe.sh'`. `WRITE_BOUNDS=1` in front of the script re-records the
|
|
device's bounds file and prints it between `BOUNDS-BEGIN` and `BOUNDS-END`.
|
|
|
|
## Doctrine
|
|
|
|
The goal is mechanical sympathy between CPU-side authoring, native kernels, and
|
|
GPU-side shader math. If an intrinsic exists here, it should behave like the
|
|
shader concept unless a runtime's numeric rules make that impossible. Weird
|
|
deviations get tests and documentation, not folklore.
|
|
|
|
CultMath is still not a shader generator. Projects should include
|
|
`shaders/CultMath.hlsl` directly or add that directory to their shader include
|
|
path. If a shared operation needs CPU/GPU parity, put the primitive here and
|
|
test it here before copying local math helpers into a renderer or daemon.
|