Skip to content
Draft
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
1 change: 1 addition & 0 deletions apps/typegpu-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@tailwindcss/vite": "^4.1.18",
"@typegpu/color": "workspace:*",
"@typegpu/geometry": "workspace:*",
"@typegpu/gl": "workspace:*",
"@typegpu/noise": "workspace:*",
"@typegpu/sdf": "workspace:*",
"@typegpu/sort": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import StackBlitzSDK from '@stackblitz/sdk';
import { parse } from 'yaml';
import { type } from 'arktype';
import typegpuColorPackageJson from '@typegpu/color/package.json' with { type: 'json' };
import typegpuGlPackageJson from '@typegpu/gl/package.json' with { type: 'json' };
import typegpuNoisePackageJson from '@typegpu/noise/package.json' with { type: 'json' };
import typegpuSdfPackageJson from '@typegpu/sdf/package.json' with { type: 'json' };
import typegpuThreePackageJson from '@typegpu/three/package.json' with { type: 'json' };
Expand Down Expand Up @@ -120,6 +121,7 @@ ${example.htmlFile.content}
"three": "${pnpmWorkspaceYaml.catalogs.example.three}",
"@typegpu/noise": "${typegpuNoisePackageJson.version}",
"@typegpu/color": "${typegpuColorPackageJson.version}",
"@typegpu/gl": "${typegpuGlPackageJson.version}",
"@typegpu/sdf": "${typegpuSdfPackageJson.version}",
"@typegpu/three": "${typegpuThreePackageJson.version}"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<canvas></canvas>
169 changes: 169 additions & 0 deletions apps/typegpu-docs/src/examples/rendering/caustics-gl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { perlin3d } from '@typegpu/noise';
import tgpu, { d, std } from 'typegpu';
import { initWithGL } from '@typegpu/gl';
import { defineControls } from '../../common/defineControls.ts';

const mainVertex = tgpu.vertexFn({
in: { vertexIndex: d.builtin.vertexIndex },
out: { pos: d.builtin.position, uv: d.vec2f },
})(({ vertexIndex }) => {
const pos = [d.vec2f(0, 0.8), d.vec2f(-0.8, -0.8), d.vec2f(0.8, -0.8)];
const uv = [d.vec2f(0.5, 1), d.vec2f(0, 0), d.vec2f(1, 0)];

return {
pos: d.vec4f(pos[vertexIndex], 0, 1),
uv: uv[vertexIndex],
};
});

/**
* Given a coordinate, it returns a grayscale floor tile pattern at that
* location.
*/
const tilePattern = (uv: d.v2f): number => {
'use gpu';
const tiledUv = std.fract(uv);
const proximity = std.abs(tiledUv * 2 - 1);
const maxProximity = std.max(proximity.x, proximity.y);
return std.saturate((1 - maxProximity) ** 0.6 * 5);
};

const caustics = (uv: d.v2f, time: number, profile: d.v3f): d.v3f => {
'use gpu';
const distortion = perlin3d.sample(d.vec3f(uv * 0.5, time * 0.2));
// Distorting UV coordinates
const uv2 = uv + distortion;
const noise = std.abs(perlin3d.sample(d.vec3f(uv2 * 5, time)));
return std.pow(d.vec3f(1 - noise), profile);
};

/**
* Returns a transformation matrix that represents an `angle` rotation
* in the XY plane (around the imaginary Z axis)
*/
const rotateXY = (angle: number): d.m2x2f => {
'use gpu';
return d.mat2x2f(
/* right */ d.vec2f(std.cos(angle), std.sin(angle)),
/* up */ d.vec2f(-std.sin(angle), std.cos(angle)),
);
};

const root = initWithGL();

/** Seconds passed since the start of the example, wrapped to the range [0, 1000) */
const time = root.createUniform(d.f32);
/** Controls the angle of rotation for the pool tile texture */
const angle = 0.2;
/** The bigger the number, the denser the pool tile texture is */
const tileDensity = root.createUniform(d.f32);
/** The scene fades into this color at a distance */
const fogColor = d.vec3f(0.05, 0.2, 0.7);
/** The ambient light color */
const ambientColor = d.vec3f(0.2, 0.5, 1);

const mainFragment = tgpu.fragmentFn({
in: { uv: d.vec2f },
out: d.vec4f,
})(({ uv }) => {
'use gpu';
/**
* A transformation matrix that skews the perspective a bit
* when applied to UV coordinates
*/
const skewMat = d.mat2x2f(
d.vec2f(std.cos(angle), std.sin(angle)),
d.vec2f(-std.sin(angle) * 10 + uv.x * 3, std.cos(angle) * 5),
);
const skewedUv = skewMat * uv;
const tile = tilePattern(skewedUv * tileDensity.$);
const albedo = std.mix(d.vec3f(0.1), d.vec3f(1), tile);

// Transforming coordinates to simulate perspective squash
const cuv = d.vec2f(
uv.x * (std.pow(uv.y * 1.5, 3) + 0.1) * 5,
std.pow((uv.y * 1.5 + 0.1) * 1.5, 3) * 1,
);
// Generating two layers of caustics (large scale, and small scale)
const c1 =
caustics(cuv, time.$ * 0.2, /* profile */ d.vec3f(4, 4, 1)) *
// Tinting
d.vec3f(0.4, 0.65, 1);
const c2 =
caustics(cuv * 2, time.$ * 0.4, /* profile */ d.vec3f(16, 1, 4)) *
// Tinting
d.vec3f(0.18, 0.3, 0.5);

// -- BLEND --

const blendCoord = d.vec3f(uv * d.vec2f(5, 10), time.$ * 0.2 + 5);
// A smooth blending factor, so that caustics only appear at certain spots
const blend = std.saturate(perlin3d.sample(blendCoord) + 0.3);

// -- FOG --

const noFogColor = albedo * std.mix(ambientColor, c1 + c2, blend);
// Fog blending factor, based on the height of the pixels
const fog = std.min(uv.y ** 0.5 * 1.2, 1);

// -- GOD RAYS --

const godRayUv = rotateXY(-0.3) * uv * d.vec2f(15, 3);
const godRayFactor = uv.y;
const godRay1 =
(perlin3d.sample(d.vec3f(godRayUv, time.$ * 0.5)) + 1) *
// Tinting
d.vec3f(0.18, 0.3, 0.5) *
godRayFactor;
const godRay2 =
(perlin3d.sample(d.vec3f(godRayUv * 2, time.$ * 0.3)) + 1) *
// Tinting
d.vec3f(0.18, 0.3, 0.5) *
godRayFactor *
0.4;
const godRays = godRay1 + godRay2;

return d.vec4f(std.mix(noFogColor, fogColor, fog) + godRays, 1);
});

const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });

const pipeline = root.createRenderPipeline({
vertex: mainVertex,
fragment: mainFragment,
});

let isRunning = true;

function draw(timestamp: number) {
if (!isRunning) return;

time.write((timestamp * 0.001) % 1000);

pipeline.withColorAttachment({ view: context }).draw(3);

requestAnimationFrame(draw);
}
requestAnimationFrame(draw);

// #region Example controls and cleanup

export const controls = defineControls({
'tile density': {
initial: 10,
min: 5,
max: 20,
step: 1,
onSliderChange: (density) => {
tileDensity.write(density);
},
},
});

export function onCleanup() {
isRunning = false;
root.destroy();
}

// #endregion
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"title": "Caustics (WebGL fallback)",
"category": "rendering",
"tags": ["ecosystem", "lighting", "noise", "webgl"],
"coolFactor": 8
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<canvas></canvas>
58 changes: 58 additions & 0 deletions apps/typegpu-docs/src/examples/simple/triangle-gl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import tgpu, { d, std } from 'typegpu';
import { initWithGL } from '@typegpu/gl';

// Constants and helper functions

const purple = d.vec4f(0.769, 0.392, 1, 1);
const blue = d.vec4f(0.114, 0.447, 0.941, 1);

const getGradientColor = (ratio: number) => {
'use gpu';
return std.mix(purple, blue, ratio);
};

const pos = tgpu.const(d.arrayOf(d.vec2f, 3), [
d.vec2f(0.0, 0.5),
d.vec2f(-0.5, -0.5),
d.vec2f(0.5, -0.5),
]);

const uv = tgpu.const(d.arrayOf(d.vec2f, 3), [
d.vec2f(0.5, 1.0),
d.vec2f(0.0, 0.0),
d.vec2f(1.0, 0.0),
]);

// Render pipeline — forces the WebGL 2 fallback backend.

const root = initWithGL();
const pipeline = root.createRenderPipeline({
vertex: ({ $vertexIndex: vid }) => {
'use gpu';
return {
$position: d.vec4f(pos.$[vid], 0, 1),
uv: uv.$[vid],
};
},
fragment: ({ uv }) => {
'use gpu';
return getGradientColor((uv.x + uv.y) / 2);
},
});

// Setting up the canvas and drawing to it

const context = root.configureContext({
canvas: document.querySelector('canvas') as HTMLCanvasElement,
alphaMode: 'premultiplied',
});

pipeline.withColorAttachment({ view: context }).draw(3);

// #region Cleanup

export function onCleanup() {
root.destroy();
}

// #endregion
6 changes: 6 additions & 0 deletions apps/typegpu-docs/src/examples/simple/triangle-gl/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"title": "Triangle (WebGL fallback)",
"category": "simple",
"tags": ["basics", "primitives", "webgl"],
"coolFactor": 3
}
4 changes: 4 additions & 0 deletions apps/typegpu-docs/src/utils/examples/sandboxModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ export const SANDBOX_MODULES: Record<string, SandboxModuleDefinition> = {
import: { reroute: 'typegpu-noise/src/index.ts' },
typeDef: { reroute: 'typegpu-noise/src/index.ts' },
},
'@typegpu/gl': {
import: { reroute: 'typegpu-gl/src/index.ts' },
typeDef: { reroute: 'typegpu-gl/src/index.ts' },
},
'@typegpu/color': {
import: { reroute: 'typegpu-color/src/index.ts' },
typeDef: { reroute: 'typegpu-color/src/index.ts' },
Expand Down
17 changes: 17 additions & 0 deletions packages/typegpu-gl/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Resource management

I (@iwoplaza) was initially planning to defer buffer creation to the root, but its unclear how to replicate full `TgpuBuffer` behavior anyways. It's way more straightforward to allow just buffer shorthands (`root.createUniform`, etc.) and return simplified implementations. For apps that want to optimize the non-fallback
path, it's very easy to do with accessors for example.

```ts
const root = await initWithGLFallback();
const isGL = isGLRoot(root);

const Positions = d.arrayOf(d.vec3f, 64);
const positions = isGL ? root.createUniform(Positions) : root.createReadonly(Positions);

function updatePosition(index: number) {
'use gpu';
positions.$[index] += d.vec3f(0, 1, 0);
}
```
3 changes: 3 additions & 0 deletions packages/typegpu-gl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,8 @@
},
"peerDependencies": {
"typegpu": "workspace:^"
},
"dependencies": {
"typed-binary": "^4.3.3"
}
}
Loading
Loading