Skip to content
Draft
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
eac8d16
init
reczkok Dec 16, 2025
f75dd66
first pass of fixes and simplifications
reczkok Dec 16, 2025
226589e
clean up and simlify
reczkok Dec 16, 2025
05d254a
move to morton order and use hardware sampling
reczkok Jan 7, 2026
69df43f
move to direction first
reczkok Jan 8, 2026
59ee9dc
work
reczkok Jan 8, 2026
9440c6b
more work
reczkok Jan 10, 2026
77561ca
cleanup
reczkok Jan 13, 2026
a677387
more work
reczkok Jan 14, 2026
98193b5
Merge branch 'main' into docs/compute-gi-rc
reczkok Jan 26, 2026
351d7f3
remove post merge conflict
reczkok Jan 26, 2026
96fec65
work work
reczkok Jan 28, 2026
eced493
Merge branch 'main' into docs/compute-gi-rc
reczkok Jan 28, 2026
0f6b2f3
refine and simpligy
reczkok Jan 29, 2026
15324e3
Merge branch 'main' into docs/compute-gi-rc
reczkok Jan 29, 2026
ea1ecac
Merge branch 'main' into docs/compute-gi-rc
reczkok Feb 9, 2026
21e177d
fix ci
reczkok Feb 9, 2026
bbe3d5c
work
reczkok Feb 9, 2026
2e35439
fixes&tweaks
reczkok Feb 9, 2026
0cafd39
Merge branch 'main' into docs/compute-gi-rc
reczkok Feb 17, 2026
ab0d227
Merge branch 'main' into docs/compute-gi-rc
reczkok Mar 11, 2026
2103feb
Merge branch 'main' into docs/compute-gi-rc
reczkok Apr 10, 2026
225ff52
cleaner + tsovver
reczkok Apr 10, 2026
1665fcb
improve
reczkok Apr 10, 2026
24fb17c
typo
reczkok Apr 10, 2026
49d0790
Merge branch 'main' into docs/compute-gi-rc
reczkok Apr 24, 2026
ceccef0
fixes
reczkok Apr 24, 2026
75077b3
example AA, resize logic and mobile controls
reczkok Apr 24, 2026
8bdc018
remove useless
reczkok Apr 24, 2026
3ada7df
less magic, better names, guarded
reczkok Apr 25, 2026
3c75a61
kill deno
reczkok Apr 25, 2026
f161939
thumbnails
reczkok Apr 25, 2026
291f82c
rename examples (more discoverable imo), add proper tags according to…
reczkok Apr 25, 2026
2a4f5dd
update test
reczkok Apr 25, 2026
9251eb1
refactor v1
reczkok Apr 25, 2026
d1710b1
fixes
reczkok Apr 25, 2026
55ab902
Merge remote-tracking branch 'origin/main' into impr/better-rc
reczkok Jun 15, 2026
551de8c
smol
reczkok Jun 16, 2026
c730436
Merge branch 'main' into impr/better-rc
reczkok Jun 17, 2026
bebc0b3
simplify
reczkok Jun 17, 2026
cf6d71f
more simplify
reczkok Jun 17, 2026
a3f9715
Merge remote-tracking branch 'origin/main' into impr/better-rc
reczkok Jun 17, 2026
6681107
remove ray direction workaround
reczkok Jun 17, 2026
cadaece
fix radiance cascade runner lint
reczkok Jun 17, 2026
945bc6e
simplify cascade pass dispatch
reczkok Jun 17, 2026
3a0c518
clean up final radiance sweep
reczkok Jun 17, 2026
3dc89d7
restore typegpu root data imports
reczkok Jun 17, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ context.configure({
});

const [width, height] = [canvas.width, canvas.height];
const initialBrushRadius = 0.015;

// Scene texture + views.
const sceneTexture = root
Expand Down Expand Up @@ -47,7 +48,7 @@ const paramsUniform = root.createUniform(DrawParams, {
isDrawing: 0,
lastMousePos: d.vec2f(0.5),
mousePos: d.vec2f(0.5),
brushRadius: 0.05,
brushRadius: initialBrushRadius,
lightColor: d.vec3f(1, 0.9, 0.7),
});

Expand Down Expand Up @@ -182,8 +183,10 @@ function drawScene() {

function updateScene() {
if (sceneDirty) {
floodRunner.run();
radianceRunner.run();
const encoder = root.device.createCommandEncoder();
floodRunner.run(encoder);
radianceRunner.run(encoder);
root.device.queue.submit([encoder.finish()]);
sceneDirty = false;
}
}
Expand Down Expand Up @@ -220,8 +223,8 @@ function frame(timestamp: number) {
export const controls = defineControls({
...drawInteraction.controls,
'Brush Size': {
initial: 0.015,
min: 0.015,
initial: initialBrushRadius,
min: initialBrushRadius,
max: 0.15,
step: 0.015,
onSliderChange(value: number) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { sdBox2d, sdDisk } from '@typegpu/sdf';
import type { AnySceneElement } from './scene.ts';
import { sceneElements } from './scene.ts';
import { d } from 'typegpu';
import * as d from 'typegpu/data';

type DragTarget = AnySceneElement;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,46 @@ const cascadeSampler = root.createSampler({
minFilter: 'linear',
});

const part1By1 = tgpu.fn(
[d.u32],
d.u32,
)((v) => {
'use gpu';
const x0 = v & 0x0000ffff;
const x1 = (x0 | (x0 << 8)) & 0x00ff00ff;
const x2 = (x1 | (x1 << 4)) & 0x0f0f0f0f;
const x3 = (x2 | (x2 << 2)) & 0x33333333;
return (x3 | (x3 << 1)) & 0x55555555;
});

const compact1By1 = tgpu.fn(
[d.u32],
d.u32,
)((v) => {
'use gpu';
const x0 = v & 0x55555555;
const x1 = (x0 ^ (x0 >> 1)) & 0x33333333;
const x2 = (x1 ^ (x1 >> 2)) & 0x0f0f0f0f;
const x3 = (x2 ^ (x2 >> 4)) & 0x00ff00ff;
return (x3 ^ (x3 >> 8)) & 0x0000ffff;
});

const morton2D = tgpu.fn(
[d.u32, d.u32],
d.u32,
)((x, y) => {
'use gpu';
return part1By1(x) | (part1By1(y) << 1);
});

const unmorton2D = tgpu.fn(
[d.u32],
d.vec2u,
)((index) => {
'use gpu';
return d.vec2u(compact1By1(index), compact1By1(index >> 1));
});

const cascadePassPipeline = root
.with(sceneDataAccess, sceneDataUniform)
.createGuardedComputePipeline((x, y) => {
Expand Down Expand Up @@ -136,7 +176,7 @@ const cascadePassPipeline = root

for (const i of tgpu.unroll(std.range(4))) {
const dirActual = dirStored * 2 + d.vec2u(i & 1, i >> 1);
const rayIndex = d.f32(dirActual.y * raysDimActual + dirActual.x) + 0.5;
const rayIndex = d.f32(morton2D(dirActual.x, dirActual.y)) + 0.5;
const angle = (rayIndex / d.f32(rayCountActual)) * (Math.PI * 2) - Math.PI;
const rayDir = d.vec2f(std.cos(angle), -std.sin(angle));

Expand Down Expand Up @@ -304,7 +344,8 @@ const overlayFrag = tgpu.fragmentFn({
const rayDist = sdf.sdLine(uv, probePos, probePos + rayDir * std.max(rayEndDistance, 0.01));

if (rayDist < minRayDist) {
const dirStored = d.vec2u((ri % raysDimActual) >> 1, d.u32(ri / raysDimActual) >> 1);
const dirActual = unmorton2D(ri);
const dirStored = d.vec2u(dirActual.x >> 1, dirActual.y >> 1);
const sample = std.textureLoad(
overlayDebugBGL.$.cascadeTex,
d.vec2i(dirStored * probes + probe),
Expand Down
67 changes: 60 additions & 7 deletions packages/typegpu-radiance-cascades/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,65 @@ const runner = createRadianceCascades({
runner.run();
```

## TypeGPU is created by Software Mansion
`run()` batches all cascade passes and the final radiance-field build into one
command buffer. Pass your own encoder to batch it with surrounding work:

[![swm](https://logo.swmansion.com/logo?color=white&variant=desktop&width=150&tag=typegpu-github 'Software Mansion')](https://swmansion.com)
```ts
const encoder = root.device.createCommandEncoder();
runner.run(encoder);
root.device.queue.submit([encoder.finish()]);
```

## Scene contract

The default marcher expects `sdf(uv)` to return a signed distance in
short-axis-normalized UV units:

- positive outside geometry
- negative inside geometry
- zero on the blocking or emitting surface

`color(uv)` should return linear radiance/emission. If the source texture is
sRGB-like, linearize it before returning from `color`.

## Quality and memory options

```ts
const runner = createRadianceCascades({
root,
size: { width, height },
sdfResolution,
sdf,
color,

// Direction density in the base cascade. 2 matches the classic default.
baseStoredRayDim: 2,

// hardware keeps the single filtered upper sample. bilinear-fix forks toward
// the four upper probes before merging.
mergeMode: 'bilinear-fix',

// Two active 2D ping-pong cascade textures are used by default. Enable this
// only when you need to inspect every cascade layer after a run.
keepCascadeLayers: false,

erodeBiasPx: 1,
epsPx: 0.25,
minStepPx: 0.125,
maxRaySteps: 64,
stepSafety: 1,
intervalOverlapPx: 0,
});
```

Available merge modes are:

- `'hardware'`
- `'bilinear-fix'`

`intervalOverlapPx` may also be set to `'upperProbeSpacing'` to use each
layer's upper-probe spacing as the overlap amount.

Since 2012 [Software Mansion](https://swmansion.com) is a software agency with
experience in building web and mobile apps. We are Core React Native
Contributors and experts in dealing with all kinds of React Native issues. We
can help you build your next dream product –
[Hire us](https://swmansion.com/contact/projects?utm_source=typegpu&utm_medium=readme).
If you provide an output texture view, `runner.output` is that view. When the
runner owns or can prove it has a sampled texture, `runner.outputTexture` is
also available for creating sampled views.
Loading
Loading