Skip to content

@blooper.gg/toodle


@blooper.gg/toodle / textures/pixel-scraping.wgsl / default

Variable: default

default: "\n// ==============================\n// === BOUNDING BOX PASS =======\n// ==============================\n\n// Input texture from which to compute the non-transparent bounding box\n@group(0) @binding(0)\nvar input_texture: texture_2d<f32>;\n\n// Atomic bounding box storage structure\nstruct bounding_box_atomic {\n min_x: atomic<u32>,\n min_y: atomic<u32>,\n max_x: atomic<u32>,\n max_y: atomic<u32>,\n};\n\n// Storage buffer to hold atomic bounding box updates\n@group(0) @binding(1)\nvar<storage, read_write> bounds: bounding_box_atomic;\n\n// Compute shader to find the bounding box of non-transparent pixels\n@compute @workgroup_size(8, 8)\nfn find_bounds(@builtin(global_invocation_id) gid: vec3<u32>) {\n let size = textureDimensions(input_texture).xy;\n if (gid.x >= size.x || gid.y >= size.y) {\n return;\n }\n\n let pixel = textureLoad(input_texture, vec2<i32>(gid.xy), 0);\n if (pixel.a > 0.0) {\n atomicMin(&bounds.min_x, gid.x);\n atomicMin(&bounds.min_y, gid.y);\n atomicMax(&bounds.max_x, gid.x);\n atomicMax(&bounds.max_y, gid.y);\n }\n}\n\n// ==============================\n// === CROP + OUTPUT PASS ======\n// ==============================\n\n// Input texture from which cropped data is read\n@group(0) @binding(0)\nvar input_texture_crop: texture_2d<f32>;\n\n// Output texture where cropped image is written\n@group(0) @binding(1)\nvar output_texture: texture_storage_2d<rgba8unorm, write>;\n\n// Bounding box passed in as a uniform (not atomic anymore)\nstruct bounding_box_uniform {\n min_x: u32,\n min_y: u32,\n max_x: u32,\n max_y: u32,\n};\n\n@group(0) @binding(2)\nvar<uniform> bounds_uniform: bounding_box_uniform;\n\n// Struct to store both original and cropped texture dimensions\nstruct image_dimensions {\n original_width: u32,\n original_height: u32,\n cropped_width: u32,\n cropped_height: u32,\n};\n\n// Storage buffer to output the result dimensions\n@group(0) @binding(3)\nvar<storage, read_write> dimensions_out: image_dimensions;\n\n// Compute shader to crop the input texture to the bounding box and output it\n@compute @workgroup_size(8, 8)\nfn crop_and_output(@builtin(global_invocation_id) gid: vec3<u32>) {\n let size = textureDimensions(input_texture_crop).xy;\n\n let crop_width = bounds_uniform.max_x - bounds_uniform.min_x + 1u;\n let crop_height = bounds_uniform.max_y - bounds_uniform.min_y + 1u;\n\n if (gid.x >= crop_width || gid.y >= crop_height) {\n return;\n }\n\n let src_coord = vec2<i32>(\n i32(bounds_uniform.min_x + gid.x),\n i32(bounds_uniform.min_y + gid.y)\n );\n\n let dst_coord = vec2<i32>(i32(gid.x), i32(gid.y));\n let pixel = textureLoad(input_texture_crop, src_coord, 0);\n textureStore(output_texture, dst_coord, pixel);\n\n // Output dimensions from workgroup (0,0) only\n if (gid.x == 0u && gid.y == 0u) {\n dimensions_out.original_width = size.x;\n dimensions_out.original_height = size.y;\n dimensions_out.cropped_width = crop_width;\n dimensions_out.cropped_height = crop_height;\n }\n}\n\n// ==============================\n// === MISSING TEXTURE FILL ====\n// ==============================\n\n// Output texture to draw a fallback checkerboard\n@group(0) @binding(0)\nvar checker_texture: texture_storage_2d<rgba8unorm, write>;\n\n// Compute shader to fill a texture with a purple & green checkerboard\n@compute @workgroup_size(8, 8)\nfn missing_texture(@builtin(global_invocation_id) id: vec3<u32>) {\n let size = textureDimensions(checker_texture);\n if (id.x >= size.x || id.y >= size.y) {\n return;\n }\n\n let checker_size = 25u;\n let on_color = ((id.x / checker_size + id.y / checker_size) % 2u) == 0u;\n\n let color = select(\n vec4<f32>(0.5, 0.0, 0.5, 1.0), // Purple\n vec4<f32>(0.0, 1.0, 0.0, 1.0), // Green\n on_color\n );\n\n textureStore(checker_texture, vec2<i32>(id.xy), color);\n}\n"

Defined in: textures/pixel-scraping.wgsl.ts:1