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// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9// The above copyright notice and this permission notice shall be included in
10// all copies or substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
18// THE SOFTWARE.
20import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput;
22struct CASUniforms {
23 sharpness: f32,
24};
26@group(0) @binding(0) var screenTexture: texture_2d<f32>;
27@group(0) @binding(1) var samp: sampler;
28@group(0) @binding(2) var<uniform> uniforms: CASUniforms;
30// This is set at the limit of providing unnatural results for sharpening.
31const FSR_RCAS_LIMIT = 0.1875;
32// -4.0 instead of -1.0 to avoid issues with MSAA.
33const peakC = vec2<f32>(10.0, -40.0);
35/// Robust Contrast Adaptive Sharpening (RCAS)
36/// Based on the following implementation:
37/// https://github.com/GPUOpen-Effects/FidelityFX-FSR2/blob/ea97a113b0f9cadf519fbcff315cc539915a3acd/src/ffx-fsr2-api/shaders/ffx_fsr1.h#L672
38/// RCAS is based on the following logic.
39/// RCAS uses a 5 tap filter in a cross pattern (same as CAS),
40/// W b
41/// W 1 W for taps d e f
42/// W h
43/// Where 'W' is the negative lobe weight.
44/// output = (W*(b+d+f+h)+e)/(4*W+1)
45/// RCAS solves for 'W' by seeing where the signal might clip out of the {0 to 1} input range,
46/// 0 == (W*(b+d+f+h)+e)/(4*W+1) -> W = -e/(b+d+f+h)
47/// 1 == (W*(b+d+f+h)+e)/(4*W+1) -> W = (1-e)/(b+d+f+h-4)
48/// Then chooses the 'W' which results in no clipping, limits 'W', and multiplies by the 'sharp' amount.
49/// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues.
50/// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps.
51/// As well as switching from 'e' to either the minimum or maximum (depending on side), to help in energy conservation.
52/// This stabilizes RCAS.
53/// RCAS does a simple highpass which is normalized against the local contrast then shaped,
54/// 0.25
55/// 0.25 -1 0.25
56/// 0.25
57/// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges.
58/// The CAS node runs after tonemapping, so the input will be in the range of 0 to 1.
59@fragment
60fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
61 // Algorithm uses minimal 3x3 pixel neighborhood.
62 // b
63 // d e f
64 // h
65 let b = textureSample(screenTexture, samp, in.uv, vec2<i32>(0, -1)).rgb;
66 let d = textureSample(screenTexture, samp, in.uv, vec2<i32>(-1, 0)).rgb;
67 // We need the alpha value of the pixel we're working on for the output
68 let e = textureSample(screenTexture, samp, in.uv).rgba;
69 let f = textureSample(screenTexture, samp, in.uv, vec2<i32>(1, 0)).rgb;
70 let h = textureSample(screenTexture, samp, in.uv, vec2<i32>(0, 1)).rgb;
71 // Min and max of ring.
72 let mn4 = min(min(b, d), min(f, h));
73 let mx4 = max(max(b, d), max(f, h));
74 // Limiters
75 // 4.0 to avoid issues with MSAA.
76 let hitMin = mn4 / (4.0 * mx4);
77 let hitMax = (peakC.x - mx4) / (peakC.y + 4.0 * mn4);
78 let lobeRGB = max(-hitMin, hitMax);
79 var lobe = max(-FSR_RCAS_LIMIT, min(0.0, max(lobeRGB.r, max(lobeRGB.g, lobeRGB.b)))) * uniforms.sharpness;
80@if(RCAS_DENOISE) {
81 // Luma times 2.
82 let bL = b.b * 0.5 + (b.r * 0.5 + b.g);
83 let dL = d.b * 0.5 + (d.r * 0.5 + d.g);
84 let eL = e.b * 0.5 + (e.r * 0.5 + e.g);
85 let fL = f.b * 0.5 + (f.r * 0.5 + f.g);
86 let hL = h.b * 0.5 + (h.r * 0.5 + h.g);
87 // Noise detection.
88 var noise = 0.25 * bL + 0.25 * dL + 0.25 * fL + 0.25 * hL - eL;;
89 noise = saturate(abs(noise) / (max(max(bL, dL), max(fL, hL)) - min(min(bL, dL), min(fL, hL))));
90 noise = 1.0 - 0.5 * noise;
91 // Apply noise removal.
92 lobe *= noise;
94 return vec4<f32>((lobe * b + lobe * d + lobe * f + lobe * h + e.rgb) / (4.0 * lobe + 1.0), e.w);
Contents