Dogway's grading shader (slang)

With the default CRT black level of 0.1 you get a gamma of 2.4, which is the standard for Rec709/Rec2020 and the 1886 transfer function for dim surrounds, except if you enable the dim to dark option which takes it to 2.45 gamma for the mentioned spaces.

So now you control the system emulated gamma with the CRT black level option. 0.1 being 2.4, 0.0 being 2.6 and varying in between. I can make a function fit to remap the values so the effective gamma can be shown in the OSD for adjustment purposes (check at the bottom).

One thing I didn’t mention is that phosphor dynamics apply their own gamma if they are non-linear. Here is a summary.

So for example 2.4 converts to 2.75 with a phosphor gamma of 1/0.88. To get an effective gamma of 2.4 you would need to set the CRT gamma to 2.1 but you can’t reach that with CRT black level limit of 0.35. Only when phosphor dynamics are 1/0.9 you can set CRT black level to 0.35 and match the gamma of 2.55=2.3^(1/0.9)

(EDIT: or leave black level at 0.1 and raise brightness to 30 as per the below expression)

Scanline shaders should notify the gamma change when phosphor dynamics are applied, so you can make more sense of the whole system gamma.

To note “CRT black level” won’t make a big change on the black lift/pedestal, it’s mainly for calculating gamma, to actually raise the pedestal you use CRT brightness, but obviously any change in range will modify the gamma which is assumed.

Here is the expression for the effective gamma:

bl=0.1   # Black Level
br=0     # CRT Brightness
bl_gamma = 1.0/(-0.054348*pow(exp(bl)    ,-9.45461)+0.437886)
br_gamma = 1.0/( 2.057430*pow(exp(br/10.),0.028201)-1.060440)
gamma    = pow(bl_gamma,br_gamma)

Then you have to account for the phosphor dynamics gamma on top.

sys_gamma = pow(gamma,ph_gamma)

2 Likes

So in layman’s terms would it be possible to setup the new shader in a way that mimics the incumbent ones or provides a toggle to enable the old behavior so as to more or less act as a passthrough to allow existing Mega Bezel Reflection Shader presets to look the same as before in order to smooth/ease the transition?

This might be necessary for the newer implementation to not break any existing custom fine tuned black level, colour temperature or gamma adjustments which might have been made prior to these developments.

Other than that if integrated in the current form, the effects on the existing ecosystems of presets might be a bit unpredictable, as in might be better in some instances (best case) but might also be worse in other instances (worst case) and require a retuning/calibration of all existing presets.

1 Like

My recommendation is to use the CRT controls in detriment of the digital ones, so “CRT Brightness” as opposed to “Black Level”.

The changes are disruptive but you can approximate them as I showed with the examples above. That’s why I added the “CRT Beam” parameters.

In regards to gamma, this is something I wouldn’t obsess too much. The gamma would also change if you used the digital “Black Level” as per the listed equation. The only thing to remember here is that “CRT Black Level” (I wouldn’t touch it too much) is what drives the CRT gamma (the bl_gamma function) and that phosphor adds an additional gamma on top which is undocumented.

For example, with the default(?) phosphor beam gamma of 0.88 to get an output of 2.4 you’d need to set “CRT Brightness” to 30, and you are good to go.

If you want I can make it work backwards, reverse bl_gamma to solve for “bl”, so you input the gamma and you get the “CRT Black Level”:

bl = -(100000*log((72981-500000/(3*bl_gamma))/9058))/945461

I think this is more intuitive for people. I updated it here.

2 Likes

Thanks, when I was referring to “Black Levels” I wasn’t specifically referring to the Black Level control or parameter as that’s something I have never touched.

Rather, I was referring to the resultant effect on Black Levels and Shadow/Dark Detail levels after tweaking things like Gamma In, Gamma Out, Post CRT Brightness, Gamma C, Bright Boost Dark Pixels.

Some of what you said might be slightly above my pay grade so it might be better for @HyperspaceMadness to give an input as to whichever implementation or mitigation might be the preferred or more intuitive method if one had the goal of easing the transition.

I know he always strives for consistency. I will just work to suit with whatever I’m provided.

Thanks again for your prompt and detailed response.

1 Like

Man I’ve been away too long, all these new shader updates left and right I feel like I’m in a candy shop. @Dogway your shader has pretty much been the back bone to my shader presets and I love it so I’ll be trying this new updated grade shader out when I get a chance. Thank you for your amazing work.

2 Likes

Updated to RC5, running out of ideas so will probably update this to official repo tomorrow.

A few things I did here, defaulted gamma to 2.5 to don’t assume beam dynamics. Fixed “Dim to Dark” to “Dark to Dim” as the spec. And the main thing, fixed the ugly artifact of the vignette showing a hard cut where the vignette ends by using a smooth roll-off.

Something very funny that I encountered by chance is that when using the NTSC-J phoshpor gamut SMB’s sky turns blue.

I still have issues with the glsl versions so maybe someone might have a look if I’m missing something.

4 Likes

Updated to RC6, update to this version since I forgot OpenGL was column-major.

Now I double checked with my AviSynth benchtest and everything matches, including temperature.

I also updated the GLSL versions and now should work correctly.

Opinions on how to call each grade variant? grade.slang and grad-LUTless.slang?

4 Likes

Maybe grade-no-LUT.slang ?

3 Likes

This sounds great and is pretty clear.

1 Like

Thanks, straight forward.

I will update grade tomorrow probably due to inherent aspects of embedded color intents.

So probably it’s better to work backwards, say if they developed a game under a display with certain phosphor chromaticities and illuminant what the pixel color values end up is being a compensation of the display characteristics. So I will research on that front. For gamma this is currently how it behaves.

2 Likes

Updated to RC7.

Structural work mainly here. All color work was moved before transfer functions / vignette so it behaves more predictably.

The HUE vs HUE matrix was transposed to behave like Grade 2020.

And now NTSC-J uses D65 as reference white so all phosphors use a common illuminant and they are better correlated.

Also optimized and cleaned code a bit. The gamut compression was disabled since it clipped values instead of scaling them (tested with color bars). And ditched mixfix() for internal mix() as it was causing issues with blue (de)saturation.

I don’t think I will working more on this unless someone spots an issue, so I can move on to different projects.

6 Likes

Just updated Grade and Glass to official repo as PR, should be merged soon I guess.

Meanwhile this is my final comparison between RAW Sonic colors and final Grade adjusted colors. Settings are the same from the last time.

RAW

Grade 2023

6 Likes

This looks slighty warm.

This looks more than a bit cool. Contrast and saturation seem slightly muted and the red in the banner doesn’t look red. I’m not even sure what colour that is.

Overall the image is less vibrant in both brightness and colour departments.

Are sure this is where you want to leave things? Can you post a comparison image to old Grade as well?

Sure, that is with CRT Beam Red at 0.75 to roughly match my old grade 2020 preset look. Set it back to 1.0 and you will get roughly a neutral look with overall higher brightness (Red will not subtract to the overall brightness).

To make it pop more also set U and V Multiplier from my preset 0.95 to 1.0 but I believe saturation was always lost in analog systems.

The red was always like that (orangey) for NTSC-J, that hasn’t changed.

That’s basically my preset for NTSC-J but feel free to experiment and add your own CRT color artifacts.

This is RAW with luma fix:

This is Grade 2023 without color artifacts (only phosphor gamut and temperature -and vignette-):

And this is my old Grade 2020 preset (repost):

1 Like

Thanks for this. I’m not used to NTSC-J colours at least for Sega Genesis. I mostly use the 0 setting for TV-Standard because I was told that the different settings assumed that one was using a LUT on a professionally calibrated display. Someone (cough @Nesguy) can correct me if I’m wrong.

I hope I’m not confusing this with the colour space setting because I also remember something about sRGB should be used in most cases since that’s what the vast majority of users’ displays might support.

1 Like

Well, if you aim for a PVM with RGB interface sure, set signal type to RGB, phosphor to SMPTE or anything that suits you and temperature to 9200K. I simply like a consumer CRT look with red phosphor decay.

And the games were highly saturated in my opinion for the same reasons than for GBA games, because saturation is lost through the signal. Yeah, miss @Nesguy and @Syh, they were highly active back then.

The output color space is straight forward, I use 709 because my display is calibrated to 709 primaries and an EOTF of 2.4. If yours is calibrated to sRGB (EOTF of roughly a power function of 2.222) then set that to sRGB. But sRGB was never meant to be used at home, but offices and well lit rooms.

3 Likes

I updated Grade with some ideas on my repo and while at it noticed some people would get confused with the plethora of settings so I made a brief guide below.

The changelog:

  • Fix signal_type not being parsed
  • Fix a typo on SLANG no-LUT
  • Fix GLSL fallback defaults
  • Add headers for parameters and move up White Point on the stack
  • Remap CRT Brightness and CRT Contrast values to more CRT-like (also go negative)
  • Add CRT flare given a CRT reflection percentage and a Surround Luminance in nits (at simulated 2700K)
  • Quantize model conversion to 8-bits to replicate CRT (and console) circuit arithmetics (not sure about this one so please test)
Signal Type (0:RGB 1:Composite)                           = 0:RGB disables CRT color controls (Hue, U and V)
Phosphor (-2:CRT-95s -1:P22-80s 1:P22-90s 2:NTSC-J 3:PAL) = Emulated CRT phosphor gamut
Diplay Color Space (-1:709 0:sRGB 1:DCI 2:2020 3:Adobe)   = Host display calibrated color space
Dark to Dim adaptation                                    = Dark to Dim OOTF fpr 709 and 2020 color spaces

// ANALOG CONTROLS //    = This block is for CRT related controls so they behave like a CRT would do
White Point              = Emulated CRT white point temperature. Typically blue voltage was increased to increase the perceptual brightness of the image also as typically red phosphors would decay quick
CRT Hue                  = HUE drift
CRT U Shift              = Color drift in the U channel
CRT V Shift              = Color drift in the V channel
CRT U Multiplier         = Color burst to control saturation in U channel
CRT V Multiplier         = Color burst to control saturation in V channel
CRT Gamma                = CRT output gamma (EOTF)
CRT Brightness           = CRT brightness control (misnomer for Offset)
CRT Contrast             = CRT contrast   control (misnomer for Gain)
CRT Beam Red             = Controls the summed average of beam voltage current and phosphor decay for red
CRT Beam Green           = Controls the summed average of beam voltage current and phosphor decay for green 
CRT Beam Blue            = Controls the summed average of beam voltage current and phosphor decay for blue 
CRT Lambert Refl. in %   = Percetange of flare/glare lambertian reflectance (F0) of CRT display
Surround Luminance -nits-= Surround luminance in nits (cd/m2) for emulated room viewing environment at 2700K surround illuminant
Vignette Toggle          = Toggles vignetting effect around the borders
Vignette Strength        = Controls the vignette strength of the effect
Vignette Power           = Controls the decay rate for the vignette


// DIGITAL CONTROLS //   = Digital color grading controls for artistic purposes
Sega Luma Fix            = Fix white level for Retro Sega Systems; Genesis, 32x, CD and Saturn 2D had color palettes designed in TV levels to save on transformations
Brightness               = Increases brightness (read "Gain") in a roll-off fashion (aka no clipping)
Contrast                 = Applies a sigmoidal (aka no clipping) contrast
Contrast Pivot           = Sets the pivot for the sigmoidal contrast, therefore allowing going from an S shaped curve to a more power like (think gamma) curve
Saturation               = Increase or lower the global saturation
Dullness/Vibrance        = Interacts with "Saturation" to bias its weight over most saturated areas vs least saturated areas
Hue vs Sat Red           = Increase or lower saturation in reds
Hue vs Sat Green         = Increase or lower saturation in greens
Hue vs Sat Blue          = Increase or lower saturation in blues
Black Level              = Raise the black level in a "Lift" fashion
Black-Red Tint           = Red   tint for the black floor (noop if blacks are not raised)
Black-Green Tint         = Green tint for the black floor (noop if blacks are not raised)
Black-Blue Tint          = Blue  tint for the black floor (noop if blacks are not raised)
White-Red Tint           = Red   tint for the whites (similar to CRT Beam Red but keeping brightness)
White-Green Tint         = Green tint for the whites (similar to CRT Beam Red but keeping brightness)
White-Blue Tint          = Blue  tint for the whites (similar to CRT Beam Red but keeping brightness)
Red-Green Tint           = Green contribution to Reds
Red-Blue Tint            = Blue  contribution to Reds
Green-Red Tint           = Red   contribution to Greens
Green-Blue Tint          = Blue  contribution to Greens
Blue-Red Tint            = Red   contribution to Blues
Blue-Green Tint          = Green contribution to Blues
LUT 1 Toggle             = Enable LUT 1 (look LUT)
LUT Size 1               = Size of LUT 1 (8, 16, 32, 48, 64)
LUT 2 Toggle             = Enable LUT 2 (technical LUT)
LUT Size 2               = Size of LUT 2 (8, 16, 32, 48, 64)
4 Likes

Very interesting. Did the old implementation also do this with no clipping?

1 Like

Yes, the function rolled_gain() hasn’t changed in my updates.

Overall I think the shader could be squeeze-optimized a little bit further, but I don’t know how to profile the shader or whether it’s slow or fast.

1 Like

I didn’t know this and I used to be very concerned about clipping when trying to get back lost brightness due to strong mask and scanline settings in my presets.

Perhaps you can benchmark the same games using different versions to see if there are any differences.