Skip to content

Commit 77561ca

Browse files
committed
cleanup
1 parent 9440c6b commit 77561ca

1 file changed

Lines changed: 94 additions & 81 deletions

File tree

  • apps/typegpu-docs/src/examples/rendering/radiance-compute

apps/typegpu-docs/src/examples/rendering/radiance-compute/index.ts

Lines changed: 94 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
import tgpu from 'typegpu';
1+
import * as sdf from '@typegpu/sdf';
2+
import tgpu, {
3+
type SampledFlag,
4+
type StorageFlag,
5+
type TgpuTexture,
6+
} from 'typegpu';
27
import { fullScreenTriangle } from 'typegpu/common';
38
import * as d from 'typegpu/data';
49
import * as std from 'typegpu/std';
5-
import * as sdf from '@typegpu/sdf';
10+
import { DragController } from './drag-controller.ts';
611
import {
712
SceneData,
813
sceneData,
914
sceneDataAccess,
1015
sceneSDF,
1116
updateElementPosition,
1217
} from './scene.ts';
13-
import { DragController } from './drag-controller.ts';
1418

1519
const root = await tgpu.init();
1620
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
@@ -23,7 +27,7 @@ context.configure({
2327
});
2428

2529
const OUTPUT_RESOLUTION: [number, number] = [canvas.width, canvas.height];
26-
const LIGHTING_RESOLUTION = 0.4;
30+
const LIGHTING_RESOLUTION = 0.35;
2731

2832
const [outputProbesX, outputProbesY] = OUTPUT_RESOLUTION;
2933
const aspect = outputProbesX / outputProbesY;
@@ -37,28 +41,33 @@ const cascadeProbesX = aspect >= 1
3741
const cascadeProbesY = aspect >= 1
3842
? cascadeProbesMin
3943
: Math.round(cascadeProbesMin / aspect);
40-
const cascadeDimX = cascadeProbesX * 2; // 2x2 stored rays per probe
44+
const cascadeDimX = cascadeProbesX * 2;
4145
const cascadeDimY = cascadeProbesY * 2;
4246

4347
const interval0 = 1 / cascadeProbesMin;
44-
const maxIntervalStart = 1.5; // ~diagonal in UV space
48+
const maxIntervalStart = 1.5;
4549
const cascadeAmount = Math.ceil(
46-
Math.log2(maxIntervalStart * 3 / interval0 + 1) / 2,
50+
Math.log2((maxIntervalStart * 3) / interval0 + 1) / 2,
4751
);
4852

49-
const cascadesTextureA = root['~unstable']
50-
.createTexture({
51-
size: [cascadeDimX, cascadeDimY, cascadeAmount],
52-
format: 'rgba16float',
53-
})
54-
.$usage('storage', 'sampled');
55-
56-
const cascadesTextureB = root['~unstable']
57-
.createTexture({
58-
size: [cascadeDimX, cascadeDimY, cascadeAmount],
59-
format: 'rgba16float',
60-
})
61-
.$usage('storage', 'sampled');
53+
type CascadeTexture =
54+
& TgpuTexture<{
55+
size: [number, number, number];
56+
format: 'rgba16float';
57+
}>
58+
& StorageFlag
59+
& SampledFlag;
60+
61+
const cascadeTextures = Array.from(
62+
{ length: 2 },
63+
() =>
64+
root['~unstable']
65+
.createTexture({
66+
size: [cascadeDimX, cascadeDimY, cascadeAmount],
67+
format: 'rgba16float',
68+
})
69+
.$usage('storage', 'sampled'),
70+
) as [CascadeTexture, CascadeTexture];
6271

6372
const radianceFieldTex = root['~unstable']
6473
.createTexture({
@@ -128,7 +137,9 @@ const cascadePassCompute = tgpu['~unstable'].computeFn({
128137
in: { gid: d.builtin.globalInvocationId },
129138
})(({ gid }) => {
130139
const dim2 = cascadeDimUniform.$;
131-
if (gid.x >= dim2.x || gid.y >= dim2.y) return;
140+
if (gid.x >= dim2.x || gid.y >= dim2.y) {
141+
return;
142+
}
132143

133144
const layer = cascadeIndexUniform.$;
134145
const probes = probesUniform.$;
@@ -156,7 +167,7 @@ const cascadePassCompute = tgpu['~unstable'].computeFn({
156167
const cascadeProbesMinVal = d.f32(std.min(cascadeProbes.x, cascadeProbes.y));
157168
const interval0 = 1.0 / cascadeProbesMinVal;
158169
const pow4 = d.f32(d.u32(1) << (layer * d.u32(2)));
159-
const startUv = interval0 * (pow4 - 1.0) / 3.0;
170+
const startUv = (interval0 * (pow4 - 1.0)) / 3.0;
160171
const endUv = startUv + interval0 * pow4;
161172
const eps = 0.5 / cascadeProbesMinVal;
162173
const minStep = 0.25 / cascadeProbesMinVal;
@@ -165,9 +176,9 @@ const cascadePassCompute = tgpu['~unstable'].computeFn({
165176

166177
// Cast 4 rays per stored texel (2x2 block) and average
167178
for (let i = 0; i < 4; i++) {
168-
const dirActual = dirStored.mul(d.u32(2)).add(
169-
d.vec2u(d.u32(i) & d.u32(1), d.u32(i) >> d.u32(1)),
170-
);
179+
const dirActual = dirStored
180+
.mul(d.u32(2))
181+
.add(d.vec2u(d.u32(i) & d.u32(1), d.u32(i) >> d.u32(1)));
171182
const rayIndex = d.f32(dirActual.y * raysDimActual + dirActual.x) + 0.5;
172183
const angle = (rayIndex / d.f32(rayCountActual)) * (Math.PI * 2) - Math.PI;
173184
const rayDir = d.vec2f(std.cos(angle), -std.sin(angle));
@@ -284,7 +295,10 @@ const buildRadianceFieldCompute = tgpu['~unstable'].computeFn({
284295
);
285296
});
286297

287-
const ACESFilm = tgpu.fn([d.vec3f], d.vec3f)((x) => {
298+
const ACESFilm = tgpu.fn(
299+
[d.vec3f],
300+
d.vec3f,
301+
)((x) => {
288302
const a = 2.51;
289303
const b = 0.03;
290304
const c = 2.43;
@@ -298,8 +312,11 @@ const finalRadianceFieldFrag = tgpu['~unstable'].fragmentFn({
298312
in: { uv: d.vec2f },
299313
out: d.vec4f,
300314
})(({ uv }) => {
301-
const field = std.textureSample(radianceFieldView.$, radianceSampler.$, uv)
302-
.xyz;
315+
const field = std.textureSample(
316+
radianceFieldView.$,
317+
radianceSampler.$,
318+
uv,
319+
).xyz;
303320
const outRgb = std.saturate(field);
304321
return d.vec4f(ACESFilm(outRgb), 1.0);
305322
});
@@ -318,7 +335,7 @@ const overlayFrag = tgpu['~unstable'].fragmentFn({
318335
const baseColor = ACESFilm(std.saturate(field));
319336

320337
if (overlayEnabledUniform.$ === d.u32(0)) {
321-
return d.vec4f(baseColor, 1.0);
338+
return d.vec4f(baseColor, 1);
322339
}
323340

324341
const debugLayer = overlayDebugCascadeUniform.$;
@@ -335,17 +352,17 @@ const overlayFrag = tgpu['~unstable'].fragmentFn({
335352

336353
// Interval for ray visualization
337354
const cascadeProbesMinVal = d.f32(std.min(cascadeProbes.x, cascadeProbes.y));
338-
const interval0 = 1.0 / cascadeProbesMinVal;
355+
const interval0 = 1 / cascadeProbesMinVal;
339356
const pow4 = d.f32(d.u32(1) << (debugLayer * d.u32(2)));
340-
const endUv = interval0 * (pow4 - 1.0) / 3.0 + interval0 * pow4;
357+
const endUv = (interval0 * (pow4 - 1)) / 3 + interval0 * pow4;
341358

342359
// Visual parameters
343-
const probeSpacing = std.min(1.0 / d.f32(probes.x), 1.0 / d.f32(probes.y));
360+
const probeSpacing = std.min(1 / d.f32(probes.x), 1 / d.f32(probes.y));
344361
const probeRadius = std.max(probeSpacing * 0.08, 0.002);
345362
const rayThickness = std.max(probeSpacing * 0.03, 0.001);
346363

347-
let minProbeDist = d.f32(1000.0);
348-
let minRayDist = d.f32(1000.0);
364+
let minProbeDist = d.f32(1000);
365+
let minRayDist = d.f32(1000);
349366
let closestRayColor = d.vec3f();
350367

351368
const centerProbe = d.vec2i(std.floor(uv.mul(d.vec2f(probes))));
@@ -355,9 +372,13 @@ const overlayFrag = tgpu['~unstable'].fragmentFn({
355372
for (let px = -1; px <= 1; px++) {
356373
const probeXY = centerProbe.add(d.vec2i(px, py));
357374
if (
358-
probeXY.x < 0 || probeXY.x >= d.i32(probes.x) || probeXY.y < 0 ||
375+
probeXY.x < 0 ||
376+
probeXY.x >= d.i32(probes.x) ||
377+
probeXY.y < 0 ||
359378
probeXY.y >= d.i32(probes.y)
360-
) continue;
379+
) {
380+
continue;
381+
}
361382

362383
const probe = d.vec2u(probeXY);
363384
const probePos = d.vec2f(probe).add(0.5).div(d.vec2f(probes));
@@ -366,15 +387,15 @@ const overlayFrag = tgpu['~unstable'].fragmentFn({
366387
sdf.sdDisk(uv.sub(probePos), probeRadius),
367388
);
368389

369-
// Only draw rays near probe
370-
if (std.length(uv.sub(probePos)) > probeSpacing * 0.7) continue;
390+
if (std.length(uv.sub(probePos)) > probeSpacing * 0.7) {
391+
continue;
392+
}
371393

372-
// Sample subset of rays
373-
const rayStep = std.max(d.u32(1), rayCountActual / d.u32(24));
394+
const rayStep = std.max(1, d.u32(rayCountActual / 24));
374395
let ri = d.u32(0);
375396
while (ri < rayCountActual) {
376397
const rayIndex = d.f32(ri) + 0.5;
377-
const angle = (rayIndex / d.f32(rayCountActual)) * (Math.PI * 2) -
398+
const angle = (rayIndex / rayCountActual) * (Math.PI * 2) -
378399
Math.PI;
379400
const rayDir = d.vec2f(std.cos(angle), -std.sin(angle));
380401
const rayDist = sdf.sdLine(
@@ -434,8 +455,8 @@ const cascadePassBindGroups = Array.from(
434455
{ length: cascadeAmount },
435456
(_, layer) => {
436457
const writeToA = (cascadeAmount - 1 - layer) % 2 === 0;
437-
const dstTexture = writeToA ? cascadesTextureA : cascadesTextureB;
438-
const srcTexture = writeToA ? cascadesTextureB : cascadesTextureA;
458+
const dstTexture = cascadeTextures[writeToA ? 0 : 1];
459+
const srcTexture = cascadeTextures[writeToA ? 1 : 0];
439460

440461
return root.createBindGroup(cascadePassBGL, {
441462
upper: srcTexture.createView(d.texture2d(d.f32), {
@@ -455,30 +476,25 @@ const buildRadianceFieldPipeline = root['~unstable']
455476
.withCompute(buildRadianceFieldCompute)
456477
.createPipeline();
457478

458-
const buildRadianceFieldBG_A = root.createBindGroup(buildRadianceFieldBGL, {
459-
src: cascadesTextureA.createView(d.texture2d(d.f32), {
460-
baseArrayLayer: 0,
461-
arrayLayerCount: 1,
462-
}),
463-
srcSampler: cascadeSampler,
464-
dst: radianceFieldStoreView,
465-
});
466-
467-
const buildRadianceFieldBG_B = root.createBindGroup(buildRadianceFieldBGL, {
468-
src: cascadesTextureB.createView(d.texture2d(d.f32), {
469-
baseArrayLayer: 0,
470-
arrayLayerCount: 1,
471-
}),
472-
srcSampler: cascadeSampler,
473-
dst: radianceFieldStoreView,
474-
});
479+
const createBuildRadianceFieldBG = (textureIndex: number) =>
480+
root.createBindGroup(buildRadianceFieldBGL, {
481+
src: cascadeTextures[textureIndex].createView(d.texture2d(d.f32), {
482+
baseArrayLayer: 0,
483+
arrayLayerCount: 1,
484+
}),
485+
srcSampler: cascadeSampler,
486+
dst: radianceFieldStoreView,
487+
});
488+
489+
const buildRadianceFieldBindGroups = [
490+
createBuildRadianceFieldBG(0),
491+
createBuildRadianceFieldBG(1),
492+
];
475493

476494
function buildRadianceField() {
477-
// Determine which texture has cascade 0 after ping-pong
478495
const cascade0InA = (cascadeAmount - 1) % 2 === 0;
479-
const buildRadianceFieldBG = cascade0InA
480-
? buildRadianceFieldBG_A
481-
: buildRadianceFieldBG_B;
496+
const buildRadianceFieldBG =
497+
buildRadianceFieldBindGroups[cascade0InA ? 0 : 1];
482498

483499
buildRadianceFieldPipeline
484500
.with(buildRadianceFieldBG)
@@ -506,36 +522,35 @@ function runCascadesTopDown() {
506522
}
507523

508524
function updateLighting() {
509-
runCascadesTopDown(); // Fused raymarch + merge
510-
buildRadianceField(); // Build final radiance field from cascade 0
525+
runCascadesTopDown();
526+
buildRadianceField();
511527
}
512528
updateLighting();
513529

514-
// Create bind groups for overlay debug - need both A and B textures for ping-pong
515-
const overlayDebugBG_A = root.createBindGroup(overlayDebugBGL, {
516-
cascadeTex: cascadesTextureA.createView(d.texture2dArray(d.f32)),
517-
cascadeSampler: cascadeSampler,
518-
});
530+
const createOverlayDebugBG = (textureIndex: number) =>
531+
root.createBindGroup(overlayDebugBGL, {
532+
cascadeTex: cascadeTextures[textureIndex].createView(
533+
d.texture2dArray(d.f32),
534+
),
535+
cascadeSampler: cascadeSampler,
536+
});
519537

520-
const overlayDebugBG_B = root.createBindGroup(overlayDebugBGL, {
521-
cascadeTex: cascadesTextureB.createView(d.texture2dArray(d.f32)),
522-
cascadeSampler: cascadeSampler,
523-
});
538+
const overlayDebugBindGroups = [
539+
createOverlayDebugBG(0),
540+
createOverlayDebugBG(1),
541+
];
524542

525543
const renderPipeline = root['~unstable']
526544
.withVertex(fullScreenTriangle)
527545
.withFragment(overlayFrag, { format: presentationFormat })
528546
.createPipeline();
529547

530-
let isRunning = true;
531548
let frameId: number;
532549
let debugLayer = 0;
533550

534551
async function frame() {
535-
if (!isRunning) return; // Prevent using destroyed device
536-
537552
const writeToA = (cascadeAmount - 1 - debugLayer) % 2 === 0;
538-
const overlayDebugBG = writeToA ? overlayDebugBG_A : overlayDebugBG_B;
553+
const overlayDebugBG = overlayDebugBindGroups[writeToA ? 0 : 1];
539554

540555
renderPipeline
541556
.with(overlayDebugBG)
@@ -553,7 +568,6 @@ function updateUniforms() {
553568
sceneDataUniform.write(sceneData);
554569
}
555570

556-
// Set up drag controller for interactive scene manipulation
557571
const dragController = new DragController(
558572
canvas,
559573
(id, position) => {
@@ -569,7 +583,6 @@ const dragController = new DragController(
569583
);
570584

571585
export function onCleanup() {
572-
isRunning = false; // Stop the loop logic immediately
573586
dragController.destroy();
574587
if (frameId !== null) {
575588
cancelAnimationFrame(frameId);

0 commit comments

Comments
 (0)