Skip to content

Commit e97d267

Browse files
committed
docs: "Smoky triangle" example
1 parent 6816067 commit e97d267

File tree

10 files changed

+411
-53
lines changed

10 files changed

+411
-53
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<canvas></canvas>
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import { perlin3d } from '@typegpu/noise';
2+
import tgpu from 'typegpu';
3+
import * as d from 'typegpu/data';
4+
import * as std from 'typegpu/std';
5+
6+
const root = await tgpu.init();
7+
8+
const fromColor = root.createUniform(d.vec3f);
9+
const polarCoords = root.createUniform(d.u32);
10+
const squashed = root.createUniform(d.u32);
11+
const toColor = root.createUniform(d.vec3f);
12+
const sharpness = root.createUniform(d.f32);
13+
const distortion = root.createUniform(d.f32);
14+
const time = root.createUniform(d.f32);
15+
const grainSeed = root.createUniform(d.f32);
16+
17+
const getGradientColor = (ratio: number) => {
18+
'use gpu';
19+
if (squashed.$ === 1) {
20+
return std.mix(fromColor.$, toColor.$, std.smoothstep(0.1, 0.9, ratio));
21+
}
22+
return std.mix(fromColor.$, toColor.$, ratio);
23+
};
24+
25+
const tanhVec = (v: d.v2f): d.v2f => {
26+
'use gpu';
27+
const len = std.length(v);
28+
const tanh = std.tanh(len);
29+
return v.div(len).mul(tanh);
30+
};
31+
32+
const grain = (color: d.v3f, uv: d.v2f) => {
33+
'use gpu';
34+
return color.add(perlin3d.sample(d.vec3f(uv.mul(200), grainSeed.$)) * 0.1);
35+
};
36+
37+
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
38+
const pipeline = root['~unstable'].createRenderPipeline({
39+
vertex: ({ $vertexIndex }) => {
40+
'use gpu';
41+
const pos = [d.vec2f(0, 0.8), d.vec2f(-0.8, -0.8), d.vec2f(0.8, -0.8)];
42+
const uv = [d.vec2f(0.5, 1), d.vec2f(0, 0), d.vec2f(1, 0)];
43+
44+
return {
45+
$position: d.vec4f(pos[$vertexIndex], 0, 1),
46+
uv: uv[$vertexIndex],
47+
};
48+
},
49+
fragment: ({ uv }) => {
50+
'use gpu';
51+
const t = time.$ * 0.1;
52+
const ouv = uv.mul(5).add(d.vec2f(0, -t));
53+
let off = d
54+
.vec2f(
55+
perlin3d.sample(d.vec3f(ouv, t)),
56+
perlin3d.sample(d.vec3f(ouv.mul(2), t + 10)) * 0.5,
57+
).add(-0.1);
58+
// Sharpening the offset
59+
off = tanhVec(off.mul(sharpness.$));
60+
// Offsetting the sample point by the distortion
61+
const p = uv.add(off.mul(distortion.$));
62+
63+
// const factor = (p.x - p.y + 0.7) * 0.7; // How far along the diagonal we are
64+
let factor = d.f32(0);
65+
if (polarCoords.$ === 1) {
66+
factor = std.length(p.sub(d.vec2f(0.5, 0.3)).mul(2));
67+
} else {
68+
factor = (p.x + p.y) * 0.7; // How far along the diagonal we are
69+
}
70+
return std.saturate(d.vec4f(grain(getGradientColor(factor), uv), 1));
71+
},
72+
targets: { format: presentationFormat },
73+
});
74+
75+
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
76+
const context = canvas.getContext('webgpu') as GPUCanvasContext;
77+
78+
context.configure({
79+
device: root.device,
80+
format: presentationFormat,
81+
alphaMode: 'premultiplied',
82+
});
83+
84+
let frameId: number;
85+
function frame(timestamp: number) {
86+
time.write(timestamp / 1000);
87+
grainSeed.write(Math.floor(Math.random() * 100));
88+
pipeline
89+
.withColorAttachment({
90+
view: context.getCurrentTexture().createView(),
91+
loadOp: 'clear',
92+
storeOp: 'store',
93+
})
94+
.draw(3);
95+
96+
frameId = requestAnimationFrame(frame);
97+
}
98+
frameId = requestAnimationFrame(frame);
99+
100+
export const controls = {
101+
'Distortion': {
102+
initial: 0.05,
103+
min: 0,
104+
max: 0.2,
105+
step: 0.001,
106+
onSliderChange(v: number) {
107+
distortion.write(v);
108+
},
109+
},
110+
'Sharpness': {
111+
initial: 4.5,
112+
min: 0,
113+
max: 7,
114+
step: 0.1,
115+
onSliderChange(v: number) {
116+
sharpness.write(v ** 2);
117+
},
118+
},
119+
'From Color': {
120+
initial: [0.057, 0.2235, 0.4705],
121+
onColorChange(value: readonly [number, number, number]) {
122+
fromColor.write(d.vec3f(...value));
123+
},
124+
},
125+
'To Color': {
126+
initial: [1.538, 0.784, 2],
127+
onColorChange(value: readonly [number, number, number]) {
128+
toColor.write(d.vec3f(...value));
129+
},
130+
},
131+
'Polar Coordinates': {
132+
initial: false,
133+
onToggleChange(value: boolean) {
134+
polarCoords.write(value ? 1 : 0);
135+
},
136+
},
137+
'Squashed': {
138+
initial: true,
139+
onToggleChange(value: boolean) {
140+
squashed.write(value ? 1 : 0);
141+
},
142+
},
143+
'Clouds Preset': {
144+
onButtonClick() {
145+
distortion.write(0.05);
146+
sharpness.write(4.5 ** 2);
147+
fromColor.write(d.vec3f(0.057, 0.2235, 0.4705));
148+
toColor.write(d.vec3f(1.538, 0.784, 2));
149+
polarCoords.write(0);
150+
squashed.write(1);
151+
},
152+
},
153+
'Fire Preset': {
154+
onButtonClick() {
155+
distortion.write(0.1);
156+
sharpness.write(7 ** 2);
157+
fromColor.write(d.vec3f(2, 0.4, 0.5));
158+
toColor.write(d.vec3f(0, 0, 0.4));
159+
polarCoords.write(1);
160+
squashed.write(0);
161+
},
162+
},
163+
};
164+
165+
export function onCleanup() {
166+
cancelAnimationFrame(frameId);
167+
root.destroy();
168+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"title": "Smoky Triangle",
3+
"category": "simple",
4+
"tags": ["experimental"]
5+
}
1.05 MB
Loading

apps/typegpu-docs/src/examples/simple/triangle-next/index.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import tgpu from 'typegpu';
22
import * as d from 'typegpu/data';
33
import * as std from 'typegpu/std';
44

5-
const purple = d.vec4f(0.769, 0.392, 1.0, 1);
5+
const purple = d.vec4f(0.769, 0.392, 1, 1);
66
const blue = d.vec4f(0.114, 0.447, 0.941, 1);
77

88
const getGradientColor = (ratio: number) => {
@@ -16,17 +16,8 @@ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
1616
const pipeline = root['~unstable'].createRenderPipeline({
1717
vertex: ({ $vertexIndex }) => {
1818
'use gpu';
19-
const pos = [
20-
d.vec2f(0.0, 0.5),
21-
d.vec2f(-0.5, -0.5),
22-
d.vec2f(0.5, -0.5),
23-
];
24-
25-
const uv = [
26-
d.vec2f(0.5, 1.0),
27-
d.vec2f(0.0, 0.0),
28-
d.vec2f(1.0, 0.0),
29-
];
19+
const pos = [d.vec2f(0, 0.5), d.vec2f(-0.5, -0.5), d.vec2f(0.5, -0.5)];
20+
const uv = [d.vec2f(0.5, 1), d.vec2f(0, 0), d.vec2f(1, 0)];
3021

3122
return {
3223
$position: d.vec4f(pos[$vertexIndex], 0, 1),

packages/typegpu/src/core/function/autoIO.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
import { $internal, $resolve } from '../../shared/symbols.ts';
1313
import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
1414
import { createFnCore, type FnCore } from './fnCore.ts';
15-
import type { BaseIOData } from './fnTypes.ts';
15+
import type { AnyFn, BaseIOData } from './fnTypes.ts';
1616

1717
export type AnyAutoCustoms = Record<
1818
string,
@@ -72,7 +72,7 @@ export class AutoFragmentFn implements SelfResolvable {
7272
declare [$internal]: true;
7373
declare resourceType: 'auto-fragment-fn';
7474

75-
impl: AutoFragmentFnImpl;
75+
impl: AnyFn;
7676
#core: FnCore;
7777
#autoIn: AutoStruct;
7878
#autoOut: AutoStruct;
@@ -118,7 +118,7 @@ export class AutoVertexFn implements SelfResolvable {
118118
declare [$internal]: true;
119119
declare resourceType: 'auto-vertex-fn';
120120

121-
impl: AutoVertexFnImpl;
121+
impl: AnyFn;
122122
#core: FnCore;
123123
#autoIn: AutoStruct;
124124
#autoOut: AutoStruct;

packages/typegpu/src/core/pipeline/renderPipeline.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,7 @@ export type FragmentOutToTargets<T> =
216216
// (shelled) builtin return
217217
| AnyBuiltin
218218
// (shelled) no return
219-
// TODO: Try d.Void
220-
| { readonly [$internal]: unknown; type: 'void' }
219+
| Void
221220
// (shelled) empty object
222221
| Record<string, never>
223222
? Record<string, never>
@@ -235,11 +234,8 @@ export type FragmentOutToTargets<T> =
235234
export type FragmentOutToColorAttachment<T> = T extends {
236235
readonly [$internal]: unknown;
237236
} ? ColorAttachment
238-
: T extends Record<string, unknown> ? {
239-
// Stripping all decorated properties
240-
[Key in keyof T as T[Key] extends Decorated ? never : Key]:
241-
ColorAttachment;
242-
}
237+
: T extends Record<string, unknown>
238+
? { [Key in keyof OmitBuiltins<T>]: ColorAttachment }
243239
: never;
244240

245241
export type AnyFragmentTargets =

0 commit comments

Comments
 (0)