Skip to content

Instantly share code, notes, and snippets.

@SLSNe
Last active August 3, 2020 22:18
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SLSNe/bbaf2d77db0b2a2a0755df581b3cf00c to your computer and use it in GitHub Desktop.
Save SLSNe/bbaf2d77db0b2a2a0755df581b3cf00c to your computer and use it in GitHub Desktop.
CONSTRAST ADAPTIVE SHARPENING for reshade
// LICENSE
// =======
// Copyright (c) 2017-2019 Advanced Micro Devices, Inc. All rights reserved.
// -------
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
// -------
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
// -------
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
uniform float Sharpness <
ui_type = "drag";
ui_label = "Sharpening strength";
ui_tooltip = "0 := no sharpening, to 1 := full sharpening.\nScaled by the sharpness knob while being transformed to a negative lobe (values from -1/5 to -1/8 for A=1)";
ui_min = 0.0; ui_max = 1.0;
> = 0.0;
#include "ReShade.fxh"
float Min3(float x, float y, float z)
{
return min(x, min(y, z));
}
float Max3(float x, float y, float z)
{
return max(x, max(y, z));
}
float3 CASPass(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target
{
// fetch a 3x3 neighborhood around the pixel 'e',
// a b c
// d(e)f
// g h i
float pixelX = ReShade::PixelSize.x;
float pixelY = ReShade::PixelSize.y;
float3 a = tex2D(ReShade::BackBuffer, texcoord + float2(-pixelX, -pixelY)).rgb;
float3 b = tex2D(ReShade::BackBuffer, texcoord + float2(0.0, -pixelY)).rgb;
float3 c = tex2D(ReShade::BackBuffer, texcoord + float2(pixelX, -pixelY)).rgb;
float3 d = tex2D(ReShade::BackBuffer, texcoord + float2(-pixelX, 0.0)).rgb;
float3 e = tex2D(ReShade::BackBuffer, texcoord).rgb;
float3 f = tex2D(ReShade::BackBuffer, texcoord + float2(pixelX, 0.0)).rgb;
float3 g = tex2D(ReShade::BackBuffer, texcoord + float2(-pixelX, pixelY)).rgb;
float3 h = tex2D(ReShade::BackBuffer, texcoord + float2(0.0, pixelY)).rgb;
float3 i = tex2D(ReShade::BackBuffer, texcoord + float2(pixelX, pixelY)).rgb;
// Soft min and max.
// a b c b
// d e f * 0.5 + d e f * 0.5
// g h i h
// These are 2.0x bigger (factored out the extra multiply).
float mnR = Min3( Min3(d.r, e.r, f.r), b.r, h.r);
float mnG = Min3( Min3(d.g, e.g, f.g), b.g, h.g);
float mnB = Min3( Min3(d.b, e.b, f.b), b.b, h.b);
float mnR2 = Min3( Min3(mnR, a.r, c.r), g.r, i.r);
float mnG2 = Min3( Min3(mnG, a.g, c.g), g.g, i.g);
float mnB2 = Min3( Min3(mnB, a.b, c.b), g.b, i.b);
mnR = mnR + mnR2;
mnG = mnG + mnG2;
mnB = mnB + mnB2;
float mxR = Max3( Max3(d.r, e.r, f.r), b.r, h.r);
float mxG = Max3( Max3(d.g, e.g, f.g), b.g, h.g);
float mxB = Max3( Max3(d.b, e.b, f.b), b.b, h.b);
float mxR2 = Max3( Max3(mxR, a.r, c.r), g.r, i.r);
float mxG2 = Max3( Max3(mxG, a.g, c.g), g.g, i.g);
float mxB2 = Max3( Max3(mxB, a.b, c.b), g.b, i.b);
mxR = mxR + mxR2;
mxG = mxG + mxG2;
mxB = mxB + mxB2;
// Smooth minimum distance to signal limit divided by smooth max.
float rcpMR = rcp(mxR);
float rcpMG = rcp(mxG);
float rcpMB = rcp(mxB);
float ampR = saturate(min(mnR, 2.0 - mxR) * rcpMR);
float ampG = saturate(min(mnG, 2.0 - mxG) * rcpMG);
float ampB = saturate(min(mnB, 2.0 - mxB) * rcpMB);
// Shaping amount of sharpening.
ampR = sqrt(ampR);
ampG = sqrt(ampG);
ampB = sqrt(ampB);
// Filter shape.
// 0 w 0
// w 1 w
// 0 w 0
float peak = -rcp(lerp(8.0, 5.0, saturate(Sharpness)));
float wR = ampR * peak;
float wG = ampG * peak;
float wB = ampB * peak;
float rcpWeightR = rcp(1.0 + 4.0 * wR);
float rcpWeightG = rcp(1.0 + 4.0 * wG);
float rcpWeightB = rcp(1.0 + 4.0 * wB);
float3 outColor = float3(saturate((b.r*wR+d.r*wR+f.r*wR+h.r*wR+e.r)*rcpWeightR),
saturate((b.g*wG+d.g*wG+f.g*wG+h.g*wG+e.g)*rcpWeightG),
saturate((b.b*wB+d.b*wB+f.b*wB+h.b*wB+e.b)*rcpWeightB));
return outColor;
}
technique CAS
{
pass
{
VertexShader = PostProcessVS;
PixelShader = CASPass;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment