Crafter.Graphics/examples/CustomShader/inverse-circle.comp.wgsl

58 lines
2.1 KiB
WebGPU Shading Language
Raw Normal View History

2026-05-18 05:39:17 +02:00
// DOM-mode port of inverse-circle.comp.glsl. Inverts RGB inside each
// user-supplied circle; passes through every other pixel so the
// ping-pong carries the prior dispatch's scene forward.
//
// Bind-group contract (mirrors :WebGPUComputeShader.cppm):
// group 0 binding 0 — uniform UIDispatchHeader (auto-injected)
// group 1 binding 0 — texture_storage_2d<rgba8unorm, write> out (auto)
// group 1 binding 1 — texture_2d<f32> prev (auto)
// group 2 binding 0 — items SSBO declared by the C++ side via
// UICustomBinding { group=2, binding=0,
// kind=Buffer, pushOffset=offsetof(hdr.itemBuffer) }
struct UIDispatchHeader {
outImage: u32,
itemBuffer: u32,
surfaceW: u32,
surfaceH: u32,
clipX: f32,
clipY: f32,
clipW: f32,
clipH: f32,
itemCount: u32,
frameIdx: u32,
flags: u32,
_pad: u32,
};
@group(0) @binding(0) var<uniform> hdr : UIDispatchHeader;
@group(1) @binding(0) var outTex : texture_storage_2d<rgba8unorm, write>;
@group(1) @binding(1) var prevTex : texture_2d<f32>;
struct InverseCircleItem {
centerRadius: vec4<f32>,
};
@group(2) @binding(0) var<storage, read> items : array<InverseCircleItem>;
@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
if (gid.x >= hdr.surfaceW || gid.y >= hdr.surfaceH) { return; }
let coord = vec2<i32>(i32(gid.x), i32(gid.y));
let sp = vec2<f32>(f32(gid.x) + 0.5, f32(gid.y) + 0.5);
// Max-coverage over all items, so overlapping circles don't
// double-invert back to the original.
var coverage: f32 = 0.0;
for (var i: u32 = 0u; i < hdr.itemCount; i = i + 1u) {
let it = items[i];
let c = it.centerRadius.xy;
let r = it.centerRadius.z;
let d = length(sp - c) - r;
coverage = max(coverage, clamp(0.5 - d, 0.0, 1.0));
}
var dst = textureLoad(prevTex, coord, 0);
if (coverage > 0.0) {
dst = vec4<f32>(mix(dst.rgb, vec3<f32>(1.0) - dst.rgb, coverage), dst.a);
}
textureStore(outTex, coord, dst);
}