1import bevy_sprite_render::{mesh2d::vertex_output::VertexOutput, sprite_mesh::functions as sprite_fn};3struct DissolveMaterial {4 burn_edge_color: vec4<f32>,5 amount: f32,6 burn_edge: f32,8 // WebGL2 requires structs to be aligned to 16 bytes9 @if(SIXTEEN_BYTE_ALIGNMENT)10 _webgl2_padding_12b: u32,11 @if(SIXTEEN_BYTE_ALIGNMENT)12 _webgl2_padding_16b: u32,13}15@group(constants::MATERIAL_BIND_GROUP) @binding(20) var<uniform> material: DissolveMaterial;17@fragment18fn fragment(input: VertexOutput) -> @location(0) vec4<f32> {19 // Sample some simplex noise with the UV coordinates scaled up by 2 to make the noise denser20 var noise = fbm_simplex_2d(input.uv * 2.0);21 // Change the noise's range from 0..1 to burn_edge..1 - burn_edge22 // in order to avoid the burn edge showing up when the sprite is fully visible or fully hidden23 noise = mix(material.burn_edge, 1.0 - material.burn_edge, noise);25 // If this value is 0..burn_edge, the pixel is part of the burn edge26 // If it's >= burn_edge, the pixel is visible27 // Otherwise, it's hidden28 let difference = noise - material.amount;29 // Get the sprite's color30 let sprite_color = sprite_fn::sample_sprite_texture(input.uv, input.instance_index);32 var color = vec4(0.0);33 let burn_edge = difference >= 0 && difference < material.burn_edge;34 let visible = difference >= material.burn_edge;35 if burn_edge {36 color = material.burn_edge_color;37 color.a = sprite_color.a;38 } else if visible {39 color = sprite_color;40 }42 // Apply alpha discard and tint43 color = sprite_fn::get_final_color(color, input.instance_index);44 return color;45}47/// An implementation of the simplex noise algorithm to generate noise for the dissolve effect. Credit to <https://crates.io/crates/noisy_bevy>.48///49/// Note that usually sampling the noise from a prebaked texture is better for performance.50/// However, to avoid increasing the size of the bevy git repository, we generate the noise in the shader.51fn simplex_noise_2d(v: vec2<f32>) -> f32 {52 let C = vec4(53 0.211324865405187, // (3.0 - sqrt(3.0)) / 6.054 0.366025403784439, // 0.5 * (sqrt(3.0) - 1.0)55 -0.577350269189626, // -1.0 + 2.0 * C.x56 0.024390243902439 // 1.0 / 41.057 );59 // first corner60 var i = floor(v + dot(v, C.yy));61 let x0 = v - i + dot(i, C.xx);63 // other corners64 var i1 = select(vec2(0., 1.), vec2(1., 0.), x0.x > x0.y);65 var x12 = x0.xyxy + C.xxzz - vec4(i1, 0., 0.);67 // permutations68 i = i % vec2(289.);70 let p = permute_3_(permute_3_(i.y + vec3(0., i1.y, 1.)) + i.x + vec3(0., i1.x, 1.));71 var m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), vec3(0.));72 m *= m;73 m *= m;75 // gradients: 41 points uniformly over a line, mapped onto a diamond76 // the ring size, 17*17 = 289, is close to a multiple of 41 (41*7 = 287)77 let x = 2. * fract(p * C.www) - 1.;78 let h = abs(x) - 0.5;79 let ox = floor(x + 0.5);80 let a0 = x - ox;82 // normalize gradients implicitly by scaling m83 // approximation of: m *= inversesqrt(a0 * a0 + h * h);84 m = m * (1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h));86 // compute final noise value at P87 let g = vec3(a0.x * x0.x + h.x * x0.y, a0.yz * x12.xz + h.yz * x12.yw);88 return (130. * dot(m, g) + 1.) / 2.;89}91/// Fractional brownian motion using simplex noise. This approach makes the noise look more detailed.92fn fbm_simplex_2d(pos: vec2<f32>) -> f32 {93 const octaves = 3;94 const lacunarity = 2.0;95 const gain = 0.5;96 // Sum of the geometric series with `octaves` as the amount of terms and `gain` as the factor97 // This is the maximum possible value that the noise can reach98 const max = (1 - pow(gain, octaves)) / (1 - gain);100 var sum = 0.;101 var amplitude = 1.;102 var frequency = 1.;104 for (var i = 0; i < octaves; i+= 1) {105 sum += simplex_noise_2d(pos * frequency) * amplitude;106 amplitude *= gain;107 frequency *= lacunarity;108 }110 // Map the noise back to the 0..1 range.111 return sum / max;112}114fn permute_3_(x: vec3<f32>) -> vec3<f32> {115 return (((x * 34.) + 1.) * x) % vec3(289.);116}