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.
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:typeofrandSeed;
seed2:typeofrandSeed2;
seed3:typeofrandSeed3;
seed4:typeofrandSeed4;
...13 more ...;
onUnitSphere:typeofrandOnUnitSphere;
}
randf } from'@typegpu/noise';
// `typegpu` is necessary to inject library code into your custom shader
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.
@param ― options - The options for the resolution.
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.
@exampleconst 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:typeofrandSeed;
seed2:typeofrandSeed2;
seed3:typeofrandSeed3;
seed4:typeofrandSeed4;
...13 more ...;
onUnitSphere:typeofrandOnUnitSphere;
}
randf.
sample: ()=> number
sample(), // returns a random float in [0, 1)
const randf: {
seed:typeofrandSeed;
seed2:typeofrandSeed2;
seed3:typeofrandSeed3;
seed4:typeofrandSeed4;
...13 more ...;
onUnitSphere:typeofrandOnUnitSphere;
}
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. Most of the distributions can be derived from the fact that cumulative distribution functions (CDFs) are uniformly distributed on[0, 1] and can be easily inverted to generate random variables. We provide insights for probability enthusiasts. Let’s assume :
sampleExclusive - works the same as sample, but returns a value strictly between 0 and 1
exponential - returns a number based on the exponential distribution with a given rate. The rate must be greater than 0. It is commonly used for modeling the time until an event occurs
normal - returns a number based on the normal (Gaussian) distribution with a given mean (mu) and standard deviation (sigma). sigma must be > 0. Following transformation is called Box-Muller transform
cauchy - returns a number based on the Cauchy distribution with a given origin point (x0, gamma). gamma must be > 0. It can be interpreted as the probability distribution of the x-intercept of a light ray from the origin.
inUnitCircle - returns a random 2D vector uniformly distributed inside a unit circle
onUnitCircle - returns a random 2D vector uniformly distributed on the perimeter of a unit circle
inUnitCube - returns a random 3D vector uniformly distributed inside a unit cube
onUnitCube - returns a random 3D vector uniformly distributed on the surface of a unit cube
inHemisphere - returns a random 3D vector uniformly distributed inside an upper hemisphere oriented according to a given normal vector
onHemisphere - returns a random 3D vector uniformly distributed on the surface of an upper hemisphere oriented according to a given normal vector
inUnitSphere - returns a random 3D vector uniformly distributed inside a unit sphere. Thanks to the nature of the normal distribution, we can derive this distribution by sampling three coordinates from the and normalizing the result (this gives us a direction vector). Finally, we choose the radius as
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].
@exampleconst 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.
functionstaticCache(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.
Creates a root from the given device, instead of requesting it like
@see ― init. *
@example
const device:GPUDevice = ...;
const root = tgpu.initFromDevice({ device });
initFromDevice({
device: GPUDevice
device });
const
const cache: perlin3d.StaticPerlin3DCache
cache =
import perlin3d
perlin3d.
functionstaticCache(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.
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.
@param ― options - The options for the resolution.
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.
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.