Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions packages/typegpu-noise/src/generator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import tgpu, { d, type TgpuFnShell, type TgpuSlot } from 'typegpu';
import { add, cos, dot, fract } from 'typegpu/std';
import { cos, dot, fract } from 'typegpu/std';

export interface StatefulGenerator {
seed?: (seed: number) => void;
Expand Down Expand Up @@ -28,11 +28,13 @@ export const BPETER: StatefulGenerator = (() => {
}),

seed3: tgpu.fn([d.vec3f])((value) => {
seed.$ = add(value.xy, d.vec2f(value.z));
'use gpu';
seed.$ = value.xy + d.vec2f(value.z);
}),

seed4: tgpu.fn([d.vec4f])((value) => {
seed.$ = add(value.xy, value.zw);
'use gpu';
seed.$ = value.xy + value.zw;
}),

sample: randomGeneratorShell(() => {
Expand Down
57 changes: 26 additions & 31 deletions packages/typegpu-noise/src/perlin-2d/algorithm.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import tgpu, { d } from 'typegpu';
import { add, dot, floor, fract, mul, sub } from 'typegpu/std';
import { dot, floor, fract } from 'typegpu/std';
import { randOnUnitCircle, randSeed2 } from '../random.ts';
import { quinticDerivative2, quinticInterpolation2 } from '../utils.ts';
import { quinticDerivative, quinticInterpolation } from '../utils.ts';

export const computeJunctionGradient = tgpu.fn([d.vec2i], d.vec2f)((pos) => {
randSeed2(mul(0.001, d.vec2f(pos)));
'use gpu';
randSeed2(0.001 * d.vec2f(pos));
return randOnUnitCircle();
});

Expand All @@ -14,22 +15,23 @@ export const getJunctionGradientSlot = tgpu.slot(computeJunctionGradient);
* Returns value of Perlin Noise at point `pos`
*/
export const sample = tgpu.fn([d.vec2f], d.f32)((pos) => {
'use gpu';
// Reference: https://iquilezles.org/articles/gradientnoise/

const i = d.vec2i(floor(pos));
const f = fract(pos);

const u = quinticInterpolation2(f);
const u = quinticInterpolation(f);

const ga = getJunctionGradientSlot.$(i);
const gb = getJunctionGradientSlot.$(add(i, d.vec2i(1, 0)));
const gc = getJunctionGradientSlot.$(add(i, d.vec2i(0, 1)));
const gd = getJunctionGradientSlot.$(add(i, d.vec2i(1, 1)));
const gb = getJunctionGradientSlot.$(i + d.vec2i(1, 0));
const gc = getJunctionGradientSlot.$(i + d.vec2i(0, 1));
const gd = getJunctionGradientSlot.$(i + d.vec2i(1, 1));

const va = dot(ga, sub(f, d.vec2f(0, 0)));
const vb = dot(gb, sub(f, d.vec2f(1, 0)));
const vc = dot(gc, sub(f, d.vec2f(0, 1)));
const vd = dot(gd, sub(f, d.vec2f(1, 1)));
const va = dot(ga, f - d.vec2f(0, 0));
const vb = dot(gb, f - d.vec2f(1, 0));
const vc = dot(gc, f - d.vec2f(0, 1));
const vd = dot(gd, f - d.vec2f(1, 1));

const noise = va + u.x * (vb - va) + u.y * (vc - va) +
u.x * u.y * (va - vb - vc + vd);
Expand All @@ -42,38 +44,31 @@ export const sample = tgpu.fn([d.vec2f], d.f32)((pos) => {
* the gradient of the function at that point as yz coordinates.
*/
export const sampleWithGradient = tgpu.fn([d.vec2f], d.vec3f)((pos) => {
'use gpu';
// Reference: https://iquilezles.org/articles/gradientnoise/

const i = d.vec2i(floor(pos));
const f = fract(pos);

const u = quinticInterpolation2(f);
const du = quinticDerivative2(f);
const u = quinticInterpolation(f);
const du = quinticDerivative(f);

const ga = getJunctionGradientSlot.$(i);
const gb = getJunctionGradientSlot.$(add(i, d.vec2i(1, 0)));
const gc = getJunctionGradientSlot.$(add(i, d.vec2i(0, 1)));
const gd = getJunctionGradientSlot.$(add(i, d.vec2i(1, 1)));
const gb = getJunctionGradientSlot.$(i + d.vec2i(1, 0));
const gc = getJunctionGradientSlot.$(i + d.vec2i(0, 1));
const gd = getJunctionGradientSlot.$(i + d.vec2i(1, 1));

const va = dot(ga, sub(f, d.vec2f(0, 0)));
const vb = dot(gb, sub(f, d.vec2f(1, 0)));
const vc = dot(gc, sub(f, d.vec2f(0, 1)));
const vd = dot(gd, sub(f, d.vec2f(1, 1)));
const va = dot(ga, f - d.vec2f(0, 0));
const vb = dot(gb, f - d.vec2f(1, 0));
const vc = dot(gc, f - d.vec2f(0, 1));
const vd = dot(gd, f - d.vec2f(1, 1));

const noise = va + u.x * (vb - va) + u.y * (vc - va) +
u.x * u.y * (va - vb - vc + vd);

// ga + u.x*(gb-ga) + u.y*(gc-ga) + u.x*u.y*(ga-gb-gc+gd) + du * (u.yx*(va-vb-vc+vd) + vec2(vb,vc) - va))
const grad = add(
ga,
add(
add(
add(mul(u.x, sub(gb, ga)), mul(u.y, sub(gc, ga))),
mul(u.x, mul(u.y, add(sub(sub(ga, gb), gc), gd))),
),
mul(du, sub(add(mul(u.yx, va - vb - vc + vd), d.vec2f(vb, vc)), va)),
),
);
const grad = ga + u.x * (gb - ga) + u.y * (gc - ga) +
u.x * u.y * (ga - gb - gc + gd) +
du * (u.yx * (va - vb - vc + vd) + d.vec2f(vb, vc) - va);

return d.vec3f(noise, grad);
});
37 changes: 20 additions & 17 deletions packages/typegpu-noise/src/perlin-3d/algorithm.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
import tgpu from 'typegpu';
import * as d from 'typegpu/data';
import { add, dot, floor, mix, mul, sub } from 'typegpu/std';
import { dot, floor, mix } from 'typegpu/std';
import { randOnUnitSphere, randSeed3 } from '../random.ts';
import { quinticInterpolation3 } from '../utils.ts';
import { quinticInterpolation } from '../utils.ts';

export const computeJunctionGradient = tgpu.fn([d.vec3i], d.vec3f)((pos) => {
randSeed3(mul(0.001, d.vec3f(pos)));
'use gpu';
randSeed3(0.001 * d.vec3f(pos));
return randOnUnitSphere();
});

export const getJunctionGradientSlot = tgpu.slot(computeJunctionGradient);

const dotProdGrid = tgpu.fn([d.vec3f, d.vec3f], d.f32)((pos, junction) => {
const relative = sub(pos, junction);
const gridVector = getJunctionGradientSlot.value(d.vec3i(junction));
const dotProdGrid = (pos: d.v3f, junction: d.v3f): number => {
'use gpu';
const relative = pos - junction;
const gridVector = getJunctionGradientSlot.$(d.vec3i(junction));
return dot(relative, gridVector);
});
};

export const sample = tgpu.fn([d.vec3f], d.f32)((pos) => {
'use gpu';
const minJunction = floor(pos);

const xyz = dotProdGrid(pos, minJunction);
const xyZ = dotProdGrid(pos, add(minJunction, d.vec3f(0, 0, 1)));
const xYz = dotProdGrid(pos, add(minJunction, d.vec3f(0, 1, 0)));
const xYZ = dotProdGrid(pos, add(minJunction, d.vec3f(0, 1, 1)));
const Xyz = dotProdGrid(pos, add(minJunction, d.vec3f(1, 0, 0)));
const XyZ = dotProdGrid(pos, add(minJunction, d.vec3f(1, 0, 1)));
const XYz = dotProdGrid(pos, add(minJunction, d.vec3f(1, 1, 0)));
const XYZ = dotProdGrid(pos, add(minJunction, d.vec3f(1, 1, 1)));

const partial = sub(pos, minJunction);
const smoothPartial = quinticInterpolation3(partial);
const xyZ = dotProdGrid(pos, minJunction + d.vec3f(0, 0, 1));
const xYz = dotProdGrid(pos, minJunction + d.vec3f(0, 1, 0));
const xYZ = dotProdGrid(pos, minJunction + d.vec3f(0, 1, 1));
const Xyz = dotProdGrid(pos, minJunction + d.vec3f(1, 0, 0));
const XyZ = dotProdGrid(pos, minJunction + d.vec3f(1, 0, 1));
const XYz = dotProdGrid(pos, minJunction + d.vec3f(1, 1, 0));
const XYZ = dotProdGrid(pos, minJunction + d.vec3f(1, 1, 1));

const partial = pos - minJunction;
const smoothPartial = quinticInterpolation(partial);

// Resolving the z-axis into a xy-slice
const xy = mix(xyz, xyZ, smoothPartial.z);
Expand Down
11 changes: 6 additions & 5 deletions packages/typegpu-noise/src/random.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import {
cos,
dot,
log,
mul,
normalize,
pow,
select,
sign,
sin,
Expand Down Expand Up @@ -93,12 +91,13 @@ export const randOnUnitCircle: TgpuFn<() => d.Vec2f> = tgpu

export const randInUnitSphere: TgpuFn<() => d.Vec3f> = tgpu
.fn([], d.vec3f)(() => {
'use gpu';
const u = randomGeneratorSlot.$.sample();
const v = d.vec3f(randNormal(0, 1), randNormal(0, 1), randNormal(0, 1));

const vNorm = normalize(v);

return vNorm.mul(pow(u, 0.33));
return vNorm * (u ** 0.33);
});

export const randOnUnitSphere: TgpuFn<() => d.Vec3f> = tgpu
Expand All @@ -114,18 +113,20 @@ export const randOnUnitSphere: TgpuFn<() => d.Vec3f> = tgpu

export const randInUnitHemisphere: TgpuFn<(normal: d.Vec3f) => d.Vec3f> = tgpu
.fn([d.vec3f], d.vec3f)((normal) => {
'use gpu';
const value = randInUnitSphere();
const alignment = dot(normal, value);

return mul(sign(alignment), value);
return sign(alignment) * value;
});

export const randOnUnitHemisphere: TgpuFn<(normal: d.Vec3f) => d.Vec3f> = tgpu
.fn([d.vec3f], d.vec3f)((normal) => {
'use gpu';
const value = randOnUnitSphere();
const alignment = dot(normal, value);

return mul(sign(alignment), value);
return sign(alignment) * value;
});

export const randUniformExclusive: TgpuFn<() => d.F32> = tgpu
Expand Down
46 changes: 13 additions & 33 deletions packages/typegpu-noise/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import tgpu, { d } from 'typegpu';
import { add, mul, sub } from 'typegpu/std';
import type { d } from 'typegpu';

export type Prettify<T> =
& {
Expand All @@ -11,42 +10,23 @@ export type PrefixKeys<Prefix extends string, T> = {
[K in keyof T as K extends string ? `${Prefix}${K}` : K]: T[K];
};

// t * t * t * (t * (6t - (15, 15)) + (10, 10));
const quinticInterpolationImpl = <T extends d.v2f | d.v3f>(t: T): T => {
'use gpu';
return mul(mul(t, mul(t, t)), add(mul(t, sub(mul(t, 6), 15)), 10));
// TODO: Write it using fluent APIs when it becomes available:
// return t.mul(t).mul(t).mul((t.mul(6).sub(15)).add(10));
};

/**
* Works as a replacement for smoothstep, but with a continuous
* second derivative, which in e.g. smooth normals
*/
export const quinticInterpolation2 = tgpu
.fn([d.vec2f], d.vec2f)(quinticInterpolationImpl);

/**
* Works as a replacement for smoothstep, but with a continuous
* second derivative, which in e.g. smooth normals
*/
export const quinticInterpolation3 = tgpu
.fn([d.vec3f], d.vec3f)(quinticInterpolationImpl);

// 30 * t * t * (t * (t - (2, 2)) + (1, 1))
const quinticDerivativeImpl = <T extends d.v2f | d.v3f>(t: T): T => {
export function quinticInterpolation(t: d.v2f): d.v2f;
export function quinticInterpolation(t: d.v3f): d.v3f;
export function quinticInterpolation(t: d.vecBase): d.vecBase {
'use gpu';
return mul(mul(mul(30, t), t), add(mul(t, sub(t, 2)), 1));
};
return t * t * t * (t * (t * 6 - 15) + 10);
}

/**
* Derivative of {@link quinticInterpolation2}
* Derivative of {@link quinticInterpolation}
*/
export const quinticDerivative2 = tgpu
.fn([d.vec2f], d.vec2f)(quinticDerivativeImpl);

/**
* Derivative of {@link quinticInterpolation3}
*/
export const quinticDerivative3 = tgpu
.fn([d.vec3f], d.vec3f)(quinticDerivativeImpl);
export function quinticDerivative(t: d.v2f): d.v2f;
export function quinticDerivative(t: d.v3f): d.v3f;
export function quinticDerivative(t: d.vecBase): d.vecBase {
'use gpu';
return 30 * t * t * ((t * (t - 2)) + 1);
}
Loading
Loading