Code:
// Emulate TDP software rendering.
static const float3 LUMINANCE_VECTOR = float3(0.2125, 0.7154, 0.0721);
float g_fGamma;
float g_fSaturation;
float g_fContrast;
float g_fBrightness;
float4 g_fColorFilter;
float2 g_fScreenSize;
// the frame texture
sampler s0 : register(s0);
/*
* RGB <-> HSV conversion by Ronja Böhringer.
* https://creativecommons.org/licenses/by/4.0/
*/
float3 hue2rgb(float hue) {
hue = frac(hue); //only use fractional part
float r = abs(hue * 6 - 3) - 1; //red
float g = 2 - abs(hue * 6 - 2); //green
float b = 2 - abs(hue * 6 - 4); //blue
float3 rgb = float3(r,g,b); //combine components
rgb = saturate(rgb); //clamp between 0 and 1
return rgb;
}
float3 hsv2rgb(float3 hsv)
{
float3 rgb = hue2rgb(hsv.x); //apply hue
rgb = lerp(1, rgb, hsv.y); //apply saturation
rgb = rgb * hsv.z; //apply value
return rgb;
}
float3 rgb2hsv(float3 rgb)
{
float maxComponent = max(rgb.r, max(rgb.g, rgb.b));
float minComponent = min(rgb.r, min(rgb.g, rgb.b));
float diff = maxComponent - minComponent;
float hue = 0;
if(maxComponent == rgb.r) {
hue = 0+(rgb.g-rgb.b)/diff;
} else if(maxComponent == rgb.g) {
hue = 2+(rgb.b-rgb.r)/diff;
} else if(maxComponent == rgb.b) {
hue = 4+(rgb.r-rgb.g)/diff;
}
hue = frac(hue / 6);
float saturation = diff / maxComponent;
float value = maxComponent;
return float3(hue, saturation, value);
}
float4 SatGammaPS(in float2 uv : TEXCOORD0, uniform int bDoSat, uniform int bDoGamma, uniform int bDoContrBright) : COLOR
{
float4 vColor = tex2D(s0, uv);
float levels = 16.0;
float qGamma = .68;
float3 hsv = rgb2hsv(saturate(vColor.xyz - .02));
hsv.z = pow(hsv.z, qGamma);
if (hsv.x > .02 && hsv.x < .3 && hsv.z < .3) {
/*
* There's a bunch of brown-ish colors in TDP global
* palette, so these colors have more levels, and some hue
* errors.
*/
levels *= 1.48;
float f = floor(hsv.z * levels);
f = fmod(f, 3.0) - 1.0;
hsv.x += f * .05;
hsv.x = frac(hsv.x + 1);
}
hsv.z = floor(hsv.z * levels) / levels;
hsv.z = pow(hsv.z, 1.0 / qGamma);
hsv.y *= lerp(1, pow((1.0-hsv.z), 2.0), .5);
vColor.xyz = hsv2rgb(saturate(hsv));
float lumi = dot(vColor.xyz, LUMINANCE_VECTOR);
vColor.xyz = lerp(lumi.xxx, vColor.xyz, g_fSaturation * 1.4) * g_fColorFilter.xyz;
vColor.xyz = saturate(vColor.xyz * g_fContrast * 1.5 + g_fBrightness);
if (bDoGamma)
{
vColor.xyz = pow(vColor.xyz, g_fGamma);
}
return vColor;
}
// apply saturation/color filter, brightness/contrast and gamma
technique TeqBrSatGamma
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(1, 1, 1);
}
}
// apply brightness/contrast and gamma
technique TeqBrGamma
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(0, 1, 1);
}
}
// apply brightness/contrast and saturation/color
technique TeqBrSat
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(1, 0, 1);
}
}
// apply saturation/color filter and gamma
technique TeqSatGamma
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(1, 1, 0);
}
}
// apply only gamma
technique TeqGamma
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(0, 1, 0);
}
}
// apply only brightness/contrast
technique TeqBr
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(0, 0, 1);
}
}
// apply only saturation/color
technique TeqSat
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(1, 0, 0);
}
}
// plain copy
technique TeqCopy
{
pass P0
{
PixelShader = compile ps_3_0 SatGammaPS(0, 0, 0);
}
}