About Nvidia Sharpen
- sanek7814
-
Topic Author
Please Log in or Create an account to join the conversation.
github.com/NVIDIAGameWorks/NVIDIAImageScaling
(Docs say to set NIS_SCALER=0 to make it sharpen only and not scale.)
It's for integrating in games, but one could probably port it to ReShade.
Please Log in or Create an account to join the conversation.
- sanek7814
-
Topic Author
Please Log in or Create an account to join the conversation.
- odikzz2
-
Please Log in or Create an account to join the conversation.
- YF
-
- sanek7814
-
Topic Author
Please Log in or Create an account to join the conversation.
- YF
-
uniform float sharpness <
ui_type = "slider";
ui_label = "sharpness";
ui_min = 0.0; ui_max = 1.0;ui_step = 0.01;
> = 0.2;
uniform float kContrastBoost <
ui_type = "slider";
ui_label = "kContrastBoost";
ui_min = 0.0; ui_max = 1.0;ui_step = 0.01;
> = 1.0;
#include "ReShade.fxh"
#define inputPtX BUFFER_RCP_WIDTH
#define inputPtY BUFFER_RCP_HEIGHT
#define kDetectRatio (1127.f / 1024.f)
#define kDetectThres (64.0f / 1024.0f)
#define kEps 1.0f
#define NIS_SCALE_FLOAT 1.0f
#define kMinContrastRatio 2.0f
#define kMaxContrastRatio 10.0f
#define kRatioNorm (1.0f / (kMaxContrastRatio - kMinContrastRatio))
#define kSharpStartY 0.45f
#define kSharpEndY 0.9f
#define kSharpScaleY (1.0f / (kSharpEndY - kSharpStartY))
#define sharpen_slider (sharpness - 0.5f)
#define MinScale ((sharpen_slider >= 0.0f) ? 1.25f : 1.0f)
#define MaxScale ((sharpen_slider >= 0.0f) ? 1.25f : 1.75f)
#define kSharpStrengthMin max(0.0f, 0.4f + sharpen_slider * MinScale * 1.2f)
#define kSharpStrengthMax (1.6f + sharpen_slider * 1.8f)
#define kSharpStrengthScale (kSharpStrengthMax - kSharpStrengthMin)
#define kSharpLimitMin max(0.1f, 0.14f + sharpen_slider * LimitScale * 0.32f)
#define kSharpLimitMax (0.5f + sharpen_slider * LimitScale * 0.6f)
#define kSharpLimitScale (kSharpLimitMax - kSharpLimitMin)
#define LimitScale ((sharpen_slider >= 0.0f) ? 1.25f : 1.0f)
#define kSupportSize 5
float getY(float3 rgba) {
return 0.2126f * rgba.x + 0.7152f * rgba.y + 0.0722f * rgba.z;
}
float4 GetEdgeMap(float p[25], int i, int j) {
const float g_0 = abs(p[i+5*j] + p[i+5*(j+1)] + p[i+5*(j+2)] - p[i+2+5*j] - p[i+2+5*(j+1)] - p[i+2+5*(j+2)]);
const float g_45 = abs(p[i+1+5*j] + p[i+5*j] + p[i+5*(j+1)] - p[i+2+5*(j+1)] - p[i+2+5*(j+2)] - p[i+1+5*(j+2)]);
const float g_90 = abs(p[i+5*j] + p[i+1+5*j] + p[i+2+5*j] - p[i+5*(j+2)] - p[i+1+5*(j+2)] - p[i+2+5*(j+2)]);
const float g_135 = abs(p[i+1+5*j] + p[i+2+5*j] + p[i+2+5*(j+1)] - p[i+5*(j+1)] - p[i+5*(j+2)] - p[i+1+5*(j+2)]);
const float g_0_90_max = max(g_0, g_90);
const float g_0_90_min = min(g_0, g_90);
const float g_45_135_max = max(g_45, g_135);
const float g_45_135_min = min(g_45, g_135);
float e_0_90 = 0;
float e_45_135 = 0;
if ((g_0_90_max + g_45_135_max) != 0) {
e_0_90 = g_0_90_max / (g_0_90_max + g_45_135_max);
e_0_90 = min(e_0_90, 1.0f);
e_45_135 = 1.0f - e_0_90;
}
float e = ((g_0_90_max > (g_0_90_min * kDetectRatio)) && (g_0_90_max > kDetectThres) && (g_0_90_max > g_45_135_min)) ? 1.f : 0.f;
float edge_0 = (g_0_90_max == g_0) ? e : 0.f;
float edge_90 = (g_0_90_max == g_0) ? 0.f : e;
e = ((g_45_135_max > (g_45_135_min * kDetectRatio)) && (g_45_135_max > kDetectThres) && (g_45_135_max > g_0_90_min)) ? 1.f : 0.f;
float edge_45 = (g_45_135_max == g_45) ? e : 0.f;
float edge_135 = (g_45_135_max == g_45) ? 0.f : e;
float weight_0 = 0.f;
float weight_90 = 0.f;
float weight_45 = 0.f;
float weight_135 = 0.f;
if ((edge_0 + edge_90 + edge_45 + edge_135) >= 2.0f) {
weight_0 = (edge_0 == 1.0f) ? e_0_90 : 0.f;
weight_90 = (edge_0 == 1.0f) ? 0.f : e_0_90;
weight_45 = (edge_45 == 1.0f) ? e_45_135 : 0.f;
weight_135 = (edge_45 == 1.0f) ? 0.f : e_45_135;
} else if ((edge_0 + edge_90 + edge_45 + edge_135) >= 1.0f) {
weight_0 = edge_0;
weight_90 = edge_90;
weight_45 = edge_45;
weight_135 = edge_135;
}
return float4(weight_0, weight_90, weight_45, weight_135);
}
float CalcLTIFast(const float y[5]) {
const float a_min = min(min(y[0], y[1]), y[2]);
const float a_max = max(max(y[0], y[1]), y[2]);
const float b_min = min(min(y[2], y[3]), y[4]);
const float b_max = max(max(y[2], y[3]), y[4]);
const float a_cont = a_max - a_min;
const float b_cont = b_max - b_min;
const float cont_ratio = max(a_cont, b_cont) / (min(a_cont, b_cont) + kEps * (1.0f / NIS_SCALE_FLOAT));
return (1.0f - saturate((cont_ratio - kMinContrastRatio) * kRatioNorm)) * kContrastBoost;
}
float EvalUSM(const float pxl[5], const float sharpnessStrength, const float sharpnessLimit) {
// USM profile
float y_usm = -0.6001f * pxl[1] + 1.2002f * pxl[2] - 0.6001f * pxl[3];
// boost USM profile
y_usm *= sharpnessStrength;
// clamp to the limit
y_usm = min(sharpnessLimit, max(-sharpnessLimit, y_usm));
// reduce ringing
y_usm *= CalcLTIFast(pxl);
return y_usm;
}
float4 GetDirUSM(const float p[25]) {
// sharpness boost & limit are the same for all directions
const float scaleY = 1.0f - saturate((p[12] - kSharpStartY) * kSharpScaleY);
// scale the ramp to sharpen as a function of luma
const float sharpnessStrength = scaleY * kSharpStrengthScale + kSharpStrengthMin;
// scale the ramp to limit USM as a function of luma
const float sharpnessLimit = (scaleY * kSharpLimitScale + kSharpLimitMin) * p[12];
float4 rval;
// 0 deg filter
float interp0Deg[5];
{
[unroll]
for (int i = 0; i < 5; ++i) {
interp0Deg = p[i+10];
}
}
rval.x = EvalUSM(interp0Deg, sharpnessStrength, sharpnessLimit);
// 90 deg filter
float interp90Deg[5];
{
[unroll]
for (int i = 0; i < 5; ++i) {
interp90Deg = p[2+5*i];
}
}
rval.y = EvalUSM(interp90Deg, sharpnessStrength, sharpnessLimit);
//45 deg filter
float interp45Deg[5];
interp45Deg[0] = p[6];
interp45Deg[1] = lerp(p[7], p[11], 0.5f);
interp45Deg[2] = p[12];
interp45Deg[3] = lerp(p[13], p[17], 0.5f);
interp45Deg[4] = p[18];
rval.z = EvalUSM(interp45Deg, sharpnessStrength, sharpnessLimit);
//135 deg filter
float interp135Deg[5];
interp135Deg[0] = p[8];
interp135Deg[1] = lerp(p[13], p[7], 0.5f);
interp135Deg[2] = p[12];
interp135Deg[3] = lerp(p[17], p[11], 0.5f);
interp135Deg[4] = p[16];
rval.w = EvalUSM(interp135Deg, sharpnessStrength, sharpnessLimit);
return rval;
}
float4 NVSPass(float4 vpos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
{
float p[25];
[unroll]
for (int i = 0; i < 5; ++i) {
[unroll]
for (int j = 0; j < 5; ++j) {
p[i+5*j] = getY(tex2D(ReShade::BackBuffer, texcoord + float2(j - 2, i - 2) * float2(inputPtX, inputPtY)).rgb);
}
}
// get directional filter bank output
const float4 dirUSM = GetDirUSM(p);
// generate weights for directional filters
float4 w = GetEdgeMap(p, kSupportSize / 2 - 1, kSupportSize / 2 - 1);
// final USM is a weighted sum filter outputs
const float usmY = (dirUSM.x * w.x + dirUSM.y * w.y + dirUSM.z * w.z + dirUSM.w * w.w);
float4 op = tex2D(ReShade::BackBuffer, texcoord);
op.x += usmY;
op.y += usmY;
op.z += usmY;
return op;
}
technique NVSharpen
{
pass
{
VertexShader = PostProcessVS;
PixelShader = NVSPass;
}
}
Please Log in or Create an account to join the conversation.
- sanek7814
-
Topic Author
Error x3020: cannot convert these types (from float to float[5])
Please Log in or Create an account to join the conversation.
- YF
-
Please Log in or Create an account to join the conversation.
- lordbean
-
float interp0Deg[5];
{
[unroll]
for (int i = 0; i < 5; ++i) {
interp0Deg[i] = p[i+10];
}
}
rval.x = EvalUSM(interp0Deg, sharpnessStrength, sharpnessLimit);
// 90 deg filter
float interp90Deg[5];
{
[unroll]
for (int i = 0; i < 5; ++i) {
interp90Deg[i] = p[2+5*i];
}
}
Edit: this fix is probably actually present already in YF's ported code. If you look closely though, the forum's BBcode is interpreting the array reference tag to mean "start italics here" and removing it. Using the code header instead of the spoiler header fixes that.
Please Log in or Create an account to join the conversation.
- sanek7814
-
Topic Author
P.S Either it's a bug, or it's a sharpness feature from nvidia. The fact is that, for example, when using CAS or DELC, the sharpness goes all over the picture (even houses in the distance become visible) In the case of nvidia sharpen, these very houses are not visible. As if sharpness is added only to nearby objects.
Please Log in or Create an account to join the conversation.
- odikzz2
-
Details.yaml
samplerstates:
SamplerLinear:
filter: MIN_MAG_MIP_LINEAR
addressU: CLAMP
addressV: CLAMP
type: RGBA8_uint
constant-buffers:
globalParams: {screenSize: SCREEN_SIZE, captureState: CAPTURE_STATE, tileUV: TILE_UV_RANGE}
controlBuf: {g_sldSharpen: sharpenSlider, g_sldClarity : claritySlider, g_sldHDR : hdrSlider, g_sldBloom : bloomSlider}
typical_slider1:
type: float
minimum-value: -1.0
maximum-value: 1.0
default-value: 0.0
ui-sticky-region: 0.01
ui-value-unit: "%"
ui-value-min: -100
ui-value-max: 100
ui-value-step: 5
typical_slider2:
type: float
minimum-value: 0.0
maximum-value: 1.0
default-value: 0.0
ui-sticky-region: 0.01
ui-value-unit: "%"
ui-value-min: 0
ui-value-max: 100
ui-value-step: 5
user-constants:
- name: sharpenSlider
ui-label: "Sharpen"
ui-label-localized:
ru-RU: "Резкость"
fr-FR: "Accentuation"
it-IT: "Messa a fuoco"
de-DE: "Schärfen"
es-ES: "Nitidez"
es-MX: "Enfoque"
zh-CHS: "锐化"
zh-CHT: "銳化"
ja-JP: "シャープにする"
cs-CZ: "Zaostřit"
da-DK: "Gør skarpere"
el-GR: "Αύξηση ευκρίνειας"
en-UK: "Sharpen"
fi-FI: "Terävöitä"
hu: "Élesítés"
ko-KR: "선명도"
nl-NL: "Scherper maken"
nb-NO: "Gjør skarpere"
pl: "Wyostrzanie"
pt-PT: "Nitidez"
pt-BR: "Nitidez"
sl-SI: "Ostrenje"
sk-SK: "Zostriť"
sv-SE: "Skärpa"
th-TH: "ทำให้คมชัดขึ้น"
tr-TR: "Keskinleştirme"
default-value: 0.0
ui-sticky-value: 0.0
import: [typical_slider2]
- name: claritySlider
ui-label: "Clarity"
ui-label-localized:
ru-RU: "Четкость"
fr-FR: "Clarté"
it-IT: "Nitidezza"
de-DE: "Klarheit"
es-ES: "Claridad"
es-MX: "Claridad"
zh-CHS: "清晰度"
zh-CHT: "清晰度"
ja-JP: "鮮明さ"
cs-CZ: "Čirost"
da-DK: "Klarhed"
el-GR: "Καθαρότητα"
en-UK: "Clarity"
fi-FI: "Selkeys"
hu: "Tisztaság"
ko-KR: "투명도"
nl-NL: "Helderheid"
nb-NO: "Klarhet"
pl: "Przejrzystość"
pt-PT: "Claridade"
pt-BR: "Claridade"
sl-SI: "Jasnost"
sk-SK: "Jasnosť"
sv-SE: "Klarhet"
th-TH: "ความชัดเจน"
tr-TR: "Netlik"
default-value: 0.0
ui-sticky-value: 0.0
import: [typical_slider1]
- name: hdrSlider
ui-label: "HDR Toning"
ui-label-localized:
ru-RU: "Тонирование HDR"
fr-FR: "Tonalité HDR"
it-IT: "HDR"
de-DE: "HDR-Toning"
es-ES: "Tono HDR"
es-MX: "Tono de HDR"
zh-CHS: "高动态范围 (HDR) 调色"
zh-CHT: "HDR 色調"
ja-JP: "HDRトーニング"
cs-CZ: "Tónování HDR"
da-DK: "HDR-toning"
el-GR: "Τόνος HDR"
en-UK: "HDR toning"
fi-FI: "HDR-sävytys"
hu: "HDR árnyalatok"
ko-KR: "HDR 조색"
nl-NL: "HDR-toning"
nb-NO: "HDR-toning"
pl: "Tonowanie HDR"
pt-PT: "Tonalidade HDR"
pt-BR: "Tonalização do HDR"
sl-SI: "Toniranje HDR"
sk-SK: "Tónovanie HDR"
sv-SE: "HDR-toning"
th-TH: "HDR โทนนิ่ง"
tr-TR: "HDR tonlama"
default-value: 0.0
ui-sticky-value: 0.0
import: [typical_slider1]
- name: bloomSlider
ui-label: "Bloom"
ui-label-localized:
ru-RU: "Размытие"
fr-FR: "Flou"
it-IT: "Patinatura"
de-DE: "Überstrahleffekte"
es-ES: "Bloom (resplandor)"
es-MX: "Brillo de iluminación"
zh-CHS: "全屏泛光"
zh-CHT: "光暈"
ja-JP: "ブルーム"
cs-CZ: "Záře"
da-DK: "Bloom"
el-GR: "Bloom"
en-UK: "Bloom"
fi-FI: "Bloom-tehoste"
hu: "Virág"
ko-KR: "블룸"
nl-NL: "Bloom-effect"
nb-NO: "Bloom"
pl: "Rozświetlenie"
pt-PT: "Brilho"
pt-BR: "Bloom"
sl-SI: "Žarenje"
sk-SK: "Žiara"
sv-SE: "Bloom"
th-TH: "บลูม"
tr-TR: "Bloom"
default-value: 0.0
ui-sticky-value: 0.0
import: [typical_slider2]
blur1:
import: [pass_template]
shader: PS_LargeBlur1@Details.yfx
textures:
texOriginalColor:
channel: PIPE_INPUTS_COLOR
main:
import: [pass_template]
shader: PS_SharpenClarity@Details.yfx
textures:
texBlurred:
import: [blur1]
texOriginalColor:
channel: PIPE_INPUTS_COLOR
Details.yfx
// Shader file for NVIDIA Ansel
//
//
// Constants
//
#define PixelSize float2(1.0 / screenSize.x, 1.0 / screenSize.y) //x = Pixel width, y = Pixel height
cbuffer globalParams
{
float2 screenSize; //x = screen width, y = screen height
int captureState; //unused, my math works without using specific cases
float4 tileUV; //xy - top left tile coordinate, zw - bottom right tile coordinate
}
cbuffer controlBuf
{
float g_sldSharpen;
float g_sldClarity;
float g_sldHDR;
float g_sldBloom;
}
struct VSOut
{
float4 position : SV_POSITION;
float2 txcoord : TEXCOORD;
};
//
// Textures, Samplers
//
Texture2D texOriginalColor;
Texture2D texBlurred;
//
SamplerState SamplerLinear;
//
// Functions
//
float4 ScaleableGaussianBlurLinear(Texture2D tex,
float2 texcoord,
int nSteps,
float2 axis,
float2 texelsize)
{
float norm = -1.35914091423/(nSteps*nSteps);
float4 accum = tex.Sample(SamplerLinear,texcoord.xy);
float2 offsetinc = axis * texelsize;
float divisor = 0.5; //exp(0) * 0.5
[loop]
for(float iStep = 1; iStep <= nSteps; iStep++)
{
float2 tapOffsetD = iStep * 2.0 + float2(-1.0,0.0);
float2 tapWeightD = exp(tapOffsetD*tapOffsetD*norm);
float tapWeightL = dot(tapWeightD,1.0);
float tapOffsetL = dot(tapOffsetD,tapWeightD)/tapWeightL;
accum += tex.SampleLevel(SamplerLinear,texcoord.xy + offsetinc * tapOffsetL,0) * tapWeightL;
accum += tex.SampleLevel(SamplerLinear,texcoord.xy - offsetinc * tapOffsetL,0) * tapWeightL;
divisor += tapWeightL;
}
accum /= 2.0 * divisor;
return accum;
}
float4 BoxBlur(Texture2D tex, float2 texcoord, float2 texelsize)
{
float3 blurData[8] =
{
float3( 0.5, 1.5,1.50),
float3( 1.5,-0.5,1.50),
float3(-0.5,-1.5,1.50),
float3(-1.5, 0.5,1.50),
float3( 2.5, 1.5,1.00),
float3( 1.5,-2.5,1.00),
float3(-2.5,-1.5,1.00),
float3(-1.5, 2.5,1.00),
};
float4 blur = 0.0;
for(int i=0; i<8; i++) { blur += tex.SampleLevel(SamplerLinear,texcoord.xy + blurData.xy * texelsize.xy,0) * blurData.z; }
blur /= (4 * 1.5) + (4 * 1.0);
return blur;
}
//
// Pixel Shaders
//
float4 PS_LargeBlur1( VSOut IN ): SV_Target
{
return ScaleableGaussianBlurLinear(texOriginalColor,IN.txcoord.xy,15,float2(1,0),PixelSize.xy);
}
//
float4 PS_SharpenClarity( VSOut IN ): SV_Target
{
float4 color = texOriginalColor.Sample(SamplerLinear,IN.txcoord.xy);
float4 largeblur = ScaleableGaussianBlurLinear(texBlurred,IN.txcoord.xy,15,float2(0,1),PixelSize.xy);
float4 smallblur = BoxBlur(texOriginalColor,IN.txcoord.xy,PixelSize);
float a = dot(color.rgb,float3(0.299,0.587,0.114));
float sqrta = sqrt(a);
float b = dot(largeblur.rgb,float3(0.299,0.587,0.114));
float c = dot(smallblur.rgb,float3(0.299,0.587,0.114));
//HDR Toning
float HDRToning = sqrta * lerp(sqrta*(2*a*b-a-2*b+2.0), (2*sqrta*b-2*b+1), b > 0.5); //modified soft light v1
color = color / (a+1e-6) * lerp(a,HDRToning,g_sldHDR);
//sharpen
//float Sharpen = (a-c)/(g_sldSharpen*2.0+1e-6); //clamp to +- 1.0 / SHARPEN_AMOUNT with smooth falloff
//Sharpen = sign(Sharpen)*(pow(Sharpen,6)-abs(Sharpen))/(pow(Sharpen,6)-1);
//color += Sharpen*color*g_sldSharpen*2.0;
float Sharpen = dot(color.rgb - smallblur.rgb,float3(0.299,0.587,0.114));
float sharplimit = lerp(0.25,0.6,g_sldSharpen);
Sharpen = clamp(Sharpen,-sharplimit,sharplimit);
color.rgb = color.rgb / a * lerp(a,a+Sharpen,g_sldSharpen);
//clarity
float Clarity = (0.5 + a - b);
Clarity = lerp(2*Clarity + a*(1-2*Clarity), 2*(1-Clarity)+(2*Clarity-1)*rsqrt(a), a > b); //modified soft light v2
color.rgb *= lerp(1.0,Clarity,g_sldClarity);
//bloom
color.rgb = 1-(1-color.rgb)*(1-largeblur.rgb * g_sldBloom);
return color;
}
Please Log in or Create an account to join the conversation.
- sanek7814
-
Topic Author
Please Log in or Create an account to join the conversation.
- odikzz2
-
Please Log in or Create an account to join the conversation.
- b1es
-
HDR Toning is really good
Please Log in or Create an account to join the conversation.
- odikzz2
-
Please Log in or Create an account to join the conversation.