duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

Deband.fx (9551B)


      1 /**
      2  * Deband shader by haasn
      3  * https://github.com/haasn/gentoo-conf/blob/xor/home/nand/.mpv/shaders/deband-pre.glsl
      4  *
      5  * Copyright (c) 2015 Niklas Haas
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a copy
      8  * of this software and associated documentation files (the "Software"), to deal
      9  * in the Software without restriction, including without limitation the rights
     10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11  * copies of the Software, and to permit persons to whom the Software is
     12  * furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included in
     15  * all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE.
     24  *
     25  * Modified and optimized for ReShade by JPulowski
     26  * https://reshade.me/forum/shader-presentation/768-deband
     27  *
     28  * Do not distribute without giving credit to the original author(s).
     29  *
     30  * 1.0  - Initial release
     31  * 1.1  - Replaced the algorithm with the one from MPV
     32  * 1.1a - Minor optimizations
     33  *      - Removed unnecessary lines and replaced them with ReShadeFX intrinsic counterparts
     34  * 2.0  - Replaced "grain" with CeeJay.dk's ordered dithering algorithm and enabled it by default
     35  *      - The configuration is now more simpler and straightforward
     36  *      - Some minor code changes and optimizations
     37  *      - Improved the algorithm and made it more robust by adding some of the madshi's
     38  *        improvements to flash3kyuu_deband which should cause an increase in quality. Higher
     39  *        iterations/ranges should now yield higher quality debanding without too much decrease
     40  *        in quality.
     41  *      - Changed licensing text and original source code URL
     42  * 3.0  - Replaced the entire banding detection algorithm with modified standard deviation and
     43  *        Weber ratio analyses which give more accurate and error-free results compared to the
     44  *        previous algorithm
     45  *      - Added banding map debug view
     46  *      - Added and redefined UI categories
     47  *      - Added depth detection (credits to spiro) which should be useful when banding only
     48  *        occurs in the sky texture for example
     49  *      - Fixed a bug in random number generation which was causing artifacts on the upper left
     50  *        side of the screen
     51  *      - Dithering is now applied only when debanding a pixel as it should be which should
     52  *        reduce the overall noise in the final texture
     53  *      - Minor code optimizations
     54  * 3.1  - Switched to chroma-based analysis from luma-based analysis which was causing artifacts
     55  *        under some scenarios
     56  *      - Changed parts of the code which was causing compatibility issues on some renderers
     57  */
     58 
     59 #include "ReShadeUI.fxh"
     60 #include "ReShade.fxh"
     61 
     62 uniform bool enable_weber <
     63     ui_category = "Banding analysis";
     64     ui_label = "Weber ratio";
     65     ui_tooltip = "Weber ratio analysis that calculates the ratio of the each local pixel's intensity to average background intensity of all the local pixels.";
     66     ui_type = "radio";
     67 > = true;
     68 
     69 uniform bool enable_sdeviation <
     70     ui_category = "Banding analysis";
     71     ui_label = "Standard deviation";
     72     ui_tooltip = "Modified standard deviation analysis that calculates nearby pixels' intensity deviation from the current pixel instead of the mean.";
     73     ui_type = "radio";
     74 > = true;
     75 
     76 uniform bool enable_depthbuffer <
     77     ui_category = "Banding analysis";
     78     ui_label = "Depth detection";
     79     ui_tooltip = "Allows depth information to be used when analysing banding, pixels will only be analysed if they are in a certain depth. (e.g. debanding only the sky)";
     80     ui_type = "radio";
     81 > = false;
     82 
     83 uniform float t1 <
     84     ui_category = "Banding analysis";
     85     ui_label = "Standard deviation threshold";
     86     ui_max = 0.5;
     87     ui_min = 0.0;
     88     ui_step = 0.001;
     89     ui_tooltip = "Standard deviations lower than this threshold will be flagged as flat regions with potential banding.";
     90     ui_type = "slider";
     91 > = 0.007;
     92 
     93 uniform float t2 <
     94     ui_category = "Banding analysis";
     95     ui_label = "Weber ratio threshold";
     96     ui_max = 2.0;
     97     ui_min = 0.0;
     98     ui_step = 0.01;
     99     ui_tooltip = "Weber ratios lower than this threshold will be flagged as flat regions with potential banding.";
    100     ui_type = "slider";
    101 > = 0.04;
    102 
    103 uniform float banding_depth <
    104     ui_category = "Banding analysis";
    105     ui_label = "Banding depth";
    106     ui_max = 1.0;
    107     ui_min = 0.0;
    108     ui_step = 0.001;
    109     ui_tooltip = "Pixels under this depth threshold will not be processed and returned as they are.";
    110     ui_type = "slider";
    111 > = 1.0;
    112 
    113 uniform float range <
    114     ui_category = "Banding detection & removal";
    115     ui_label = "Radius";
    116     ui_max = 32.0;
    117     ui_min = 1.0;
    118     ui_step = 1.0;
    119     ui_tooltip = "The radius increases linearly for each iteration. A higher radius will find more gradients, but a lower radius will smooth more aggressively.";
    120     ui_type = "slider";
    121 > = 24.0;
    122 
    123 uniform int iterations <
    124     ui_category = "Banding detection & removal";
    125     ui_label = "Iterations";
    126     ui_max = 4;
    127     ui_min = 1;
    128     ui_tooltip = "The number of debanding steps to perform per sample. Each step reduces a bit more banding, but takes time to compute.";
    129     ui_type = "slider";
    130 > = 1;
    131 
    132 uniform int debug_output <
    133     ui_category = "Debug";
    134     ui_items = "None\0Blurred (LPF) image\0Banding map\0";
    135     ui_label = "Debug view";
    136     ui_tooltip = "Blurred (LPF) image: Useful when tweaking radius and iterations to make sure all banding regions are blurred enough.\nBanding map: Useful when tweaking analysis parameters, continuous green regions indicate flat (i.e. banding) regions.";
    137     ui_type = "combo";
    138 > = 0;
    139 
    140 // Reshade uses C rand for random, max cannot be larger than 2^15-1
    141 uniform int drandom < source = "random"; min = 0; max = 32767; >;
    142 
    143 float rand(float x)
    144 {
    145     return frac(x / 41.0);
    146 }
    147 
    148 float permute(float x)
    149 {
    150     return ((34.0 * x + 1.0) * x) % 289.0;
    151 }
    152 
    153 float3 PS_Deband(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target
    154 {
    155     float3 ori = tex2Dlod(ReShade::BackBuffer, float4(texcoord, 0.0, 0.0)).rgb;
    156 
    157     if (enable_depthbuffer && (ReShade::GetLinearizedDepth(texcoord) < banding_depth))
    158         return ori;
    159 
    160     // Initialize the PRNG by hashing the position + a random uniform
    161     float3 m = float3(texcoord + 1.0, (drandom / 32767.0) + 1.0);
    162     float h = permute(permute(permute(m.x) + m.y) + m.z);
    163 
    164     // Compute a random angle
    165     float dir  = rand(permute(h)) * 6.2831853;
    166     float2 o;
    167     sincos(dir, o.y, o.x);
    168     
    169     // Distance calculations
    170     float2 pt;
    171     float dist;
    172 
    173     for (int i = 1; i <= iterations; ++i) {
    174         dist = rand(h) * range * i;
    175         pt = dist * BUFFER_PIXEL_SIZE;
    176     
    177         h = permute(h);
    178     }
    179     
    180     // Sample at quarter-turn intervals around the source pixel
    181     float3 ref[4] = {
    182         tex2Dlod(ReShade::BackBuffer, float4(mad(pt,                  o, texcoord), 0.0, 0.0)).rgb, // SE
    183         tex2Dlod(ReShade::BackBuffer, float4(mad(pt,                 -o, texcoord), 0.0, 0.0)).rgb, // NW
    184         tex2Dlod(ReShade::BackBuffer, float4(mad(pt, float2(-o.y,  o.x), texcoord), 0.0, 0.0)).rgb, // NE
    185         tex2Dlod(ReShade::BackBuffer, float4(mad(pt, float2( o.y, -o.x), texcoord), 0.0, 0.0)).rgb  // SW
    186     };
    187 
    188     // Calculate weber ratio
    189     float3 mean = (ori + ref[0] + ref[1] + ref[2] + ref[3]) * 0.2;
    190     float3 k = abs(ori - mean);
    191     for (int j = 0; j < 4; ++j) {
    192         k += abs(ref[j] - mean);
    193     }
    194 
    195     k = k * 0.2 / mean;
    196 
    197     // Calculate std. deviation
    198     float3 sd = 0.0;
    199 
    200     for (int j = 0; j < 4; ++j) {
    201         sd += pow(ref[j] - ori, 2);
    202     }
    203 
    204     sd = sqrt(sd * 0.25);
    205 
    206     // Generate final output
    207     float3 output;
    208 
    209     if (debug_output == 2)
    210         output = float3(0.0, 1.0, 0.0);
    211     else
    212         output = (ref[0] + ref[1] + ref[2] + ref[3]) * 0.25;
    213 
    214     // Generate a binary banding map
    215     bool3 banding_map = true;
    216 
    217     if (debug_output != 1) {
    218         if (enable_weber)
    219             banding_map = banding_map && k <= t2 * iterations;
    220 
    221         if (enable_sdeviation)
    222             banding_map = banding_map && sd <= t1 * iterations;
    223     }
    224 
    225 	/*------------------------.
    226 	| :: Ordered Dithering :: |
    227 	'------------------------*/
    228 	//Calculate grid position
    229 	float grid_position = frac(dot(texcoord, (BUFFER_SCREEN_SIZE * float2(1.0 / 16.0, 10.0 / 36.0)) + 0.25));
    230 
    231 	//Calculate how big the shift should be
    232 	float dither_shift = 0.25 * (1.0 / (pow(2, BUFFER_COLOR_BIT_DEPTH) - 1.0));
    233 
    234 	//Shift the individual colors differently, thus making it even harder to see the dithering pattern
    235 	float3 dither_shift_RGB = float3(dither_shift, -dither_shift, dither_shift); //subpixel dithering
    236 
    237 	//modify shift acording to grid position.
    238 	dither_shift_RGB = lerp(2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position); //shift acording to grid position.
    239     
    240     return banding_map ? output + dither_shift_RGB : ori;
    241 }
    242 
    243 technique Deband <
    244 ui_tooltip = "Alleviates color banding by trying to approximate original color values.";
    245 >
    246 {
    247     pass
    248     {
    249         VertexShader = PostProcessVS;
    250         PixelShader = PS_Deband;
    251     }
    252 }