Dogway's grading shader (slang)

Scroll up until you find a big box that says GitHub.

It’s his GitHub the things you seek are there, lol.

Sorry I just woke up. So I think I got jokes.

Edit: it’s post 21 here.

1 Like

got it, thanks!

Next question: How do I get these shaders to play nice with guest-dr-venom? Grade seems to break the entire chain when added as the first pass.

1 Like

Uhhh, I did ghetto things for this I just replaced d65-d50 with grade in the preset, as grade is handling white point anyway. (Seems to be working fine on my end.) Make sure to keep the alias# = WhitePointPass thing.

I don’t really see that there’d be an issue with how I’m doing it as d65-d50 only has two jobs in the chain off the top of my head. The first obviously being white point/temperature. The second being setting up avg_lum for the raster bloom (which is still working for me.)

2 Likes

Here I keep my shaders, mods and whatnot, and this is the thread EDIT: we are in the thread lol. The shader is pretty much finished, I recall wanting to refine the hotspot fix but got busy with other things. A lot of code in grade is not mine so it’s not up to me to include in official repo, although it would be very welcome.

2 Likes

That hotspot fix would be cool, as we could get rid of a setting potentially. Your reply cracked me up.

This really does need to be in the repo though.

2 Likes

Still no luck. Mind posting your preset?

Do I need to change filter or scale with any passes?

The shader itself loads fine but it breaks when I add any passes after it.

1 Like

Are you trying to load each pass individually into RetroArch one at a time? Hang on are you loading up the preset in RA and then changing the pass?

I’ll have to just make you a preset, as mine has more going on then you want, are you going to use any of guests color passes (I’m assuming not)?

Also just realized in the preset you need to add referencing for the luts lol.

I tried changing the passes within RA and tried editing the preset file, no luck.

I probably won’t use any of the guest color passes with this.

1 Like

Hey I did the things. Just tested it. Also make sure guest-dr-venom is up-to-date please. As I don’t know if it’ll break things if its not.

You need to use this copy of grade.slang and put in the guest folder. The only difference I made to this copy of grade is I renamed the gamma things in the shader so it would run. I didn’t alter how the code works at all.

#version 450

layout(push_constant) uniform Push
{
    float g_gamma_out;
    float g_gamma_in;
    float gamma_type;
    float g_vignette;
    float g_vstr;
    float g_vpower;
    float g_csize;
    float g_bsize;
    float wp_temperature;
    float g_sat;
    float g_vibr;
    float g_hpfix;
    float g_lum;
    float g_cntrst;
    float g_mid;
    float g_lift;
    float blr;
    float blg;
    float blb;
    float wlr;
    float wlg;
    float wlb;
    float rg;
    float rb;
    float gr;
    float gb;
    float br;
    float bg;
    float LUT_Size1;
    float LUT1_toggle;
    float LUT_Size2;
    float LUT2_toggle;
} params;

/*
   Grade
   > Ubershader grouping some color related monolithic shaders like color-mangler, vignette, lut_x2, white_point,
   > and the addition of vibrance, black level, corner size, rolled gain, sigmoidal contrast and proper gamma transforms.

   Author: hunterk, Guest, Dr. Venom, Dogway
   License: Public domain
*/

#pragma parameter g_gamma_out "LCD Gamma" 2.20 0.0 3.0 0.05
#pragma parameter g_gamma_in "CRT Gamma" 2.40 0.0 3.0 0.05
#pragma parameter gamma_type "CRT Gamma (POW = 0, sRGB = 1)" 1.0 0.0 1.0 1.0
#pragma parameter g_vignette "Vignette Toggle" 1.0 0.0 1.0 1.0
#pragma parameter g_vstr "Vignette Strength" 40.0 0.0 50.0 1.0
#pragma parameter g_vpower "Vignette Power" 0.20 0.0 0.5 0.01
#pragma parameter g_csize "Corner size" 0.0 0.0 0.07 0.01
#pragma parameter g_bsize "Border smoothness" 600.0 100.0 600.0 25.0
#pragma parameter wp_temperature "White Point" 9311.0 1031.0 12047.0 72.0
#pragma parameter g_sat "Saturation" 0.0 -1.0 2.0 0.02
#pragma parameter g_vibr "Dullness/Vibrance" 0.0 -1.0 1.0 0.05
#pragma parameter g_hpfix "Hotspot Fix" 0.0 0.0 1.0 1.00
#pragma parameter g_lum "Brightness" 0.0 -0.5 1.0 0.01
#pragma parameter g_cntrst "Contrast" 0.0 -1.0 1.0 0.05
#pragma parameter g_mid "Contrast Pivot" 0.5 0.0 1.0 0.01
#pragma parameter g_lift "Black Level" 0.0 -0.5 0.5 0.01
#pragma parameter blr "Black-Red Tint" 0.0 0.0 1.0 0.01
#pragma parameter blg "Black-Green Tint" 0.0 0.0 1.0 0.01
#pragma parameter blb "Black-Blue Tint" 0.0 0.0 1.0 0.01
#pragma parameter wlr "White-Red Tint" 1.0 0.0 2.0 0.01
#pragma parameter wlg "White-Green Tint" 1.0 0.0 2.0 0.01
#pragma parameter wlb "White-Blue Tint" 1.0 0.0 2.0 0.01
#pragma parameter rg "Red-Green Tint" 0.0 -1.0 1.0 0.005
#pragma parameter rb "Red-Blue Tint" 0.0 -1.0 1.0 0.005
#pragma parameter gr "Green-Red Tint" 0.0 -1.0 1.0 0.005
#pragma parameter gb "Green-Blue Tint" 0.0 -1.0 1.0 0.005
#pragma parameter br "Blue-Red Tint" 0.0 -1.0 1.0 0.005
#pragma parameter bg "Blue-Green Tint" 0.0 -1.0 1.0 0.005
#pragma parameter LUT_Size1 "LUT Size 1" 16.0 8.0 64.0 16.0
#pragma parameter LUT1_toggle "LUT 1 Toggle" 0.0 0.0 1.0 1.0
#pragma parameter LUT_Size2 "LUT Size 2" 64.0 0.0 64.0 16.0
#pragma parameter LUT2_toggle "LUT 2 Toggle" 0.0 0.0 1.0 1.0

layout(std140, set = 0, binding = 0) uniform UBO
{
    mat4 MVP;
    vec4 SourceSize;
    vec4 OriginalSize;
    vec4 OutputSize;
} global;

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;

void main()
{
    gl_Position = global.MVP * Position;
    vTexCoord = TexCoord;
}

#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
layout(set = 0, binding = 3) uniform sampler2D SamplerLUT1;
layout(set = 0, binding = 4) uniform sampler2D SamplerLUT2;


// White Point Mapping function
//
// From the first comment post (sRGB primaries and linear light compensated)
//      http://www.zombieprototypes.com/?p=210#comment-4695029660
// Based on the Neil Bartlett's blog update
//      http://www.zombieprototypes.com/?p=210
// Inspired itself by Tanner Helland's work
//      http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/

vec3 wp_adjust(vec3 color){

    float temp = params.wp_temperature / 100.;
    float k = params.wp_temperature / 10000.;
    float lk = log(k);

    vec3 wp = vec3(1.);

    // calculate RED
    wp.r = (temp <= 65.) ? 1. : 0.32068362618584273 + (0.19668730877673762 * pow(k - 0.21298613432655075, - 1.5139012907556737)) + (- 0.013883432789258415 * lk);

    // calculate GREEN
    float mg = 1.226916242502167 + (- 1.3109482654223614 * pow(k - 0.44267061967913873, 3.) * exp(- 5.089297600846147 * (k - 0.44267061967913873))) + (0.6453936305542096 * lk);
    float pg = 0.4860175851734596 + (0.1802139719519286 * pow(k - 0.14573069517701578, - 1.397716496795082)) + (- 0.00803698899233844 * lk);
    wp.g = (temp <= 65.5) ? ((temp <= 8.) ? 0. : mg) : pg;

    // calculate BLUE
    wp.b = (temp <= 19.) ? 0. : (temp >= 66.) ? 1. : 1.677499032830161 + (- 0.02313594016938082 * pow(k - 1.1367244820333684, 3.) * exp(- 4.221279555918655 * (k - 1.1367244820333684))) + (1.6550275798913296 * lk);

    // clamp
    wp.rgb = clamp(wp.rgb, vec3(0.), vec3(1.));

    // Linear color input
    return color * wp;
}

vec3 sRGB_to_XYZ(vec3 RGB){

    const mat3x3 m = mat3x3(
    0.4124564, 0.3575761, 0.1804375,
    0.2126729, 0.7151522, 0.0721750,
    0.0193339, 0.1191920, 0.9503041);
    return RGB * m;
}


vec3 XYZtoYxy(vec3 XYZ){

    float XYZrgb = XYZ.r+XYZ.g+XYZ.b;
    float Yxyg = (XYZrgb <= 0.0) ? 0.3805 : XYZ.r / XYZrgb;
    float Yxyb = (XYZrgb <= 0.0) ? 0.3769 : XYZ.g / XYZrgb;
    return vec3(XYZ.g, Yxyg, Yxyb);
}


vec3 XYZ_to_sRGB(vec3 XYZ){

    const mat3x3 m = mat3x3(
    3.2404542, -1.5371385, -0.4985314,
   -0.9692660,  1.8760108,  0.0415560,
    0.0556434, -0.2040259,  1.0572252);
    return XYZ * m;
}


vec3 YxytoXYZ(vec3 Yxy){

    float Xs = Yxy.r * (Yxy.g/Yxy.b);
    float Xsz = (Yxy.r <= 0.0) ? 0.0 : 1.0;
    vec3 XYZ = vec3(Xsz,Xsz,Xsz) * vec3(Xs, Yxy.r, (Xs/Yxy.g)-Xs-Yxy.r);
    return XYZ;
}

//  This shouldn't be necessary but it seems some undefined values can
//  creep in and each GPU vendor handles that differently. This keeps
//  all values within a safe range
vec3 mixfix(vec3 a, vec3 b, float c)
{
    return (a.z < 1.0) ? mix(a, b, c) : a;
}


vec4 mixfix_v4(vec4 a, vec4 b, float c)
{
    return (a.z < 1.0) ? mix(a, b, c) : a;
}


float SatMask(float color_r, float color_g, float color_b)
{
    float max_rgb = max(color_r, max(color_g, color_b));
    float min_rgb = min(color_r, min(color_g, color_b));
    float msk = clamp((max_rgb - min_rgb) / (max_rgb + min_rgb), 0.0, 1.0);
    return msk;
}


float moncurve_f( float color, float gamma, float offs)
{
    // Forward monitor curve
    color = clamp(color, 0.0, 1.0);
    float fs = (( gamma - 1.0) / offs) * pow( offs * gamma / ( ( gamma - 1.0) * ( 1.0 + offs)), gamma);
    float xb = offs / ( gamma - 1.0);

    color = ( color > xb) ? pow( ( color + offs) / ( 1.0 + offs), gamma) : color * fs;
    return color;
}


vec3 moncurve_f_f3( vec3 color, float gamma, float offs)
{
    color.r = moncurve_f( color.r, gamma, offs);
    color.g = moncurve_f( color.g, gamma, offs);
    color.b = moncurve_f( color.b, gamma, offs);
    return color.rgb;
}


float moncurve_r( float color, float gamma, float offs)
{
    // Reverse monitor curve
    color = clamp(color, 0.0, 1.0);
    float yb = pow( offs * gamma / ( ( gamma - 1.0) * ( 1.0 + offs)), gamma);
    float rs = pow( ( gamma - 1.0) / offs, gamma - 1.0) * pow( ( 1.0 + offs) / gamma, gamma);

    color = ( color > yb) ? ( 1.0 + offs) * pow( color, 1.0 / gamma) - offs : color * rs;
    return color;
}


vec3 moncurve_r_f3( vec3 color, float gamma, float offs)
{
    color.r = moncurve_r( color.r, gamma, offs);
    color.g = moncurve_r( color.g, gamma, offs);
    color.b = moncurve_r( color.b, gamma, offs);
    return color.rgb;
}


//  Performs better in gamma encoded space
float contrast_sigmoid(float color, float cont, float pivot){

    cont = pow(cont + 1., 3.);

    float knee = 1. / (1. + exp(cont * pivot));
    float shldr = 1. / (1. + exp(cont * (pivot - 1.)));

    color = (1. / (1. + exp(cont * (pivot - color))) - knee) / (shldr - knee);

    return color;
}


//  Performs better in gamma encoded space
float contrast_sigmoid_inv(float color, float cont, float pivot){

    cont = pow(cont - 1., 3.);

    float knee = 1. / (1. + exp (cont * pivot));
    float shldr = 1. / (1. + exp (cont * (pivot - 1.)));

    color = pivot - log(1. / (color * (shldr - knee) + knee) - 1.) / cont;

    return color;
}


float rolled_gain(float color, float gain){

    float gx = gain + 1.0;
    float ax = (max(0.5 - (gx / 2.0), 0.5));
    float cx = (gx > 0.0) ? (1.0 - gx + (gx / 2.0)) : abs(gx) / 2.0;

    float gain_plus = ((color * gx) > ax) ? (ax + cx * tanh((color * gx - ax) / cx)) : (color * gx);
    float ax_g = 1.0 - abs(gx);
    float gain_minus = (color > ax_g) ? (ax_g + cx * tanh((color - ax_g) / cx)) : color;
    color = (gx > 0.0) ? gain_plus : gain_minus;

    return color;
}

vec4 rolled_gain_v4(vec4 color, float gain){

    color.r = rolled_gain(color.r, gain);
    color.g = rolled_gain(color.g, gain);
    color.b = rolled_gain(color.b, gain);

    return vec4(color.rgb, 1.0);
}


//  Borrowed from cgwg's crt-geom, under GPL
float corner(vec2 coord)
{
    coord *= global.OriginalSize.xy / global.SourceSize.xy;
    coord = (coord - vec2(0.5)) * 1.0 + vec2(0.5);
    coord = min(coord, vec2(1.0)-coord) * vec2(1.0, global.OutputSize.y/global.OutputSize.x);
    vec2 cdist = vec2(max(params.g_csize, max((1.0-smoothstep(100.0,600.0,params.g_bsize))*0.01,0.002)));
    coord = (cdist - min(coord,cdist));
    float dist = sqrt(dot(coord,coord));
    return clamp((cdist.x-dist)*params.g_bsize,0.0, 1.0);
}


void main()
{

//  Pure power was crushing blacks (eg. DKC2). You can mimic pow(c, 2.40) by raising the gamma_in value to 2.55
    vec3 imgColor = texture(Source, vTexCoord.xy).rgb;
    imgColor = (params.gamma_type == 1.0) ? moncurve_f_f3(imgColor, params.g_gamma_in + 0.15, 0.055) : pow(imgColor, vec3(params.g_gamma_in));


//  Look LUT
    float red = (imgColor.r * (params.LUT_Size1 - 1.0) + 0.4999) / (params.LUT_Size1 * params.LUT_Size1);
    float green = (imgColor.g * (params.LUT_Size1 - 1.0) + 0.4999) / params.LUT_Size1;
    float blue1 = (floor(imgColor.b * (params.LUT_Size1 - 1.0)) / params.LUT_Size1) + red;
    float blue2 = (ceil(imgColor.b * (params.LUT_Size1 - 1.0)) / params.LUT_Size1) + red;
    float mixer = clamp(max((imgColor.b - blue1) / (blue2 - blue1), 0.0), 0.0, 32.0);
    vec3 color1 = texture(SamplerLUT1, vec2(blue1, green)).rgb;
    vec3 color2 = texture(SamplerLUT1, vec2(blue2, green)).rgb;
    vec3 vcolor = (params.LUT1_toggle == 0.0) ? imgColor : mixfix(color1, color2, mixer);


//  Saturation agnostic sigmoidal contrast
    vec3 Yxy = XYZtoYxy(sRGB_to_XYZ(vcolor));
    float toGamma = clamp(moncurve_r(Yxy.r, 2.40, 0.055), 0.0, 1.0);
    toGamma = (params.g_hpfix == 0.0) ? toGamma : ((Yxy.r > 0.5) ? contrast_sigmoid_inv(toGamma, 2.3, 0.5) : toGamma);
    float sigmoid = (params.g_cntrst > 0.0) ? contrast_sigmoid(toGamma, params.g_cntrst, params.g_mid) : contrast_sigmoid_inv(toGamma, params.g_cntrst, params.g_mid);
    vec3 contrast = vec3(moncurve_f(sigmoid, 2.40, 0.055), Yxy.g, Yxy.b);
    vec3 XYZsrgb = clamp(XYZ_to_sRGB(YxytoXYZ(contrast)), 0.0, 1.0);
    contrast = (params.g_cntrst == 0.0) && (params.g_hpfix == 0.0) ? vcolor : ((params.g_cntrst != 0.0) || (params.g_hpfix != 0.0) ? XYZsrgb : vcolor);


//  Vignetting & Black Level
    vec2 vpos = vTexCoord*(global.OriginalSize.xy/global.SourceSize.xy);

    vpos *= 1.0 - vpos.xy;
    float vig = vpos.x * vpos.y * params.g_vstr;
    vig = min(pow(vig, params.g_vpower), 1.0);
    contrast *= (params.g_vignette == 1.0) ? vig : 1.0;

    contrast += (params.g_lift / 20.0) * (1.0 - contrast);


//  RGB related transforms
    vec4 screen = vec4(max(contrast, 0.0), 1.0);
    float sat = params.g_sat + 1.0;

                   //  r               g           b  alpha ; alpha does nothing for our purposes
    mat4 color = mat4(params.wlr, params.rg,  params.rb,   0.0,  //red tint
                      params.gr,  params.wlg, params.gb,   0.0,  //green tint
                      params.br,  params.bg,  params.wlb,  0.0,  //blue tint
                      params.blr/20., params.blg/20., params.blb/20., 0.0);  //black tint

    mat4 adjust = mat4((1.0 - sat) * 0.2126 + sat, (1.0 - sat) * 0.2126, (1.0 - sat) * 0.2126, 1.0,
                       (1.0 - sat) * 0.7152, (1.0 - sat) * 0.7152 + sat, (1.0 - sat) * 0.7152, 1.0,
                       (1.0 - sat) * 0.0722, (1.0 - sat) * 0.0722, (1.0 - sat) * 0.0722 + sat, 1.0,
                        0.0, 0.0, 0.0, 1.0);

    screen = clamp(rolled_gain_v4(screen, params.g_lum * 2.0), 0.0, 1.0);
    screen = color * screen;
    float sat_msk = (params.g_vibr > 0.0) ? clamp(1.0 - (SatMask(screen.r, screen.g, screen.b) * params.g_vibr), 0.0, 1.0) : clamp(1.0 - abs(SatMask(screen.r, screen.g, screen.b) - 1.0) * abs(params.g_vibr), 0.0, 1.0);
    screen = mixfix_v4(screen, adjust * screen, sat_msk);


//  Color Temperature
    vec3 adjusted = wp_adjust(screen.rgb);
    vec3 base_luma = XYZtoYxy(sRGB_to_XYZ(screen.rgb));
    vec3 adjusted_luma = XYZtoYxy(sRGB_to_XYZ(adjusted));
    adjusted = adjusted_luma + (vec3(base_luma.r, 0.0, 0.0) - vec3(adjusted_luma.r, 0.0, 0.0));
    adjusted = clamp(XYZ_to_sRGB(YxytoXYZ(adjusted)), 0.0, 1.0);


//  Technical LUT
    float red_2 = (adjusted.r * (params.LUT_Size2 - 1.0) + 0.4999) / (params.LUT_Size2 * params.LUT_Size2);
    float green_2 = (adjusted.g * (params.LUT_Size2 - 1.0) + 0.4999) / params.LUT_Size2;
    float blue1_2 = (floor(adjusted.b * (params.LUT_Size2 - 1.0)) / params.LUT_Size2) + red_2;
    float blue2_2 = (ceil(adjusted.b * (params.LUT_Size2 - 1.0)) / params.LUT_Size2) + red_2;
    float mixer_2 = clamp(max((adjusted.b - blue1_2) / (blue2_2 - blue1_2), 0.0), 0.0, 32.0);
    vec3 color1_2 = texture(SamplerLUT2, vec2(blue1_2, green_2)).rgb;
    vec3 color2_2 = texture(SamplerLUT2, vec2(blue2_2, green_2)).rgb;
    vec3 LUT2_output = mixfix(color1_2, color2_2, mixer_2);

    LUT2_output = (params.LUT2_toggle == 0.0) ? adjusted : LUT2_output;
    LUT2_output = (params.g_gamma_out == 1.00) ? LUT2_output : moncurve_r_f3(LUT2_output, params.g_gamma_out + 0.20, 0.055);

    vpos *= (global.SourceSize.xy/global.OriginalSize.xy);
    FragColor = vec4(LUT2_output*corner(vpos), 1.0);
}

Then you need to place this preset with the rest of the crt presets are, it should be sitting by the standard guest preset. I removed the color_profile pass, the LUT pass, and replaced the d65-d50 pass with grade.

shaders = 8

textures = "SamplerLUT1;SamplerLUT2"
SamplerLUT1 = shaders/guest/lut/sony_trinitron1.png
SamplerLUT1_linear = true 
SamplerLUT2 = shaders/guest/lut/sony_trinitron2.png
SamplerLUT2_linear = true 

shader0 = shaders/guest/grade.slang
filter_linear0 = true
scale_type0 = source
scale0 = 1.0
alias0 = WhitePointPass

shader1 = shaders/guest/afterglow.slang
filter_linear1 = true
scale_type1 = source
scale1 = 1.0
alias1 = AfterglowPass

shader2 = shaders/guest/avg-lum.slang
filter_linear2 = true
scale_type2 = source
scale2 = 1.0
mipmap_input2 = true
float_framebuffer2 = true
alias2 = AvgLumPass

shader3 = shaders/guest/linearize.slang
filter_linear3 = true
scale_type3 = source
scale3 = 1.0
float_framebuffer3 = true
alias3 = LinearizePass

shader4 = shaders/guest/blur_horiz.slang
filter_linear4 = true
scale_type4 = source
scale4 = 1.0
float_framebuffer4 = true

shader5 = shaders/guest/blur_vert.slang
filter_linear5 = true
scale_type5 = source
scale5 = 1.0
float_framebuffer5 = true
alias5 = GlowPass

shader6 = shaders/guest/linearize_scanlines.slang
filter_linear6 = true
scale_type6 = source
scale6 = 1.0
float_framebuffer6 = true

shader7 = shaders/guest/crt-guest-dr-venom.slang
filter_linear7 = true
scale_type7 = viewport
scale_x7 = 1.0
scale_y7 = 1.0

I just set it to use the luts from the guest folder. So you’ll have to swap those out if you’re wanting the same ones that dogway is using.

2 Likes

@Nesguy

Let me know how this works for you.

You may need to adjust the LUT size settings in the shader, as I left them at default.

Works great, thanks!

@dogway, @Syh

So now that there are two gamma adjustments I’m a little confused. I want to use the gamma adjustment in grade, so how do I prevent the gamma adjustment in guest-dr-venom from messing with this?

1 Like

As long as you leave guests gamma stock it should be fine to use grades gamma adjustment for things.

As the gamma in guest is just setting lcd to crt gamma, basically giving you a crt color space. That’s my understanding anyway. If I’m wrong I hope someone corrects me. @Dogway @hunterk @guest.r

1 Like

grade outputs a gamma of 2.2, so anything further let it decode gamma with a value of 2.2 and re-encode with the same value to yield a no-op gamma wise. In my presets I keep crt-royale gamma values “crt_gamma” and “lcd_gamma” both to 2.2.

2 Likes

Just wanted to say that this shader is really great. I was able to solve a lot of issues I was having with color balance, saturation, etc. with my guest-dr-venom presets and improve the dynamic range of the image by a significant amount. This also increases the potential beam width variation by quite a lot.

I don’t quite understand “contrast pivot,” though. What exactly does that do?

2 Likes

Go up and look at post 30 something that has some graphs. He doesn’t quite explain it but my understanding is that you’re choosing where the contrast will do it’s (curve?) pivot, basically you’re picking the point for the middle of the s-curve it has going on. With it’s default in the center (I’m assuming) as it’s 0.50 out of a 0.0 - 1.0 scale.

@Dogway please correct me if I’m wrong about this. This is just how I’m understanding it from the explanation about the contrast and the graph provided.

@Nesguy I hope this helps you out some.

2 Likes

Yes, that’s it. If the pivot is at 0 and you ‘increase’ “contrast” you will be actually adding brightness from the center of the curve (or image dynamic range) instead of from the top as “brightness” does. It’s a bias for the S-curve to have more influence on one side or the other.

One thing to note, since it’s applied on gamma encoded material, moving the pivot while contrast is 0.0 will change brightness a bit.

2 Likes

So far I’ve just applied the white point, gamma and contrast pivot. I’m maxing out my display backlight, so gamma input had to be adjusted to 2.85 to get the black level and TV color levels looking right. After that, contrast pivot had to come up to 0.80 to eliminate clipping.

Hotspot fix seems to smooth out gradients toward the high end; without it there can be an abrupt jump between brighter color values with some configs. So that’s cool.

I’m still getting some clipping on the very low end, mainly with green. Probably something that could be fixed with a bit of offset; need to play around some more.

1 Like

So, adjusting offset on my TV didn’t do anything, so the clipping with dark colors is a software/math thing and not a hardware thing. Raising black level to 0.01 seems to fix it! Should this be the default setting? I’m guessing this is affected by gamma, though.

Difference is most noticeable with green.

Here’s black level at 0.00:

Here’s black level at 0.01:

1 Like

Post your settings, I guess you are using sRGB mode gamma for CRT right? That would be the linear part I mentioned above.

Yes, sRGB gamma mode.

Changes from defaults:

CRT gamma: 2.85 Black level: 0.01 Contrast pivot: 0.80

This is with the LCD backlight at 100%.

shaders = "8"
shader0 = "shaders_slang/crt/shaders/guest/grade.slang"
filter_linear0 = "true"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = "WhitePointPass"
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
scale_type_x0 = "source"
scale_x0 = "1.000000"
scale_type_y0 = "source"
scale_y0 = "1.000000"
shader1 = "shaders_slang/crt/shaders/guest/afterglow.slang"
filter_linear1 = "true"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = "AfterglowPass"
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
scale_type_x1 = "source"
scale_x1 = "1.000000"
scale_type_y1 = "source"
scale_y1 = "1.000000"
shader2 = "shaders_slang/crt/shaders/guest/avg-lum.slang"
filter_linear2 = "true"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "true"
alias2 = "AvgLumPass"
float_framebuffer2 = "true"
srgb_framebuffer2 = "false"
scale_type_x2 = "source"
scale_x2 = "1.000000"
scale_type_y2 = "source"
scale_y2 = "1.000000"
shader3 = "shaders_slang/crt/shaders/guest/linearize.slang"
filter_linear3 = "true"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
alias3 = "LinearizePass"
float_framebuffer3 = "true"
srgb_framebuffer3 = "false"
scale_type_x3 = "source"
scale_x3 = "1.000000"
scale_type_y3 = "source"
scale_y3 = "1.000000"
shader4 = "shaders_slang/crt/shaders/guest/blur_horiz.slang"
filter_linear4 = "true"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "false"
alias4 = ""
float_framebuffer4 = "true"
srgb_framebuffer4 = "false"
scale_type_x4 = "source"
scale_x4 = "1.000000"
scale_type_y4 = "source"
scale_y4 = "1.000000"
shader5 = "shaders_slang/crt/shaders/guest/blur_vert.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = "GlowPass"
float_framebuffer5 = "true"
srgb_framebuffer5 = "false"
scale_type_x5 = "source"
scale_x5 = "1.000000"
scale_type_y5 = "source"
scale_y5 = "1.000000"
shader6 = "shaders_slang/crt/shaders/guest/linearize_scanlines.slang"
filter_linear6 = "true"
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
alias6 = ""
float_framebuffer6 = "true"
srgb_framebuffer6 = "false"
scale_type_x6 = "source"
scale_x6 = "1.000000"
scale_type_y6 = "source"
scale_y6 = "1.000000"
shader7 = "shaders_slang/crt/shaders/guest/crt-guest-dr-venom.slang"
filter_linear7 = "true"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
alias7 = ""
float_framebuffer7 = "false"
srgb_framebuffer7 = "false"
scale_type_x7 = "viewport"
scale_x7 = "1.000000"
scale_type_y7 = "viewport"
scale_y7 = "1.000000"
parameters = "g_gamma_out;g_gamma_in;gamma_type;g_vignette;g_vstr;g_vpower;g_csize;g_bsize;wp_temperature;g_sat;g_vibr;g_hpfix;g_lum;g_cntrst;g_mid;g_lift;blr;blg;blb;wlr;wlg;wlb;rg;rb;gr;gb;br;bg;LUT_Size1;LUT1_toggle;LUT_Size2;LUT2_toggle;SW;AR;PR;AG;PG;AB;PB;sat;lsmooth;GAMMA_INPUT;TAPSH;GLOW_FALLOFF_H;TAPSV;GLOW_FALLOFF_V;TATE;IOS;OS;BLOOM;brightboost;brightboost1;gsl;scanline1;scanline2;beam_min;beam_max;beam_size;h_sharp;s_sharp;csize;bsize;warpX;warpY;glow;shadowMask;masksize;vertmask;slotmask;slotwidth;double_slot;slotms;mcut;maskDark;maskLight;CGWG;gamma_out;spike;inter;interm;bloom;scans"
g_gamma_out = "2.200000"
g_gamma_in = "2.850000"
gamma_type = "1.000000"
g_vignette = "0.000000"
g_vstr = "40.000000"
g_vpower = "0.200000"
g_csize = "0.000000"
g_bsize = "600.000000"
wp_temperature = "9311.000000"
g_sat = "0.000000"
g_vibr = "-0.000000"
g_hpfix = "0.000000"
g_lum = "-0.000000"
g_cntrst = "0.000000"
g_mid = "0.800000"
g_lift = "0.010000"
blr = "0.000000"
blg = "0.000000"
blb = "0.000000"
wlr = "1.000000"
wlg = "1.000000"
wlb = "1.000000"
rg = "0.000000"
rb = "-0.050000"
gr = "0.005000"
gb = "0.000000"
br = "0.000000"
bg = "0.000000"
LUT_Size1 = "16.000000"
LUT1_toggle = "0.000000"
LUT_Size2 = "64.000000"
LUT2_toggle = "0.000000"
SW = "1.000000"
AR = "0.070000"
PR = "0.050000"
AG = "0.070000"
PG = "0.050000"
AB = "0.070000"
PB = "0.050000"
sat = "0.100000"
lsmooth = "0.900000"
GAMMA_INPUT = "2.200000"
TAPSH = "4.000000"
GLOW_FALLOFF_H = "0.000000"
TAPSV = "4.000000"
GLOW_FALLOFF_V = "0.000000"
TATE = "0.000000"
IOS = "0.000000"
OS = "1.000000"
BLOOM = "0.000000"
brightboost = "1.000000"
brightboost1 = "1.000000"
gsl = "2.000000"
scanline1 = "15.000000"
scanline2 = "23.000000"
beam_min = "1.000000"
beam_max = "0.700000"
beam_size = "0.000000"
h_sharp = "3.500000"
s_sharp = "0.000000"
csize = "0.000000"
bsize = "600.000000"
warpX = "0.000000"
warpY = "0.000000"
glow = "0.000000"
shadowMask = "-1.000000"
masksize = "1.000000"
vertmask = "0.000000"
slotmask = "0.000000"
slotwidth = "2.000000"
double_slot = "1.000000"
slotms = "1.000000"
mcut = "0.250000"
maskDark = "0.500000"
maskLight = "1.500000"
CGWG = "0.300000"
gamma_out = "2.200000"
spike = "1.000000"
inter = "400.000000"
interm = "1.000000"
bloom = "0.000000"
scans = "0.000000"
textures = "SamplerLUT1;SamplerLUT2"
SamplerLUT1 = "shaders_slang/crt/shaders/guest/lut/sony_trinitron1.png"
SamplerLUT1_linear = "true"
SamplerLUT1_wrap_mode = "clamp_to_border"
SamplerLUT1_mipmap = "false"
SamplerLUT2 = "shaders_slang/crt/shaders/guest/lut/sony_trinitron2.png"
SamplerLUT2_linear = "true"
SamplerLUT2_wrap_mode = "clamp_to_border"
SamplerLUT2_mipmap = "false"