This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Noise

Project-owned Perlin noise toolkit — signed Perlin 1D/2D/3D, fractal multi-octave, layered (amp, freq, phase) stacks with the NoiseLayer struct, unit-range wrappers, and decorrelation patterns. Backs PhantomCam noise stages and any time-varying value modulation consumer.

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

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 a GetClamp safety 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.

ParameterDefaultClamping
octaves4Clamped to [1, 16].
persistence0.5Non-positive values bumped to a safe minimum.
lacunarity2.0Non-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 == 0 or m_frequency == 0 are 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 decorrelate parameter is added to every layer’s input — see Decorrelation Pattern.

Camera-shake authoring conventions

QuantityTypical range
Position layer amplitude0.002 – 0.4 m
Rotation layer amplitude0.1 – 17 deg
Frequency0.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_time per-instance — replays at the same m_time reproduce the same sway frame-for-frame.

Performance

OperationCost
Perlin1D hot path2 table lookups, 2 gradient evals, 1 fade, 1 lerp. Effectively free at gameplay rates.
LayeredPerlin1DLinear 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 & 511 instead of branching.
  • Gradient sets and the quintic fade (6t⁵ − 15t⁴ + 10t³) are public-domain math.

Consumers

SubsystemFunction usedTime source
PhantomCam PerlinNoise additive stageLayeredPerlin1D × 6 axesm_time += deltaTime since activation.
PhantomCam ImpulseNoise additive stageLayeredPerlin1D × 6 axes, ADSR-envelopedm_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.
  • CurvesCurveType enum 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:


Get GS_Core

Explore other GS_Play gems on the product page.