I think if you set crt_gamma to 2.4 and display_gamma to 2.16, you should get the same result as 0.9 in that shader. More of an issue, I would think, though, is the grayscale part, which is using the egalitarian-but-not-quite-right 0.3333 for all three channels, instead of something like float3(0.2126, 0.7152, 0.0722) or float3(0.299, 0.587, 0.114), which are the standards for ATSC and NTSC, respectively. Either of these makes greens much less dark and muddy than 0.3333 for all.
I agree that setting the saturation to 0.5 is probably a bit too low, but it’s going to be very subjective and never look quite right to everyone, due in part to the fact that it is/was a front-lit screen, so environmental lighting plays a big role. I made a fun frontlighting shader for higan that uses a light map and desaturates the colors based on how much “light” is on it, but I don’t think it is/was particularly “accurate” or even worth using.
How much saturation should be used will also very from game-to-game, as there was no real standard for how to compensate for the crummy screens. For example, using FF6 vs FF3 SNES to calibrate will look good for that game, but may not look right for other ports that have more/less intense colors.
Here’s that same shader with the ATSC grayscale and the easier-to-understand gamma settings:
/*
Author: Themaister
License: Public domain
*/
// Shader that replicates gamma-ramp of bSNES/Higan.
void main_vertex
(
float4 position : POSITION,
out float4 oPosition : POSITION,
uniform float4x4 modelViewProj,
float2 tex : TEXCOORD,
out float2 oTex : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oTex = tex;
}
// Tweakables.
#define saturation 0.5
#define crt_gamma 2.4
#define monitor_gamma 2.16
#define luminance 0.9
float3 grayscale(float3 col)
{
// better grayscale
return float3(dot(col, float3(0.2126, 0.7152, 0.0722)));
}
float4 main_fragment(float2 tex : TEXCOORD, uniform sampler2D s0 : TEXUNIT0) : COLOR
{
float3 res = tex2D(s0, tex).xyz;
res = lerp(grayscale(res), res, saturation); // Apply saturation
res = pow(res, float3(crt_gamma / monitor_gamma)); // Apply gamma
return float4(saturate(res * luminance), 1.0);
}