[Help] Tone map and adaptation in shader

share shaders here
Post Reply
  • Author
  • Message
Offline
Posts: 66
Joined: 12 Jun 2013, 07:19

[Help] Tone map and adaptation in shader

I have been trying to create a new tone map/adaptation code. Have tried all kinds of formula I can find by Google and self learned a little, now I have something working. Since I didn't have any shader programming experience before, I figure it's better to share it here and get some nice suggestions.

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

Offline
User avatar
*blah-blah-blah maniac*
Posts: 1938
Joined: 05 Mar 2012, 02:08

Re: [Help] Tone map and adaptation in shader

Nice bit of code you have here wonderfulmore, I tried it out but removed the temp variable and replaced them with default commands, prefer it like that and I also added Kermles Tonemapping method to it and it's a really nice addition to the enbeffect.fx :)

Offline
Posts: 66
Joined: 12 Jun 2013, 07:19

Re: [Help] Tone map and adaptation in shader

--JawZ-- wrote:Nice bit of code you have here wonderfulmore, I tried it out but removed the temp variable and replaced them with default commands, prefer it like that and I also added Kermles Tonemapping method to it and it's a really nice addition to the enbeffect.fx :)
It's an honor to be used in your file sir!

In my own opinion, by number this adaptation may not be as optimized as Postprocess 2 but it's more versatile and easier to tweak. I'll come up with an adaptation tuning guide after I settled every piece. Now I am struggling with contrast. Even without exact mid tone, it's possible to used a relatively static turning point if you always optimize gamma before apply contrast. However I can't get a good formula, most of the contrast formulas i found are horrible to be used in this shader... keep looking and doing math.

UPDATE: Finally got a contrast formula work, here's the code

Code: Select all

	float conVal = tempF2.z; // Key 7: contrast
	float conTurn = 0.5*(0-tempF2.w); // Key 8: contrast turnning point (variable 8 is -1 by default)

	// Apply contrast
	float conGray = dot(color.xyz,float3(0.27, 0.67, 0.06));
	float conNewGray = conGray;
	if(conGray < conTurn && conGray > 0)
	{
		conNewGray = conTurn*pow(conGray/conTurn, conVal);
	};
	if(conGray > conTurn && conGray < 1)
	{
		conNewGray = 1-(1-conTurn)*pow((1-conGray)/(1-conTurn),conVal);
	};
	color.xyz = color.xyz*conNewGray/conGray;
Post Reply