|
| 1 | +// Texture (unsigned-integer, fetch-only) |
| 2 | +var sourceTexture: texture_2d<u32>; |
| 3 | + |
| 4 | +// Uniforms (auto-buffered, accessed as uniform.<name>) |
| 5 | +uniform mousePosition: vec2f; |
| 6 | +uniform brushRadius: f32; |
| 7 | + |
| 8 | +// Interpolated varying (from vertex shader) |
| 9 | +varying uv0: vec2f; |
| 10 | + |
| 11 | +// Color constants |
| 12 | +const whiteColor: vec3f = vec3f(1.0); |
| 13 | +const skyBlueColor: vec3f = vec3f(0.2, 0.2, 0.2); |
| 14 | +const yellowSandColor: vec3f = vec3f(0.73, 0.58, 0.26); |
| 15 | +const orangeSandColor: vec3f = vec3f(0.87, 0.43, 0.22); |
| 16 | +const graySandColor: vec3f = vec3f(0.13, 0.16, 0.17); |
| 17 | +const grayWallColor: vec3f = vec3f(0.5, 0.5, 0.5); |
| 18 | +const waterBlueColor: vec3f = vec3f(0.2, 0.3, 0.8); |
| 19 | + |
| 20 | +// Particle element constants |
| 21 | +const AIR: u32 = 0u; |
| 22 | +const SAND: u32 = 1u; |
| 23 | +const ORANGESAND: u32 = 2u; |
| 24 | +const GRAYSAND: u32 = 3u; |
| 25 | +const WALL: u32 = 4u; |
| 26 | + |
| 27 | +// Circle distance function |
| 28 | +fn circle(p: vec2f, r: f32) -> f32 { |
| 29 | + return length(p) - r; |
| 30 | +} |
| 31 | + |
| 32 | +const circleOutline: f32 = 0.0025; |
| 33 | + |
| 34 | +// Helper: check bounds in integer texel space |
| 35 | +fn isInBounds(c: vec2i, size: vec2i) -> bool { |
| 36 | + return (c.x > 0 && c.x < size.x - 1) && |
| 37 | + (c.y > 0 && c.y < size.y - 1); |
| 38 | +} |
| 39 | + |
| 40 | +// Particle representation |
| 41 | +struct Particle { |
| 42 | + element: u32, |
| 43 | + movedThisFrame: bool, |
| 44 | + shade: u32, |
| 45 | + waterMass: u32 // unused here |
| 46 | +}; |
| 47 | + |
| 48 | +// Pseudo-random generator |
| 49 | +fn rand(pos: vec2f, val: f32) -> f32 { |
| 50 | + return fract(pos.x * pos.y * val * 1000.0); |
| 51 | +} |
| 52 | + |
| 53 | +// Pack a Particle into a single u32 |
| 54 | +fn pack(p: Particle) -> u32 { |
| 55 | + var packed: u32 = 0u; |
| 56 | + packed |= (p.element & 0x7u); |
| 57 | + packed |= u32(p.movedThisFrame) << 3; |
| 58 | + packed |= ((p.shade & 0xFu) << 4); |
| 59 | + return packed; |
| 60 | +} |
| 61 | + |
| 62 | +// Unpack a u32 into a Particle |
| 63 | +fn unpack(packed: u32) -> Particle { |
| 64 | + var pt: Particle; |
| 65 | + pt.element = packed & 0x7u; |
| 66 | + pt.movedThisFrame = ((packed >> 3) & 0x1u) != 0u; |
| 67 | + pt.shade = (packed >> 4) & 0xFu; |
| 68 | + pt.waterMass = 0u; |
| 69 | + return pt; |
| 70 | +} |
| 71 | + |
| 72 | +// Fetch and decode a particle from the texture |
| 73 | +fn getParticle(coord: vec2i) -> Particle { |
| 74 | + let texel: vec4<u32> = textureLoad(sourceTexture, coord, 0); |
| 75 | + return unpack(texel.x); |
| 76 | +} |
| 77 | + |
| 78 | +@fragment |
| 79 | +fn fragmentMain(input: FragmentInput) -> FragmentOutput { |
| 80 | + var output: FragmentOutput; |
| 81 | + |
| 82 | + // Determine integer texture size & sample coordinate |
| 83 | + let dims: vec2u = textureDimensions(sourceTexture); |
| 84 | + let size: vec2i = vec2i(dims); |
| 85 | + let coord: vec2i = vec2i(input.uv0 * vec2f(size)); |
| 86 | + |
| 87 | + let particle = getParticle(coord); |
| 88 | + |
| 89 | + var gameColor: vec3f = skyBlueColor; |
| 90 | + if (particle.element == SAND) { |
| 91 | + gameColor = mix(yellowSandColor, whiteColor, (f32(particle.shade) / 15.0) * 0.5); |
| 92 | + } else if (particle.element == WALL) { |
| 93 | + gameColor = grayWallColor; |
| 94 | + } else if (particle.element == ORANGESAND) { |
| 95 | + gameColor = mix(orangeSandColor, whiteColor, (f32(particle.shade) / 15.0) * 0.5); |
| 96 | + } else if (particle.element == GRAYSAND) { |
| 97 | + gameColor = mix(graySandColor, whiteColor, (f32(particle.shade) / 15.0) * 0.5); |
| 98 | + } |
| 99 | + |
| 100 | + // Render a brush circle |
| 101 | + let d: f32 = length(input.uv0 - uniform.mousePosition); |
| 102 | + let wd: f32 = fwidth(d); |
| 103 | + let circleVal: f32 = smoothstep(uniform.brushRadius + wd, uniform.brushRadius, d); |
| 104 | + let circleInner: f32 = smoothstep(uniform.brushRadius - circleOutline + wd, uniform.brushRadius - circleOutline, d); |
| 105 | + let brush: f32 = max(circleVal - circleInner, 0.0) * 0.5; |
| 106 | + |
| 107 | + let outColor: vec3f = mix(gameColor, vec3f(1.0), brush); |
| 108 | + |
| 109 | + output.color = vec4f(outColor, 1.0); |
| 110 | + return output; |
| 111 | +} |
| 112 | + |
0 commit comments