Noise
Categories:
The Noise namespace (GS_Core::Noise) provides a project-owned, stateless, deterministic, thread-safe Perlin noise toolkit. It backs every continuous-noise subsystem in the framework — PhantomCam Noise Profiles and the PerlinNoise / ImpulseNoise additive stages — plus any future UI wobble, unit idle-jitter, or VFX parameter modulation consumer that needs smooth time-varying values.
The same NoiseLayer struct documented below is what CameraNoiseProfile stores in its six per-axis vectors — no duplication. The utility is intentionally outside PhantomCam so non-camera consumers can adopt it without pulling a camera dependency.
Used by: gs_phantomcam (camera noise and impulse stages). Open for adoption by any consumer needing time-varying smooth modulation.
Contents
- Signed Perlin
- Fractal Perlin
- NoiseLayer Struct
- LayeredPerlin1D
- Unit-Range Wrappers
- Decorrelation Pattern
- Determinism
- Licensing Posture
- Consumers
- See Also
Signed Perlin
Classic Perlin noise. Output range approximately [-1, 1], statistically zero-biased — typical samples cluster near zero, not the extremes. Amplitude tuning at the consumer side accounts for this.
float Perlin1D(float x);
float Perlin2D(float x, float y);
float Perlin2D(const AZ::Vector2& p);
float Perlin3D(float x, float y, float z);
float Perlin3D(const AZ::Vector3& p);
Normalization details:
- 1D scaled by
* 0.125(gradient outputs land in ~[-8, 8]). - 2D scaled by
* 1/3(gradient set in ~[-3, 3]). - 3D natively in ~
[-1, 1]with aGetClampsafety bound.
Fractal Perlin
Multi-octave Perlin renormalized so output stays ~[-1, 1] regardless of octave count. Useful for environmental / spatial noise (terrain undulations, ambient flicker, foliage rustle).
float FractalPerlin1D(float x,
int octaves = 4,
float persistence = 0.5f,
float lacunarity = 2.0f);
float FractalPerlin2D(const AZ::Vector2& p,
int octaves = 4,
float persistence = 0.5f,
float lacunarity = 2.0f);
float FractalPerlin3D(const AZ::Vector3& p,
int octaves = 4,
float persistence = 0.5f,
float lacunarity = 2.0f);
Each octave doubles frequency (lacunarity) and halves amplitude (persistence) by default. The accumulated total is divided by the amplitude sum so the output remains ~[-1, 1] regardless of octave count.
| Parameter | Default | Clamping |
|---|---|---|
octaves | 4 | Clamped to [1, 16]. |
persistence | 0.5 | Non-positive values bumped to a safe minimum. |
lacunarity | 2.0 | Non-positive values bumped to a safe minimum. |
Camera noise does NOT use Fractal. PhantomCam’s noise stages use LayeredPerlin1D below, which gives authors explicit per-layer control over the (amp, freq, phase) stack rather than the geometric octave progression Fractal enforces.
NoiseLayer Struct
A single layer in a layered Perlin stack — amplitude, frequency, optional phase.
struct NoiseLayer
{
AZ_TYPE_INFO(NoiseLayer, "{2D5E9B7A-4C03-4518-8A1C-6F7B1E3D9C40}");
AZ_CLASS_ALLOCATOR(NoiseLayer, AZ::SystemAllocator);
float m_amplitude = 0.0f; // consumer units (m, deg, unitless)
float m_frequency = 1.0f; // Hz of input `t`
float m_phase = 0.0f; // per-layer time offset
static void Reflect(AZ::ReflectContext* context);
};
Reflection
NoiseLayer::Reflect registers the type once, guarded with FindClassData (required because multiple owners may reflect it). On-disk field strings are Amplitude, Frequency, Phase — capitalized, no m_ prefix. The EditContext UI handler uses AutoExpand: true so layers display inline in the Asset Editor without further user action.
This is the same struct CameraNoiseProfile stores in its six per-axis vectors. Other future consumers reuse the same type.
LayeredPerlin1D
Sum of per-layer Perlin1D samples at time t.
float LayeredPerlin1D(float t,
const AZStd::vector<NoiseLayer>& layers,
float decorrelate = 0.0f);
Evaluation:
result = Σ Perlin1D(t * layer.frequency + layer.phase + decorrelate) * layer.amplitude
Key behaviors
- Layers with
m_amplitude == 0orm_frequency == 0are skipped by the inner loop. Authors can leave dead rows in a profile without paying for them. - Output is NOT renormalized. Unlike Fractal, amplitudes sum directly. Authors choose amplitudes that add up to their intended envelope.
- Layer ordering does not matter — the sum is commutative.
- The
decorrelateparameter is added to every layer’s input — see Decorrelation Pattern.
Camera-shake authoring conventions
| Quantity | Typical range |
|---|---|
| Position layer amplitude | 0.002 – 0.4 m |
| Rotation layer amplitude | 0.1 – 17 deg |
| Frequency | 0.1 – 60 Hz |
Concrete preset values live in the shipped .camnoiseprofile assets — see Noise Profiles for the asset library.
Unit-Range Wrappers
For consumers that want a 0..1 modulator (UI alpha pulses, scalar VFX drivers) rather than a signed delta.
inline float Perlin1DUnit(float x) { return Perlin1D(x) * 0.5f + 0.5f; }
inline float Perlin2DUnit(const AZ::Vector2& p) { return Perlin2D(p) * 0.5f + 0.5f; }
inline float Perlin3DUnit(const AZ::Vector3& p) { return Perlin3D(p) * 0.5f + 0.5f; }
A linear * 0.5 + 0.5 remap of the signed signal.
Decorrelation Pattern
For multi-channel consumers that share one stream’s shape but need independent values (camera 6-DoF noise is the canonical case), offset the sample point rather than reseeding:
const float chX = Perlin1D(t * freqX + 1000.0f);
const float chY = Perlin1D(t * freqY + 2000.0f);
const float chZ = Perlin1D(t * freqZ + 3000.0f);
Offsets of a few hundred units in the sample space give effectively independent samples from the shared gradient table. PhantomCam’s PerlinNoise stage applies a per-axis decorrelation offset to LayeredPerlin1D so the same profile drives all six axes without phase-locking them.
Determinism
- The permutation table is built once at static init from a fixed seed. No runtime randomness.
Perlin1D(t)returns the same value every call across runs, platforms, and threads — no shared mutable state in the hot path.- Camera noise stages own
m_timeper-instance — replays at the samem_timereproduce the same sway frame-for-frame.
Performance
| Operation | Cost |
|---|---|
Perlin1D hot path | 2 table lookups, 2 gradient evals, 1 fade, 1 lerp. Effectively free at gameplay rates. |
LayeredPerlin1D | Linear in the number of non-skipped layers. Typical 1–3 layers per axis → ~18 Perlin1D evaluations per cam tick across all six axes. |
FractalPerlin* | Linear in octaves. Default 4 octaves is comparable to a 4-layer LayeredPerlin1D. |
Licensing Posture
The toolkit is license-clean for commercial distribution. The header carries a longform license note; the summary:
- Ken Perlin’s noise algorithm (1983, fade refinement 2002) is not patented and never was.
- The expired Simplex patent (US 6,867,776) does not apply — this is classic Perlin, and even Simplex would now be free.
- The permutation table is not transcribed from any reference implementation. It is generated at static init from a deterministic LCG (Numerical Recipes constants) seeded with
0x4E435347(“GSCN” = GS Core Noise). The 256-byte table is duplicated to 512 so the hot path wraps with& 511instead of branching. - Gradient sets and the quintic fade (
6t⁵ − 15t⁴ + 10t³) are public-domain math.
Consumers
| Subsystem | Function used | Time source |
|---|---|---|
PhantomCam PerlinNoise additive stage | LayeredPerlin1D × 6 axes | m_time += deltaTime since activation. |
PhantomCam ImpulseNoise additive stage | LayeredPerlin1D × 6 axes, ADSR-enveloped | m_elapsed since Trigger(strength). |
Future consumers will live in this table as they ship — UI wobble, unit idle-jitter, VFX scalar drivers, etc. The utility is namespaced under GS_Core::Noise precisely so other gems can adopt it without pulling PhantomCam.
See Also
Related GS_Core utilities:
- Springs — spring-damper smoothing. Use Springs for one-shot smoothing toward a goal; use Noise for sustained time-varying modulation around a baseline.
- Curves —
CurveTypeenum for easing curves. Often paired with Noise — an ADSR envelope (curve-shaped) gates a Layered Perlin burst, for example. - Orbital Solver — pivot-aware blend primitive. Companion utility consumed by the same PhantomCam systems.
PhantomCam consumer pages:
- Noise Profiles —
.camnoiseprofileasset that storesAZStd::vector<NoiseLayer>per axis. - Additive Stage Variants — Noise —
PerlinNoiseandImpulseNoisekinematic models.
Get GS_Core
Explore other GS_Play gems on the product page.