Source:
noise.h
noise¶
A smooth, deterministic pseudo-random field: the staple organic-motion source for effects.
Functions¶
| Return | Name | Description |
|---|---|---|
constexpr uint8_t |
inoise8 constexpr |
|
constexpr uint8_t |
[inoise8] constexpr |
|
constexpr uint8_t |
[inoise8] constexpr |
|
constexpr uint16_t |
inoise16 constexpr |
1D gradient noise at 16 bits. x is 16.16 fixed point: the whole part selects the cell, the fraction interpolates within it. |
constexpr uint16_t |
[inoise16] constexpr |
2D gradient noise at 16 bits: the common case for a panel. |
constexpr uint16_t |
[inoise16] constexpr |
3D gradient noise at 16 bits. z is the axis a 2D effect uses as time, so the field evolves in place instead of scrolling past. |
uint16_t |
fbm16 inline |
Fractal Brownian motion at 16 bits: octaves samples at doubling frequency, halving amplitude. |
uint16_t |
[fbm16] inline |
The same sum with a third axis, so a volumetric fixture samples a real field. |
uint8_t |
fbm8 inline |
Fractal Brownian motion: octaves samples at doubling frequency, halving amplitude, returned normalized to 0..255. octaves=1 is plain noise; 3-4 is the usual cloud look. |
uint8_t |
[fbm8] inline |
3D fbm: the same sum with a z axis, so a 2D effect can use z as time for a field that evolves in place rather than scrolling past. |
uint8_t |
turbulence8 inline |
Turbulence: fbm over |noise - 128|, which creases the field where it crosses the midpoint. The creases are what read as billowing smoke and flame rather than soft cloud. |
uint8_t |
[turbulence8] inline |
3D turbulence: the same creased sum with a z axis. |
uint8_t |
warpImpl inline |
Domain warp: displace the sample coordinate by a noise field, then sample there. strength is how far the displacement reaches, in the same fixed-point units as the coordinates. |
uint8_t |
warp8 inline |
3D domain warp: the field flows through a volume rather than through a plane. |
uint8_t |
[warp8] inline |
Displace the sample coordinate by a noise field, then sample there. |
void |
curl16 inline |
Curl of a noise potential: a velocity field that cannot pile up or thin out. |
void |
[curl16] inline |
The 2D form: the same field at a fixed z, which is what a panel wants. |
inoise8¶
constexpr
inoise8¶
constexpr
inoise8¶
constexpr
inoise16¶
constexpr
1D gradient noise at 16 bits. x is 16.16 fixed point: the whole part selects the cell, the fraction interpolates within it.
inoise16¶
constexpr
2D gradient noise at 16 bits: the common case for a panel.
inoise16¶
constexpr
3D gradient noise at 16 bits. z is the axis a 2D effect uses as time, so the field evolves in place instead of scrolling past.
fbm16¶
inline
Fractal Brownian motion at 16 bits: octaves samples at doubling frequency, halving amplitude.
fbm16¶
inline
The same sum with a third axis, so a volumetric fixture samples a real field.
fbm8¶
inline
Fractal Brownian motion: octaves samples at doubling frequency, halving amplitude, returned normalized to 0..255. octaves=1 is plain noise; 3-4 is the usual cloud look.
fbm8¶
inline
3D fbm: the same sum with a z axis, so a 2D effect can use z as time for a field that evolves in place rather than scrolling past.
turbulence8¶
inline
Turbulence: fbm over |noise - 128|, which creases the field where it crosses the midpoint. The creases are what read as billowing smoke and flame rather than soft cloud.
turbulence8¶
inline
3D turbulence: the same creased sum with a z axis.
warpImpl¶
inline
template<int Dims> inline uint8_t warpImpl(uint32_t x, uint32_t y, uint32_t z, uint16_t strength, uint8_t octaves)
Domain warp: displace the sample coordinate by a noise field, then sample there. strength is how far the displacement reaches, in the same fixed-point units as the coordinates.
3D domain warp: the same displacement with a z axis, so the field flows through a volume. Three probes, each offset by its own constant, so the axes displace independently.
warp8¶
inline
3D domain warp: the field flows through a volume rather than through a plane.
warp8¶
inline
Displace the sample coordinate by a noise field, then sample there.
curl16¶
inline
inline void curl16(uint32_t x, uint32_t y, uint32_t z, int32_t strength, int32_t & vx, int32_t & vy, uint32_t eps = 4096)
Curl of a noise potential: a velocity field that cannot pile up or thin out.
curl16¶
inline
inline void curl16(uint32_t x, uint32_t y, int32_t strength, int32_t & vx, int32_t & vy, uint32_t eps = 4096)
The 2D form: the same field at a fixed z, which is what a panel wants.
FadeTable¶
src/core/util/noise.h:149The quintic fade as a table, because the polynomial needs a wide intermediate per sample.
Public Attributes¶
uint32_t v
Public Methods¶
constexpr inline constexpr FadeTable()
Tier16¶
src/core/util/noise.h:182Public Static Attributes¶
constexpr int kFrac = 16
: How many fraction bits a coordinate carries at this tier.
Public Static Methods¶
constexpr static inline constexpr uint32_t fade(int32_t f)
: The quintic fade for this tier's fraction width.
constexpr static inline constexpr int32_t blend(int32_t a, int32_t b, uint32_t w)
: Operands reach 2^18 against a 2^16 weight: a widening multiply (two on a 32-bit core).
Tier8¶
src/core/util/noise.h:172The two tiers as policies for the one core below: the fraction width, the fade of a fraction.
The blend a→b by a 0.16 weight sized to the tier's operand range.
Public Static Attributes¶
constexpr int kFrac = 8
: How many fraction bits a coordinate carries at this tier.
Public Static Methods¶
constexpr static inline constexpr uint32_t fade(int32_t f)
: The quintic fade for this tier's fraction width.
constexpr static inline constexpr int32_t blend(int32_t a, int32_t b, uint32_t w)
: Operands stay within ±2^11 (three offsets of a 256 cell), so the product fits 32 bits.
More info¶
The value varies smoothly across space, so neighboring coordinates give similar results, unlike a raw hash. Sample it across a grid for clouds, plasma or fire-like fields, and scroll a coordinate to animate. One gradient set and one interpolation serve all three dimensions.
The algorithm¶
This is Perlin's improved noise. A pseudo-random gradient sits at every lattice corner, chosen by a hash from the twelve cube-edge directions. Its dot product with the offset from the corner, a quintic fade on the fraction, and a linear blend across the corners give the value.
Gradient noise is zero at every lattice point and has no value bias toward the corners. That is why it reads as smooth and isotropic where value noise, which picks a random value per corner, reads blocky and axis-aligned at low frequency.
Two dimensions is three with the last offset at zero. One takes gradients of its own, since the cube-edge set projected onto a single axis is zero for six codes of sixteen. The cost scales with the corners: two, four and eight dot products.
Why the gradients are a table¶
A table rather than the usual select expression, because the selects compile to branches and a core without a branch predictor pays for each one. The select form was forty-four branches per two-dimensional sample on this target; the table is loads and small multiplies.
Why the core works a slice at a time¶
One instantiation per arity compiles to straight-line code over exactly its corners. A slice is two or four corners with the blends between them, and three dimensions is that slice at two depths blended by the depth fade.
One body with eight corners in flight spilled half its registers on a sixteen-register window, fifty-two stack stores a sample. The slice form keeps at most four.
Why the scale is per arity¶
Halving regardless is right for two and three dimensions and wrong for one, which then spans only the middle half of the range. Measured, a one-dimensional field covered the central half of the byte range and read washed out beside the same field sampled in two.
The three standard compositions¶
One sample is a smooth blur; the looks people recognize come from composing samples, and each composition is a few lines over the base rather than a new field generator.
Summing octaves at doubling frequency and halving amplitude turns the blur into cloud, terrain or smoke structure: large shapes with fine detail on them. The same sum over the absolute deviation creases the field, and those creases read as billows and flame. Sampling at a coordinate that noise itself displaced is the warp, which produces the flowing, marbled look.
Cost is stated per call because it decides whether an effect fits. Each octave is one sample, so three octaves cost three, and a warp costs its own probes plus the field it then samples. On a large fixture that multiplies by the pixel count.
What a domain warp is for¶
A warp displaces the sample coordinate by a noise field and then samples there, the strength saying how far that reaches in the same units as the coordinates. It is the primitive behind the flowing, marbled look. The field stops looking like a texture laid on the grid and starts looking like something moving through it, for two extra samples.
Why the volumetric warp takes three probes¶
It uses three rather than two, each offset by its own constant so the axes displace independently. Sampling one field three times would move everything along a diagonal.
Its octave count has no default, unlike the flat form's. With one, the two four-argument calls would both be viable and every existing call ambiguous, so requiring it makes the arity say which field the caller means.
Both share one body, so the two cannot drift apart. Each still compiles to exactly its own arity: the flat instantiation walks four corners and never touches the third axis. Calling the volumetric body with a zero third coordinate would have been simpler and measured nearly twice as slow. Every probe and the inner sum then walk eight corners to reach the same answer.
Why curl noise cannot clump¶
It is the perpendicular gradient of a scalar field, from Bridson's curl-noise paper. Taking the gradient of a potential and turning it a quarter turn gives a field whose divergence is zero by construction. Whatever flows into a region flows out again.
That is what separates it from sampling noise straight into a velocity, where the field has sources and sinks. Anything carried by such a field collects in the sinks and drains from the sources, which looks like clumping rather than flow.
The output is scaled by a strength in the caller's own units, and the sampling distance for the central difference is in the same coordinates as the field. Too small and the difference is quantization noise; too large and the curl is of a blurrier field than the one being sampled. The default is a sixteenth of a cell.
The output uses its full range¶
The dot product is signed offsets added, so the blend spans about a cell rather than the tighter bound a unit vector suggests. An exact search over every gradient choice at every fraction gives half a cell in one dimension, a cell in two and slightly more in three.
Each tier halves the raw blend onto its range, so the midpoint is the field's mean and both ends are reached. The clamp is a guard that only the three-dimensional extreme touches.
How a coordinate is read¶
A coordinate is fixed point scaled however the caller likes: its high byte selects the noise cell and its low byte the interpolation position within. A larger step per pixel is therefore finer noise, with more cells across the grid, and a smaller step is broader and smoother.
What the lattice hash costs¶
Each coordinate is multiplied by its own odd constant and the results xored, which shares work across a cell's corners: six multiplies per three-dimensional sample rather than twenty-four. Then one shift, one multiply, and the top nibble of the product, its best-mixed part.
Four bits is all a gradient needs, so that is the whole hash. A byte-wide avalanche spent three multiplies per corner on bits nothing read. Checked against it the sixteen codes are uniform to well under a percent, with adjacent corners as independent as the avalanche managed. A linear pre-mix with a single multiply was not: adjacent corners correlated six-fold, which reads as lattice patterns.
Both tiers share the hash, so the 16-bit field is the 8-bit one at finer resolution rather than an unrelated field.
The implementation is written fresh and integer throughout: the hash and the fade table are ours, and the gradient trick is Perlin's.