Chromatic Adaptation

share shaders here
Post Reply
  • Author
  • Message
Offline
Posts: 3
Joined: 03 Mar 2019, 05:34

Chromatic Adaptation

based on https://web.stanford.edu/~sujason/Color ... ation.html

Code: Select all

bool enableWhiteBalance < string UIName = "EnableWhiteBalance"; > = {true}

float catType
<
    string UIName = "ChromaticAdaptationTransformationType";
    string UIWidget = "Spinner";
    float UIMin = 0.0f;
    float UIMax = 5.0f;
    float UIStep = 1.0f;
> = {1.0f};

float catClampXMax
<
    string UIName = "ChromaticAdaptationXMax";
    string UIWidget = "Spinner";
    float UIMin = 0.0f;
    float UIMax = 5.0f;
    float UIStep = 1.02f;
> = {0.01f};

float catClampXMin
<
    string UIName = "ChromaticAdaptationXMin";
    string UIWidget = "Spinner";
    float UIMin = 0.0f;
    float UIMax = 5.0f;
    float UIStep = 0.85f;
> = {0.01f};

float catClampZMax
<
    string UIName = "ChromaticAdaptationZMax";
    string UIWidget = "Spinner";
    float UIMin = 0.0f;
    float UIMax = 5.0f;
    float UIStep = 1.07f;
> = {0.01f};

float catClampZMin
<
    string UIName = "ChromaticAdaptationZMin";
    string UIWidget = "Spinner";
    float UIMin = 0.0f;
    float UIMax = 5.0f;
    float UIStep = 0.95f;
> = {0.01f};

Code: Select all

//sRGB D65, RGB must be linear and in range [0,1]
//output cover whole value range[0,1], z may exceed 1
//http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
float3 RGB2XYZ(float3 color)
{
    static const float3x3 mat = float3x3(
		0.412453, 0.357580, 0.180423,
		0.212671, 0.715160, 0.072169,
		0.019334, 0.119193, 0.950227);
	return mul(mat, color);
}

float3 XYZ2RGB(float3 color)
{
    static const float3x3 mat = float3x3(
		 3.240479, -1.53715, -0.498535,
		-0.969256,  1.875991, 0.041556,
		 0.055648, -0.204043, 1.057311);
	return mul(mat, color);
}

float2 XYZ2xy(float3 i)
{
	float2 o = i.xy;
	float s = i.x + i.y + i.z;
	return o/s;
}

float3 xyY2XYZ(float3 i)
{
	float3 o;
	o.y = i.z/i.y;
	o.x = i.x*o.y;
	o.z = (1.0-i.x-i.y)*o.y;
	o.z = o.y-i.x*o.y-i.y*o.y;
	o.y = i.z;
	return o;
}

Code: Select all

//https://web.stanford.edu/~sujason/ColorBalancing/
float3x3 cbCAT(float3 xyz_est, float3 xyz_target, float type)
{
	// the following are mostly taken from S. Bianco. "Two New von Kries Based
	// Chromatic Adapatation Transforms Found by Numerical Optimization."
	float3x3 xfm;
	float3x3 xfm_inv;
	[branch]if(type == 0)
	{
		//vonKries Hunt-Pointer-Estevez normalized to D65
		xfm = float3x3(0.40024, 0.7076,-0.08081,
					  -0.2263, 1.16532, 0.0457,
					   0, 0, 0.91822);
		xfm_inv = float3x3(1.85994, -1.12938, 0.2198974,
						   0.36119, 0.63881, -0.00000637,
						   0, 0, 1.089);
	}
	else if(type == 1.0)
	{
		//bradford
		xfm = float3x3(0.8951, 0.2664, -0.1614,
					   -0.7502, 1.7135, 0.0367,
					   0.0389, -0.0685, 1.0296);
		xfm_inv = float3x3(0.98699, -0.147054, 0.15996,
						   0.4323, 0.51836, 0.04929,
						   -0.00852866, 0.04004282, 0.9684867);
	}
	else if(type == 2.0)
	{
		//sharp
		xfm = float3x3(1.2694, -0.0988, -0.1706,
					   -0.8364, 1.8006, 0.0357,
					   0.0297, -0.0315, 1.0018);
		xfm_inv = float3x3(0.8156333, 0.0471547788, 0.137216627,
						   0.379114399, 0.5769424, 0.04400087,
						   -0.01226, 0.016743, 0.99552);
	}
	else if(type == 3.0)
	{
		//cmccat2000
		xfm = float3x3(0.7982, 0.3389, -0.1371,
					   -0.5918, 1.5512, 0.0406,
					   0.0008, 0.239, 0.9753);
		xfm_inv = float3x3(1.0623, -0.256743, 0.16,
						   0.40792, 0.55, 0.03443688,
						   -0.100833, -0.1346262, 1.01675543);
	}
	else if(type == 4.0)
	{
		//cat02
		xfm = float3x3(0.7328, 0.4296, -0.1624,
					   -0.7036, 1.6975, 0.0061,
					   0.0030, 0.0136, 0.9834);
		xfm_inv = float3x3(1.09612382, -0.278869, 0.1827452,
						   0.45436904, 0.4735331543, 0.0720978,
						   -0.0096276, -0.005698, 1.01532564);
	}
	else
	{
		//XYZ Scale
		xfm = float3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);
		xfm_inv = float3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);
	}
	
	float3 gain = mul(xfm, xyz_target)/mul(xfm, xyz_est);
	gain.x = clamp(gain.x, catClampXMin, catClampXMax);
	gain.z = clamp(gain.z, catClampZMin, catClampZMax);
	float3x3 gainMat = float3x3(gain.x,0,0,0,gain.y,0,0,0,gain.z);
	float3x3 outMat = mul(xfm_inv, mul(gainMat, xfm));
	// outMat = inv(sRGBtoXYZ)*outMat*sRGBtoXYZ;
	return outMat;
}

//@color linear rgb [0,1]
//@rgbEst image average rgb, linear, [0,1]
float3 grayWorld(float3 color, float3 rgbEst, float catType)
{
	static const float3 xyz_D65 = float3(95.04, 100.0, 108.88);//http://en.wikipedia.org/wiki/D65, normalized Y = 100
	float2 xyEst = XYZ2xy(RGB2XYZ(rgbEst));//calculate xy chromaticity
    float3 xyzEst = xyY2XYZ(float3(xyEst,100.0));//normalize Y to 100 so D65 luminance comparable
    return saturate(XYZ2RGB(mul(cbCAT(xyzEst,xyz_D65,catType), RGB2XYZ(color))));
}


how to use:
take oldrim for example
assume that enb adaptation output average rgb of the image(not work in SSE or FO4, only red channel, any work around?)
first set AdaptationSensitivity=0 in enbseries.ini

then in enbeffect

Code: Select all

// ......

//read enb adaptation in time
float4	Adaptation=tex2D(_s4, 0.5);

// ......
// do auto exposure
// ......
// do tonemap
// ......

[branch]if(enableWhiteBalance)
{
	float avgPeak = max3(Adaptation);
	float avgPeakN = avgPeak/(avgPeak+1.0);
	float3 avgRgb = Adaptation.rgb * avgPeakN / avgPeak;
	color.rgb = grayWorld(color.rgb, avgRgb, catType);
}

Post Reply