RT→rgba16float→threshold→blur→composite, end-to-end proof of the three primitives. threshold/blur run from onBeforeUpdate (one submit each) so the storage-write→sampled-read dependency gets WebGPU's per-submit barrier (there is none between dispatches within a compute pass); the composite is a UI custom shader so it owns the canvas ping-pong. Documents the Vulkan symmetry (gap 4): the native present path records passes generically and barriers between them, so the same chain is wireable today. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
1.9 KiB
WebGPU Shading Language
45 lines
1.9 KiB
WebGPU Shading Language
// HDRBloom closest-hit (runs in SHADE). Half the cubes are "emitters" that
|
|
// accumulate a strongly super-1.0 radiance (the HDR signal a bloom pass
|
|
// extracts); the rest are dim Lambert-shaded fillers near/below 1.0 that the
|
|
// threshold rejects. The linear accumulator is what RESOLVE writes into the
|
|
// rgba16float scene target — no tonemap here.
|
|
//
|
|
// Payload declared here so the assembler sees it before wfPayload / SHADE.
|
|
struct Payload {
|
|
color: vec3<f32>,
|
|
};
|
|
|
|
const SUN_DIR_TO_LIGHT: vec3<f32> = vec3<f32>(0.40, 0.85, 0.35);
|
|
const AMBIENT_COLOR: vec3<f32> = vec3<f32>(0.10, 0.11, 0.16);
|
|
|
|
// Distinct per-instance hue so emitters read as different coloured glows.
|
|
fn instanceAlbedo(i: u32) -> vec3<f32> {
|
|
let h = i * 2654435761u;
|
|
return vec3<f32>(
|
|
0.35 + 0.6 * f32((h >> 0u) & 255u) / 255.0,
|
|
0.35 + 0.6 * f32((h >> 8u) & 255u) / 255.0,
|
|
0.35 + 0.6 * f32((h >> 16u) & 255u) / 255.0);
|
|
}
|
|
|
|
fn closesthit_main(ray: RayDesc, hit: HitInfo, payload: ptr<function, Payload>) {
|
|
let meshRec = meshRecords[tlasEntries[hit.instanceId].blasMeshIdx];
|
|
let verts = _rtFetchTri(meshRec, hit.primitiveId);
|
|
let nObj = normalize(cross(verts[1] - verts[0], verts[2] - verts[0]));
|
|
let nWorld = normalize(vec3<f32>(
|
|
dot(hit.objectToWorldR0.xyz, nObj),
|
|
dot(hit.objectToWorldR1.xyz, nObj),
|
|
dot(hit.objectToWorldR2.xyz, nObj)));
|
|
|
|
let albedo = instanceAlbedo(hit.customIndex);
|
|
let viewDir = -ray.direction;
|
|
let nFacing = select(-nWorld, nWorld, dot(nWorld, viewDir) > 0.0);
|
|
let nDotL = max(0.0, dot(nFacing, normalize(SUN_DIR_TO_LIGHT)));
|
|
|
|
// Every third cube is a bright emitter (HDR, > 1.0); the rest stay dim.
|
|
if ((hit.customIndex % 3u) == 0u) {
|
|
// View-independent emission so the whole face glows uniformly.
|
|
rtAccumulate(albedo * 9.0);
|
|
} else {
|
|
rtAccumulate(albedo * (AMBIENT_COLOR + vec3<f32>(0.55 * nDotL)));
|
|
}
|
|
}
|