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// A library containing a 1D Gaussian blur kernel.
2//
3// This is used by depth of field, but you can also use it in custom
4// postprocessing passes.
6#define_import_path bevy_post_process::gaussian_blur
8// Performs a single direction of the separable Gaussian blur kernel.
9//
10// * `color_texture` is the texture to blur.
11//
12// * `color_texture_sampler` is a sampler to sample the texture. It must have
13// linear filtering for both minification and magnification.
14//
15// * `frag_coord` is the screen-space pixel coordinate of the fragment (i.e. the
16// `position` input to the fragment).
17//
18// * `coc` is the diameter (not the radius) of the circle of confusion for this
19// fragment.
20//
21// * `frag_offset` is the vector, in screen-space units, from one sample to the
22// next. For a horizontal blur this will be `vec2(1.0, 0.0)`; for a vertical
23// blur this will be `vec2(0.0, 1.0)`.
24//
25// Returns the resulting color of the fragment.
26fn gaussian_blur(color_texture: texture_2d<f32>, color_texture_sampler: sampler, frag_coord: vec4<f32>, coc: f32, frag_offset: vec2<f32>) -> vec4<f32> {
27 // Usually σ (the standard deviation) is half the radius, and the radius is
28 // half the CoC. So we multiply by 0.25.
29 let sigma = coc * 0.25;
31 // 1.5σ is a good, somewhat aggressive default for support—the number of
32 // texels on each side of the center that we process.
33 let support = i32(ceil(sigma * 1.5));
34 let uv = frag_coord.xy / vec2<f32>(textureDimensions(color_texture));
35 let offset = frag_offset / vec2<f32>(textureDimensions(color_texture));
37 // The probability density function of the Gaussian blur is (up to constant factors) `exp(-1 / 2σ² *
38 // x²). We precalculate the constant factor here to avoid having to
39 // calculate it in the inner loop.
40 let exp_factor = -1.0 / (2.0 * sigma * sigma);
42 // Accumulate samples on both sides of the current texel. Go two at a time,
43 // taking advantage of bilinear filtering.
44 var sum = textureSampleLevel(color_texture, color_texture_sampler, uv, 0.0).rgb;
45 var weight_sum = 1.0;
46 for (var i = 1; i <= support; i += 2) {
47 // This is a well-known trick to reduce the number of needed texture
48 // samples by a factor of two. We seek to accumulate two adjacent
49 // samples c₀ and c₁ with weights w₀ and w₁ respectively, with a single
50 // texture sample at a carefully chosen location. Observe that:
51 //
52 // k ⋅ lerp(c₀, c₁, t) = w₀⋅c₀ + w₁⋅c₁
53 //
54 // w₁
55 // if k = w₀ + w₁ and t = ───────
56 // w₀ + w₁
57 //
58 // Therefore, if we sample at a distance of t = w₁ / (w₀ + w₁) texels in
59 // between the two texel centers and scale by k = w₀ + w₁ afterward, we
60 // effectively evaluate w₀⋅c₀ + w₁⋅c₁ with a single texture lookup.
61 let w0 = exp(exp_factor * f32(i) * f32(i));
62 let w1 = exp(exp_factor * f32(i + 1) * f32(i + 1));
63 let uv_offset = offset * (f32(i) + w1 / (w0 + w1));
64 let weight = w0 + w1;
66 sum += (
67 textureSampleLevel(color_texture, color_texture_sampler, uv + uv_offset, 0.0).rgb +
68 textureSampleLevel(color_texture, color_texture_sampler, uv - uv_offset, 0.0).rgb
69 ) * weight;
70 weight_sum += weight * 2.0;
71 }
73 return vec4(sum / weight_sum, 1.0);
Contents