Skip to content

@typegpu/noise

The @typegpu/noise package offers a set of pseudo-random utilities for use in TypeGPU and WebGPU projects. At its core, the package provides a pseudo-random number generator for uniformly distributed values (same probability for all numbers) in the range [0, 1), as well as higher-level utilities built on top.

It also features a Perlin noise implementation, which is useful for generating smooth, natural-looking variations in visual effects, terrains, and other procedural elements.

Each utility function described in this guide is usable from the context of both TypeGPU and vanilla WebGPU. This makes it really simple to leverage the TypeGPU ecosystem in your WebGPU projects, without needing to migrate large parts of your codebase.

Calling utility functions from TypeGPU functions links them automatically. In the example below, resolving randomVec2f into a shader will include the code for randf.sample and all of its dependencies.

import {
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
} from '@typegpu/noise';
const
const randomVec2f: TgpuFn<() => d.Vec2f>
randomVec2f
=
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
.
fn: <[], d.Vec2f>(argTypes: [], returnType: d.Vec2f) => TgpuFnShell<[], d.Vec2f> (+1 overload)
fn
([],
import d
d
.
const vec2f: d.Vec2f
export vec2f

Schema representing vec2f - a vector with 2 elements of type f32. Also a constructor function for this vector value.

@example const vector = d.vec2f(); // (0.0, 0.0) const vector = d.vec2f(1); // (1.0, 1.0) const vector = d.vec2f(0.5, 0.1); // (0.5, 0.1)

@example const buffer = root.createBuffer(d.vec2f, d.vec2f(0, 1)); // buffer holding a d.vec2f value, with an initial value of vec2f(0, 1);

vec2f
)(() => {
const
const x: number
x
=
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
sample: () => number
sample
(); // returns a random float in [0, 1)
const
const y: number
y
=
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
sample: () => number
sample
(); // returns the next random float in [0, 1)
return
import d
d
.
function vec2f(x: number, y: number): d.v2f (+3 overloads)
export vec2f

Schema representing vec2f - a vector with 2 elements of type f32. Also a constructor function for this vector value.

@example const vector = d.vec2f(); // (0.0, 0.0) const vector = d.vec2f(1); // (1.0, 1.0) const vector = d.vec2f(0.5, 0.1); // (0.5, 0.1)

@example const buffer = root.createBuffer(d.vec2f, d.vec2f(0, 1)); // buffer holding a d.vec2f value, with an initial value of vec2f(0, 1);

vec2f
(
const x: number
x
,
const y: number
y
);
});
// ...

The tgpu.resolve API can be used to inject TypeGPU resources (constants, functions, etc.) into a WGSL shader.

In the example below, the sample function is accessed both as a named function, and as part of the randf object. The resolution mechanism handles deduplication out of the box, as well as omits code that is unused by your shader, so only one definition of sample will be included in the final shader.

import {
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
} from '@typegpu/noise';
// `typegpu` is necessary to inject library code into your custom shader
import
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
from 'typegpu';
const
const shader: string
shader
=
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
.
resolve: (options: TgpuResolveOptions) => string

Resolves a template with external values. Each external will get resolved to a code string and replaced in the template. Any dependencies of the externals will also be resolved and included in the output.

@paramoptions - The options for the resolution.

@returnsThe resolved code.

@example

const Gradient = d.struct({
from: d.vec3f,
to: d.vec3f,
});
const resolved = tgpu.resolve({
template: `
fn getGradientAngle(gradient: Gradient) -> f32 {
return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);
}
`,
externals: {
Gradient,
},
});
console.log(resolved);
// struct Gradient_0 {
// from: vec3f,
// to: vec3f,
// }
// fn getGradientAngle(gradient: Gradient_0) -> f32 {
// return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);
// }

resolve
({
TgpuResolveOptions.template?: string | undefined

The code template to use for the resolution. All external names will be replaced with their resolved values.

@default''

template
: `
fn random_vec2f() -> vec2f {
// Accessing the 'sample' function directly
let x = sample();
// Accessing the 'sample' function as part of the 'randf' object
let y = randf.sample();
return vec2f(x, y);
}
// ...
`,
TgpuResolveOptions.externals: Record<string, object | Wgsl>

Map of external names to their resolvable values.

externals
: {
sample: TgpuFn<() => d.F32>
sample
:
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
sample: TgpuFn<() => d.F32>
sample
,
randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
},
});
// The shader is just a WGSL string
shader;
const shader: string

Does this mean we allow object access inside of WGSL shaders?… yes, yes we do 🙈. To learn more about resolution, check our “Resolve” guide

The @typegpu/noise package provides a pseudo-random number generator (PRNG) that generates uniformly distributed random numbers in the range [0, 1). Each call to randf.sample returns the next random float in the sequence, allowing for predictable and repeatable results. The seed can be set or reset using a set of randf.seedN functions, where N is the number of components our seed has.

import
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
from 'typegpu';
import * as
import d
d
from 'typegpu/data';
import {
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
} from '@typegpu/noise';
const
const main: TgpuFragmentFn<{}, d.Vec4f>
main
=
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
['~unstable'].
fragmentFn: <{
pos: d.BuiltinPosition;
}, d.Vec4f>(options: {
in: {
pos: d.BuiltinPosition;
};
out: d.Vec4f;
}) => TgpuFragmentFnShell<{
pos: d.BuiltinPosition;
}, d.Vec4f> (+1 overload)
fragmentFn
({
in: {
pos: d.BuiltinPosition;
}
in
: {
pos: d.BuiltinPosition
pos
:
import d
d
.
const builtin: {
readonly vertexIndex: d.BuiltinVertexIndex;
readonly instanceIndex: d.BuiltinInstanceIndex;
readonly position: d.BuiltinPosition;
readonly clipDistances: d.BuiltinClipDistances;
... 10 more ...;
readonly subgroupSize: BuiltinSubgroupSize;
}
export builtin
builtin
.
position: d.BuiltinPosition
position
},
out: d.Vec4f
out
:
import d
d
.
const vec4f: d.Vec4f
export vec4f

Schema representing vec4f - a vector with 4 elements of type f32. Also a constructor function for this vector value.

@example const vector = d.vec4f(); // (0.0, 0.0, 0.0, 0.0) const vector = d.vec4f(1); // (1.0, 1.0, 1.0, 1.0) const vector = d.vec4f(1, 2, 3, 4.5); // (1.0, 2.0, 3.0, 4.5)

@example const buffer = root.createBuffer(d.vec4f, d.vec4f(0, 1, 2, 3)); // buffer holding a d.vec4f value, with an initial value of vec4f(0, 1, 2, 3);

vec4f
,
})(({
pos: d.v4f
pos
}) => {
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
seed2: (seed: d.v2f) => void
seed2
(
pos: d.v4f
pos
.
Swizzle2<v2f, v3f, v4f>.xy: d.v2f
xy
); // Generate a different sequence for each pixel
return
import d
d
.
function vec4f(x: number, y: number, z: number, w: number): d.v4f (+9 overloads)
export vec4f

Schema representing vec4f - a vector with 4 elements of type f32. Also a constructor function for this vector value.

@example const vector = d.vec4f(); // (0.0, 0.0, 0.0, 0.0) const vector = d.vec4f(1); // (1.0, 1.0, 1.0, 1.0) const vector = d.vec4f(1, 2, 3, 4.5); // (1.0, 2.0, 3.0, 4.5)

@example const buffer = root.createBuffer(d.vec4f, d.vec4f(0, 1, 2, 3)); // buffer holding a d.vec4f value, with an initial value of vec4f(0, 1, 2, 3);

vec4f
(
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
sample: () => number
sample
(), // returns a random float in [0, 1)
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
... 4 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
sample: () => number
sample
(), // returns the next random float in [0, 1)
0.0,
1.0
);
});

There are higher-level utilities built on top of randf.sample:

  • inUnitCircle - returns a random 2D vector uniformly distributed inside a unit circle
  • inUnitCube - returns a random 3D vector uniformly distributed inside a unit cube
  • onHemisphere - returns a random 3D vector uniformly distributed on the surface of the upper hemisphere oriented accordingly to given normal vector
  • onUnitSphere - returns a random 3D vector uniformly distributed on the surface of a unit sphere

The package exports an implementation for both 2D and 3D Perlin noise, perlin2d and perlin3d, respectively. Using it is as simple as calling the .sample function with the desired coordinates, and it returns a value in the range [-1, 1].

import {
import perlin2d
perlin2d
} from '@typegpu/noise';
const
const main: TgpuFragmentFn<{}, d.Vec4f>
main
=
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
['~unstable'].
fragmentFn: <{
pos: d.BuiltinPosition;
}, d.Vec4f>(options: {
in: {
pos: d.BuiltinPosition;
};
out: d.Vec4f;
}) => TgpuFragmentFnShell<{
pos: d.BuiltinPosition;
}, d.Vec4f> (+1 overload)
fragmentFn
({
in: {
pos: d.BuiltinPosition;
}
in
: {
pos: d.BuiltinPosition
pos
:
import d
d
.
const builtin: {
readonly vertexIndex: d.BuiltinVertexIndex;
readonly instanceIndex: d.BuiltinInstanceIndex;
readonly position: d.BuiltinPosition;
readonly clipDistances: d.BuiltinClipDistances;
... 10 more ...;
readonly subgroupSize: BuiltinSubgroupSize;
}
export builtin
builtin
.
position: d.BuiltinPosition
position
},
out: d.Vec4f
out
:
import d
d
.
const vec4f: d.Vec4f
export vec4f

Schema representing vec4f - a vector with 4 elements of type f32. Also a constructor function for this vector value.

@example const vector = d.vec4f(); // (0.0, 0.0, 0.0, 0.0) const vector = d.vec4f(1); // (1.0, 1.0, 1.0, 1.0) const vector = d.vec4f(1, 2, 3, 4.5); // (1.0, 2.0, 3.0, 4.5)

@example const buffer = root.createBuffer(d.vec4f, d.vec4f(0, 1, 2, 3)); // buffer holding a d.vec4f value, with an initial value of vec4f(0, 1, 2, 3);

vec4f
,
})(({
pos: d.v4f
pos
}) => {
const
const noise: number
noise
=
import perlin2d
perlin2d
.
function sample(pos: d.v2f): number
export sample

Returns value of Perlin Noise at point pos

sample
(
pos: d.v4f
pos
.
Swizzle2<v2f, v3f, v4f>.xy: d.v2f
xy
.
vecInfixNotation<v2f>.mul(other: number): d.v2f (+2 overloads)
mul
(0.1)); // Scale the coordinates for smoother noise
return
import d
d
.
function vec4f(x: number, y: number, z: number, w: number): d.v4f (+9 overloads)
export vec4f

Schema representing vec4f - a vector with 4 elements of type f32. Also a constructor function for this vector value.

@example const vector = d.vec4f(); // (0.0, 0.0, 0.0, 0.0) const vector = d.vec4f(1); // (1.0, 1.0, 1.0, 1.0) const vector = d.vec4f(1, 2, 3, 4.5); // (1.0, 2.0, 3.0, 4.5)

@example const buffer = root.createBuffer(d.vec4f, d.vec4f(0, 1, 2, 3)); // buffer holding a d.vec4f value, with an initial value of vec4f(0, 1, 2, 3);

vec4f
(
const noise: number
noise
,
const noise: number
noise
,
const noise: number
noise
, 1); // Use the noise value for RGB channels
});

This simple usage is enough for most cases, but by default, perlin.sample computes the underlying gradients on-demand, per pixel. This can be inefficient for large images or when the same noise is sampled multiple times. To improve performance, you can precompute the gradients using either a Static or a Dynamic cache. In our tests, the efficiency gain can be up to 10x!

A static cache presumes that the domain of the noise function is fixed, and cannot change between shader invocations.

const
const cache: perlin3d.StaticPerlin3DCache
cache
=
import perlin3d
perlin3d
.
function staticCache(options: {
root: TgpuRoot;
size: d.v3u;
}): perlin3d.StaticPerlin3DCache
export staticCache

A statically-sized cache for perlin noise generation, which reduces the amount of redundant calculations if sampling is done more than once. If you'd like to change the size of the cache at runtime, see perlin3d.dynamicCacheConfig.

--- Basic usage

@example

const mainFragment = tgpu.fragmentFn({ out: d.vec4f })(() => {
const n = perlin3d.sample(d.vec3f(1.1, 2.2, 3.3));
// ...
});
const cache = perlin3d.staticCache({ root, size: d.vec3u(10, 10, 1) });
const pipeline = root
// Plugging the cache into the pipeline
.pipe(cache.inject())
// ...
.withFragment(mainFragment)
.createPipeline();

--- Wrapped coordinates

If the noise generator samples outside of the bounds of this cache, the space is wrapped around.

@example

const cache = perlin3d.staticCache({ root, size: d.vec3u(10, 10, 1) });
// ...
const value = perlin3d.sample(d.vec3f(0.5, 0, 0));
const wrappedValue = perlin3d.sample(d.vec3f(10.5, 0, 0)); // the same as `value`!

staticCache
({
root: TgpuRoot

The root to use for allocating resources.

root
,
size: d.v3u

The size of the cache.

size
:
import d
d
.
function vec3u(x: number, y: number, z: number): d.v3u (+5 overloads)
export vec3u

Schema representing vec3u - a vector with 3 elements of type u32. Also a constructor function for this vector value.

@example const vector = d.vec3u(); // (0, 0, 0) const vector = d.vec3u(1); // (1, 1, 1) const vector = d.vec3u(1, 2, 3); // (1, 2, 3)

@example const buffer = root.createBuffer(d.vec3u, d.vec3u(0, 1, 2)); // buffer holding a d.vec3u value, with an initial value of vec3u(0, 1, 2);

vec3u
(10, 10, 1) });
const
const pipeline: TgpuComputePipeline
pipeline
=
const root: TgpuRoot
root
['~unstable']
// Plugging the cache into the pipeline
.
function pipe(transform: (cfg: Configurable) => Configurable): WithBinding
pipe
(
const cache: perlin3d.StaticPerlin3DCache
cache
.
StaticPerlin3DCache.inject(): (cfg: Configurable) => Configurable
inject
())
// ...
.
WithBinding.withCompute<{}>(entryFn: TgpuComputeFn<{}>): WithCompute
withCompute
(
const main: TgpuComputeFn<{}>
main
)
.
WithCompute.createPipeline(): TgpuComputePipeline
createPipeline
();

Or in WebGPU:

const
const root: TgpuRoot
root
=
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
.
initFromDevice: (options: InitFromDeviceOptions) => TgpuRoot

Creates a root from the given device, instead of requesting it like

@seeinit. *

@example

const device: GPUDevice = ...;
const root = tgpu.initFromDevice({ device });

initFromDevice
({
device: GPUDevice
device
});
const
const cache: perlin3d.StaticPerlin3DCache
cache
=
import perlin3d
perlin3d
.
function staticCache(options: {
root: TgpuRoot;
size: d.v3u;
}): perlin3d.StaticPerlin3DCache
export staticCache

A statically-sized cache for perlin noise generation, which reduces the amount of redundant calculations if sampling is done more than once. If you'd like to change the size of the cache at runtime, see perlin3d.dynamicCacheConfig.

--- Basic usage

@example

const mainFragment = tgpu.fragmentFn({ out: d.vec4f })(() => {
const n = perlin3d.sample(d.vec3f(1.1, 2.2, 3.3));
// ...
});
const cache = perlin3d.staticCache({ root, size: d.vec3u(10, 10, 1) });
const pipeline = root
// Plugging the cache into the pipeline
.pipe(cache.inject())
// ...
.withFragment(mainFragment)
.createPipeline();

--- Wrapped coordinates

If the noise generator samples outside of the bounds of this cache, the space is wrapped around.

@example

const cache = perlin3d.staticCache({ root, size: d.vec3u(10, 10, 1) });
// ...
const value = perlin3d.sample(d.vec3f(0.5, 0, 0));
const wrappedValue = perlin3d.sample(d.vec3f(10.5, 0, 0)); // the same as `value`!

staticCache
({
root: TgpuRoot

The root to use for allocating resources.

root
,
size: d.v3u

The size of the cache.

size
:
import d
d
.
function vec3u(x: number, y: number, z: number): d.v3u (+5 overloads)
export vec3u

Schema representing vec3u - a vector with 3 elements of type u32. Also a constructor function for this vector value.

@example const vector = d.vec3u(); // (0, 0, 0) const vector = d.vec3u(1); // (1, 1, 1) const vector = d.vec3u(1, 2, 3); // (1, 2, 3)

@example const buffer = root.createBuffer(d.vec3u, d.vec3u(0, 1, 2)); // buffer holding a d.vec3u value, with an initial value of vec3u(0, 1, 2);

vec3u
(10, 10, 1) });
const {
const code: string
code
,
const usedBindGroupLayouts: TgpuBindGroupLayout<Record<string, TgpuLayoutEntry | null>>[]
usedBindGroupLayouts
,
const catchall: [number, TgpuBindGroup] | undefined
catchall
} =
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
.
resolveWithContext: (options: TgpuResolveOptions) => ResolutionResult

Resolves a template with external values. Each external will get resolved to a code string and replaced in the template. Any dependencies of the externals will also be resolved and included in the output.

@paramoptions - The options for the resolution.

@returns

@example

const Gradient = d.struct({
from: d.vec3f,
to: d.vec3f,
});
const { code, usedBindGroupLayouts, catchall } = tgpu.resolveWithContext({
template: `
fn getGradientAngle(gradient: Gradient) -> f32 {
return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);
}
`,
externals: {
Gradient,
},
});
console.log(code);
// struct Gradient_0 {
// from: vec3f,
// to: vec3f,
// }
// fn getGradientAngle(gradient: Gradient_0) -> f32 {
// return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);
// }

resolveWithContext
({
TgpuResolveOptions.template?: string | undefined

The code template to use for the resolution. All external names will be replaced with their resolved values.

@default''

template
: `
fn main() {
let value = perlin3d.sample(vec3f(0.5, 0., 0.));
let wrappedValue = perlin3d.sample(vec3f(10.5, 0., 0.)); // the same as 'value'!
// ...
}
// ...
`,
TgpuResolveOptions.externals: Record<string, object | Wgsl>

Map of external names to their resolvable values.

externals
: {
perlin3d: typeof perlin3d
perlin3d
},
TgpuResolveOptions.config?: ((cfg: Configurable) => Configurable) | undefined

A function to configure the resolution context.

config
: (
cfg: Configurable
cfg
) =>
cfg: Configurable
cfg
.
Configurable.pipe(transform: (cfg: Configurable) => Configurable): Configurable
pipe
(
const cache: perlin3d.StaticPerlin3DCache
cache
.
StaticPerlin3DCache.inject(): (cfg: Configurable) => Configurable
inject
())
// Or just:
// config: cache.inject()
});

If you need to change the size of the noise domain at runtime (in between shader invocations) without having to recompile the shader, you have to use a dynamic cache. With it comes a more complex setup.

const
const cacheConfig: perlin3d.DynamicPerlin3DCacheConfig<"perlin3dCache__">
cacheConfig
=
import perlin3d
perlin3d
.
function dynamicCacheConfig(options?: {
prefix?: undefined;
}): perlin3d.DynamicPerlin3DCacheConfig<"perlin3dCache__"> (+1 overload)
export dynamicCacheConfig

Used to instantiate caches for perlin noise generation, which reduce the amount of redundant calculations if sampling is done more than once. Their domain can be changed at runtime, which makes this cache dynamic (as opposed to perlin3d.staticCache, which is simpler at the cost of rigidity).

@param

options A set of general options for instances of this cache configuration.

--- Basic usage

@example

const perlinCacheConfig = perlin3d.dynamicCacheConfig();
// Contains all resources that the perlin cache needs access to
const dynamicLayout = tgpu.bindGroupLayout({ ...perlinCacheConfig.layout });
// ...
const root = await tgpu.init();
// Instantiating the cache with an initial size.
const perlinCache = perlinCacheConfig.instance(root, d.vec3u(10, 10, 1));
const pipeline = root
// Plugging the cache into the pipeline
.pipe(perlinCacheConfig.inject(dynamicLayout.$))
// ...
.withFragment(mainFragment)
.createPipeline();
const frame = () => {
// A bind group to fulfill the resource needs of the cache
const group = root.createBindGroup(dynamicLayout, { ...perlinCache.bindings });
pipeline
.with(dynamicLayout, group)
// ...
.draw(3);
};

dynamicCacheConfig
();
// Holds all resources the perlin cache needs access to
const
const dynamicLayout: TgpuBindGroupLayout<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
dynamicLayout
=
const tgpu: {
fn: {
<Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>;
<Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
};
... 7 more ...;
'~unstable': {
...;
};
}
tgpu
.
bindGroupLayout: <{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>(entries: {
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}) => TgpuBindGroupLayout<...>
bindGroupLayout
({ ...
const cacheConfig: perlin3d.DynamicPerlin3DCacheConfig<"perlin3dCache__">
cacheConfig
.
DynamicPerlin3DCacheConfig<"perlin3dCache__">.layout: {
readonly perlin3dCache__size: {
uniform: d.Vec4u;
};
readonly perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}
layout
});
const
const pipeline: TgpuComputePipeline
pipeline
=
const root: TgpuRoot
root
['~unstable']
// Plugging the cache into the pipeline
.
function pipe(transform: (cfg: Configurable) => Configurable): WithBinding
pipe
(
const cacheConfig: perlin3d.DynamicPerlin3DCacheConfig<"perlin3dCache__">
cacheConfig
.
DynamicPerlin3DCacheConfig<"perlin3dCache__">.inject(layoutValue: {
readonly perlin3dCache__size: d.v4u;
readonly perlin3dCache__memory: d.v3f[];
}): (cfg: Configurable) => Configurable
inject
(
const dynamicLayout: TgpuBindGroupLayout<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
dynamicLayout
.
TgpuBindGroupLayout<{ perlin3dCache__size: { uniform: d.Vec4u; }; perlin3dCache__memory: { storage: (n: number) => WgslArray<Vec3f>; access: "readonly"; }; }>.$: {
perlin3dCache__size: d.v4u;
perlin3dCache__memory: d.v3f[];
}
$
))
// ...
.
WithBinding.withCompute<{}>(entryFn: TgpuComputeFn<{}>): WithCompute
withCompute
(
const main: TgpuComputeFn<{}>
main
)
.
WithCompute.createPipeline(): TgpuComputePipeline
createPipeline
();
// Instantiating the cache with an initial size.
const
const cache: perlin3d.DynamicPerlin3DCache<"perlin3dCache__">
cache
=
const cacheConfig: perlin3d.DynamicPerlin3DCacheConfig<"perlin3dCache__">
cacheConfig
.
DynamicPerlin3DCacheConfig<"perlin3dCache__">.instance(root: TgpuRoot, initialSize: d.v3u): perlin3d.DynamicPerlin3DCache<"perlin3dCache__">
instance
(
const root: TgpuRoot
root
,
import d
d
.
function vec3u(x: number, y: number, z: number): d.v3u (+5 overloads)
export vec3u

Schema representing vec3u - a vector with 3 elements of type u32. Also a constructor function for this vector value.

@example const vector = d.vec3u(); // (0, 0, 0) const vector = d.vec3u(1); // (1, 1, 1) const vector = d.vec3u(1, 2, 3); // (1, 2, 3)

@example const buffer = root.createBuffer(d.vec3u, d.vec3u(0, 1, 2)); // buffer holding a d.vec3u value, with an initial value of vec3u(0, 1, 2);

vec3u
(10, 10, 1));
// A function for updating the size of the cache
function
function initBindGroup(size: d.v3u): TgpuBindGroup<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
initBindGroup
(
size: d.v3u
size
:
import d
d
.
export v3u

Interface representing its WGSL vector type counterpart: vec3u or vec3. A vector with 3 elements of type u32

v3u
) {
const cache: perlin3d.DynamicPerlin3DCache<"perlin3dCache__">
cache
.
DynamicPerlin3DCache<"perlin3dCache__">.size: d.v3u
size
=
size: d.v3u
size
;
return
const root: TgpuRoot
root
.
TgpuRoot.createBindGroup<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>(layout: TgpuBindGroupLayout<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>, entries: ExtractBindGroupInputFromLayout<...>): TgpuBindGroup<...>

Creates a group of resources that can be bound to a shader based on a specified layout.

@example

const fooLayout = tgpu.bindGroupLayout({ foo: { uniform: d.vec3f }, bar: { texture: 'float' }, });

const fooBuffer = ...; const barTexture = ...;

const fooBindGroup = root.createBindGroup(fooLayout, { foo: fooBuffer, bar: barTexture, });

@paramlayout Layout describing the bind group to be created.

@paramentries A record with values being the resources populating the bind group and keys being their associated names, matching the layout keys.

createBindGroup
(
const dynamicLayout: TgpuBindGroupLayout<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
dynamicLayout
,
const cache: perlin3d.DynamicPerlin3DCache<"perlin3dCache__">
cache
.
DynamicPerlin3DCache<"perlin3dCache__">.bindings: {
perlin3dCache__size: TgpuBuffer<d.Vec4u> & UniformFlag;
perlin3dCache__memory: TgpuBuffer<d.WgslArray<d.Vec3f>> & StorageFlag;
}
bindings
);
}
let
let bindGroup: TgpuBindGroup<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
bindGroup
=
function initBindGroup(size: d.v3u): TgpuBindGroup<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
initBindGroup
(
import d
d
.
function vec3u(x: number, y: number, z: number): d.v3u (+5 overloads)
export vec3u

Schema representing vec3u - a vector with 3 elements of type u32. Also a constructor function for this vector value.

@example const vector = d.vec3u(); // (0, 0, 0) const vector = d.vec3u(1); // (1, 1, 1) const vector = d.vec3u(1, 2, 3); // (1, 2, 3)

@example const buffer = root.createBuffer(d.vec3u, d.vec3u(0, 1, 2)); // buffer holding a d.vec3u value, with an initial value of vec3u(0, 1, 2);

vec3u
(10, 10, 1));
// Dispatching the pipeline
const pipeline: TgpuComputePipeline
pipeline
.
TgpuComputePipeline.with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: TgpuBindGroup): TgpuComputePipeline
with
(
const dynamicLayout: TgpuBindGroupLayout<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
dynamicLayout
,
let bindGroup: TgpuBindGroup<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
bindGroup
)
.
TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number | undefined, z?: number | undefined): void
dispatchWorkgroups
(1);
// Can be called again to reinitialize the cache with
// a different domain size
let bindGroup: TgpuBindGroup<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
bindGroup
=
function initBindGroup(size: d.v3u): TgpuBindGroup<{
perlin3dCache__size: {
uniform: d.Vec4u;
};
perlin3dCache__memory: {
storage: (n: number) => d.WgslArray<d.Vec3f>;
access: "readonly";
};
}>
initBindGroup
(
import d
d
.
function vec3u(x: number, y: number, z: number): d.v3u (+5 overloads)
export vec3u

Schema representing vec3u - a vector with 3 elements of type u32. Also a constructor function for this vector value.

@example const vector = d.vec3u(); // (0, 0, 0) const vector = d.vec3u(1); // (1, 1, 1) const vector = d.vec3u(1, 2, 3); // (1, 2, 3)

@example const buffer = root.createBuffer(d.vec3u, d.vec3u(0, 1, 2)); // buffer holding a d.vec3u value, with an initial value of vec3u(0, 1, 2);

vec3u
(5, 5, 1));