The main purpose is that I want a tone map which I can have full control over the luma white point, so that I can exactly control when the lights get a little over exposed and when I look at the sun there is no whole white screen.
This requires the luma white point changes along with the adaptation. I want to control the exact start and end points, as well as the mid range (how fast it changes).
Another purpose is to get rid of palette. I realized the palette is kind of adaptive color redistribution, acts like a dynamic gamma correction changes with adaptation level. But I don't like the fact that palette is only editable outside the game with image/3D tools. If this is kind of dynamic gamma, I want it to be done in game and can be tuned on the fly. Similar to luma white point control.
Other reason to have adaptive gamma rather than palette is that tone map (the one I use is a variation of Reinhard's tone map formula) changes color distribution, I need a dynamic gamma correction that compensates.
Another feature I'd love to have is adaptive contrast, which apply an S-curve to the image but the curveness and "turning point" automatically changes with adaptation level. Unfortunately I learned from days of experimenting that it's very impossible. Because I need to know the exact midtone as the S-curve turning point, but can't get it since pixel shader process each pixel individually while midtone needs to analyse the whole screen. I'd be glad to know any tricks that can achieve this, before that I'll try SweetFX's auto contrast feature.
Ok enough explaining, here's the code:
Code: Select all
float4 tempF1; //1,2,3,4
float4 tempF2; //5,6,7,8
float4 tempF3; //9,0
float wMin = tempF1.x; // Key 1: white min
float wMax = 2.5*tempF1.y; // Key 2: white max (scale from 2.5)
float wGaMin = 0.1;
float wGaMax = 1;
float wCurve = tempF1.z; // Key 3: white curve
//----------------------------------
float gMin = 2*tempF1.w; // Key 4: gamma min (scale from 2)
float gMax = tempF2.x; // Key 5: gamma max
float gGaMin = 0.1;
float gGaMax = 1;
float gCurve = tempF2.y; // Key 6: gamma curve
//----------------------------------
float satVal = tempF3.y; // Key 0: saturation
// Get grayadapt
float gadapt = max(max(Adaptation.x, Adaptation.y), Adaptation.z); // use max(r,g,b) otherwise may lose range when color is polarized
// Saturation
color.xyz+=0.000001;
float3 ncolor = normalize(color.xyz);
float3 scolor = color.xyz/ncolor.xyz; // standardize color
ncolor.xyz = pow(ncolor.xyz, satVal); // apply saturation
color.xyz = scolor.xyz*ncolor.xyz; // restore color
// Calc luma white
float wIn = clamp(gadapt,wGaMin,wGaMax); // limit input range
float wInScaled = (wIn-wGaMin)/(wGaMax-wGaMin); // scale to 0..1
float wApplyCurve = pow(wInScaled,wCurve); // apply curve on 0..1
float lumaWhite = wApplyCurve*(wMax-wMin)+wMin; // scale back to min..max
// Tone map
color.xyz = color.xyz*(1+color.xyz/pow(lumaWhite,2))/(1+color.xyz); // map 0..lumaWhite to 0..1
// Calc Gamma
float gIn = clamp(gadapt,gGaMin,gGaMax);
float gInScaled = (gIn-gGaMin)/(gGaMax-gGaMin);
float gApplyCurve = pow(gInScaled, gCurve);
float gVal = gApplyCurve*(gMax-gMin)+gMin;
// Apply gamma
color.xyz = pow(color.xyz, gVal); // apply gamma on 0..1