Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1#import bevy_render::view::View;
2#import bevy_render::globals::Globals;
4const PI: f32 = 3.14159265358979323846;
5const SAMPLES: i32 = #SHADOW_SAMPLES;
7@group(0) @binding(0) var<uniform> view: View;
9struct BoxShadowVertexOutput {
10 @builtin(position) position: vec4<f32>,
11 @location(0) point: vec2<f32>,
12 @location(1) color: vec4<f32>,
13 @location(2) @interpolate(flat) size: vec2<f32>,
14 @location(3) @interpolate(flat) radius: vec4<f32>,
15 @location(4) @interpolate(flat) blur: f32,
18fn gaussian(x: f32, sigma: f32) -> f32 {
19 return exp(-(x * x) / (2. * sigma * sigma)) / (sqrt(2. * PI) * sigma);
22// Approximates the Gauss error function: https://en.wikipedia.org/wiki/Error_function
23fn erf(p: vec2<f32>) -> vec2<f32> {
24 let s = sign(p);
25 let a = abs(p);
26 // fourth degree polynomial approximation for erf
27 var result = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
28 result = result * result;
29 return s - s / (result * result);
32// returns the closest corner radius based on the signs of the components of p
33fn selectCorner(p: vec2<f32>, c: vec4<f32>) -> f32 {
34 return mix(mix(c.x, c.y, step(0., p.x)), mix(c.w, c.z, step(0., p.x)), step(0., p.y));
37fn horizontalRoundedBoxShadow(x: f32, y: f32, blur: f32, corner: f32, half_size: vec2<f32>) -> f32 {
38 let d = min(half_size.y - corner - abs(y), 0.);
39 let c = half_size.x - corner + sqrt(max(0., corner * corner - d * d));
40 let integral = 0.5 + 0.5 * erf((x + vec2(-c, c)) * (sqrt(0.5) / blur));
41 return integral.y - integral.x;
44fn roundedBoxShadow(
45 lower: vec2<f32>,
46 upper: vec2<f32>,
47 point: vec2<f32>,
48 blur: f32,
49 corners: vec4<f32>,
50) -> f32 {
51 let center = (lower + upper) * 0.5;
52 let half_size = (upper - lower) * 0.5;
53 let p = point - center;
54 let low = p.y - half_size.y;
55 let high = p.y + half_size.y;
56 let start = clamp(-3. * blur, low, high);
57 let end = clamp(3. * blur, low, high);
58 let step = (end - start) / f32(SAMPLES);
59 var y = start + step * 0.5;
60 var value: f32 = 0.0;
61 for (var i = 0; i < SAMPLES; i++) {
62 let corner = selectCorner(p, corners);
63 value += horizontalRoundedBoxShadow(p.x, p.y - y, blur, corner, half_size) * gaussian(y, blur) * step;
64 y += step;
65 }
66 return value;
69@vertex
70fn vertex(
71 @location(0) vertex_position: vec3<f32>,
72 @location(1) uv: vec2<f32>,
73 @location(2) vertex_color: vec4<f32>,
74 @location(3) size: vec2<f32>,
75 @location(4) radius: vec4<f32>,
76 @location(5) blur: f32,
77 @location(6) bounds: vec2<f32>,
78) -> BoxShadowVertexOutput {
79 var out: BoxShadowVertexOutput;
80 out.position = view.clip_from_world * vec4(vertex_position, 1.0);
81 out.point = (uv.xy - 0.5) * bounds;
82 out.color = vertex_color;
83 out.size = size;
84 out.radius = radius;
85 out.blur = blur;
86 return out;
89@fragment
90fn fragment(
91 in: BoxShadowVertexOutput,
92) -> @location(0) vec4<f32> {
93 let g = in.color.a * roundedBoxShadow(-0.5 * in.size, 0.5 * in.size, in.point, max(in.blur, 0.01), in.radius);
94 return vec4(in.color.rgb, g);
Contents