//////////////////Defines: ShaderPack Specific

//Do SDR onto HDR UpgradeToneMap()?
//#define RENODX_UPGRADE_ENABLED

//HDR Tonemap settings
#define RENODX_HDRTONEMAP_TYPE RENODX_HDRTONEMAP_TYPE_GT //[RENODX_HDRTONEMAP_TYPE_OFF RENODX_HDRTONEMAP_TYPE_REINHARD RENODX_HDRTONEMAP_TYPE_ACES RENODX_HDRTONEMAP_TYPE_GT RENODX_HDRTONEMAP_TYPE_GT7 RENODX_HDRTONEMAP_TYPE_HERMITE RENODX_HDRTONEMAP_TYPE_EXPROLL RENODX_HDRTONEMAP_TYPE_HABLE RENODX_HDRTONEMAP_TYPE_NEUTWO RENODX_HDRTONEMAP_TYPE_FROSTBITE RENODX_HDRTONEMAP_TYPE_PSYCHO11]

//Color space expected for each step
#define RENODX_WORKINGCS_SHADERPACK RENODX_CS_BT709 /* Internal working color space of shaderpack. */
#define RENODX_WORKINGCS_HDRTONEMAP RENODX_CS_BT709 //[RENODX_CS_BT709 RENODX_CS_BT2020 RENODX_CS_AP1] /* Colorspace to do perchannel HDR tonemap. */
#define RENODX_WORKINGCS_AFTERTONEMAP RENODX_CS_BT709 /* Colorspace to encode leaving ToneMapPass(). */
#define RENODX_WORKINGCS_AFTERTONEMAP_DOCLAMP 1 /* Clamp after ToneMapPass? */
#define RENODX_WORKINGCS_RENDERINTERMEDIATEPASSINPUT RENODX_CS_BT709 /* Colorspace to decode entering RenderIntermediatePass(). */

//Gamma encoding expected for each step
#define RENODX_WORKINGGAMMA_AFTERTONEMAP RENODX_GAMMA_NONE /* Gamma encoding to encode leaving ToneMapPass(). */
#define RENODX_WORKINGGAMMA_RENDERINTERMEDIATEPASSINPUT RENODX_GAMMA_NONE /* Gamma encoding to decode entering RenderIntermediatePass(). */


/*
//////////////////// shaders.properties
# RenoDX
screen.renodx = RENODX_BRIGHTNESS_USEREPORTED RENODX_PEAK_BRIGHTNESS RENODX_GAME_BRIGHTNESS RENODX_UI_BRIGHTNESS <empty> \
RENODX_COLORGRADE_HIGHLIGHTS RENODX_COLORGRADE_HIGHLIGHTS_MID RENODX_COLORGRADE_SHADOWS RENODX_COLORGRADE_SHADOWS_MID RENODX_COLORGRADE_CONTRAST RENODX_COLORGRADE_CONTRAST_MID RENODX_COLORGRADE_SATURATION <empty> \
RENODX_HDRTONEMAP_TYPE RENODX_EXPOSURE [renodx_reinhard] [renodx_aces] [renodx_gt] [renodx_gt7] [renodx_hermite] [renodx_exproll] [renodx_hable] [renodx_neutwo] [renodx_frostbite] <empty> \
RENODX_RCAS RENODX_RCAS_DENOISE <empty> \
RENODX_GAMMACORRECTION RENODX_INTER_MODE <empty> \
RENODX_UPGRADE_ENABLED RENODX_UPGRADE_AMOUNT RENODX_UPGRADE_AUTO RENODX_APPC_AMOUNT RENODX_APPC_POW RENODX_DEBUG
screen.renodx.columns = 1

screen.renodx_reinhard = RENODX_SCALING RENODX_WORKINGCS_HDRTONEMAP RENODX_SHOULDER_START RENODX_WHITE_CLIP 
screen.renodx_reinhard.columns = 1

screen.renodx_aces = RENODX_ACES_RGC RENODX_ACES_RRT RENODX_ACES_MIDGRAY
screen.renodx_aces.columns = 1

screen.renodx_gt = RENODX_SCALING RENODX_WORKINGCS_HDRTONEMAP RENODX_GT_A RENODX_GT_M RENODX_GT_L RENODX_GT_C RENODX_GT_B
screen.renodx_gt.columns = 1

screen.renodx_gt7 = RENODX_GT7_A RENODX_GT7_M RENODX_GT7_L RENODX_GT7_C RENODX_GT7_BLOW_AMOUNT RENODX_GT7_BLOW_START RENODX_GT7_BLOW_END
screen.renodx_gt7.columns = 1

screen.renodx_hermite = RENODX_SCALING RENODX_WORKINGCS_HDRTONEMAP RENODX_WHITE_CLIP
screen.renodx_hermite.columns = 1

screen.renodx_exproll = RENODX_SCALING RENODX_WORKINGCS_HDRTONEMAP RENODX_SHOULDER_START
screen.renodx_exproll.columns = 1

screen.renodx_hable = RENODX_SCALING RENODX_WORKINGCS_HDRTONEMAP RENODX_SHOULDER_START RENODX_HABLE_A RENODX_HABLE_B RENODX_HABLE_C RENODX_HABLE_D RENODX_HABLE_E RENODX_HABLE_F RENODX_HABLE_W RENODX_HABLE_BLOWOUT
screen.renodx_hable.columns = 1

screen.renodx_neutwo = RENODX_SCALING RENODX_WORKINGCS_HDRTONEMAP 
screen.renodx_neutwo.columns = 1

screen.renodx_frostbite = RENODX_SCALING RENODX_SHOULDER_START RENODX_FROSTBITE_SAT RENODX_FROSTBITE_HUE
screen.renodx_neutwo.columns = 1

screen.renodx_daniele RENODX_SCALING RENODX_DANIELE_G RENODX_DANIELE_C RENODX_DANIELE_C_D RENODX_DANIELE_W_G RENODX_DANIELE_T_1 RENODX_DANIELE_R_HIT_MIN RENODX_DANIELE_R_HIT_MAX
screen.renodx_neutwo.columns = 1

//////////////////// shaders.properties sliders 
RENODX_PEAK_BRIGHTNESS RENODX_UI_BRIGHTNESS RENODX_GAME_BRIGHTNESS RENODX_SHOULDER_START RENODX_WHITE_CLIP RENODX_UPGRADE_AMOUNT RENODX_UPGRADE_AUTO RENODX_APPC_AMOUNT RENODX_APPC_POW RENODX_COLORGRADE_HIGHLIGHTS RENODX_COLORGRADE_HIGHLIGHTS_MID RENODX_COLORGRADE_SHADOWS RENODX_COLORGRADE_SHADOWS_MID RENODX_COLORGRADE_CONTRAST RENODX_COLORGRADE_CONTRAST_MID RENODX_COLORGRADE_SATURATION RENODX_RCAS RENODX_EXPOSURE RENODX_ACES_MIDGRAY RENODX_GT_A RENODX_GT_M RENODX_GT_L RENODX_GT_C RENODX_GT_B RENODX_GT7_A RENODX_GT7_M RENODX_GT7_L RENODX_GT7_C RENODX_GT7_BLOW_AMOUNT RENODX_GT7_BLOW_START RENODX_GT7_BLOW_END RENODX_HABLE_A RENODX_HABLE_B RENODX_HABLE_C RENODX_HABLE_D RENODX_HABLE_E RENODX_HABLE_F RENODX_HABLE_W RENODX_HABLE_BLOWOUT RENODX_FROSTBITE_SAT RENODX_FROSTBITE_HUE RENODX_DANIELE_G RENODX_DANIELE_C RENODX_DANIELE_C_D RENODX_DANIELE_W_G RENODX_DANIELE_T_1 RENODX_DANIELE_R_HIT_MIN RENODX_DANIELE_R_HIT_MAX \

//////////////////// en_US.lang
# RenoDX Screens
screen.renodx = RenoDX
screen.renodx_reinhard = Reinhard Piecewise Settings
screen.renodx_reinhard.comment = Gradually roll off highlights to peak.
screen.renodx_aces = ACES Settings
screen.renodx_aces.comment = Contrast and blowout via ACES color space.
screen.renodx_gt = Gran Turismo Sport Settings
screen.renodx_gt.comment = ACES-like contrast, very clippy with a fully linear section.
screen.renodx_gt7 = Gran Turismo 7 Settings
screen.renodx_gt7.comment = Similar to GTSport, but perceptual in BT2020.
screen.renodx_hermite = Hermite Spline Settings
screen.renodx_hermite.comment = Scalable spline.
screen.renodx_hable = Uncharted 2 / Hable Settings
screen.renodx_hable.comment = From Uncharted 2 by John Hable, with toe, linear extended to HDR.
screen.renodx_exproll = Exponential Roll-off Settings
screen.renodx_exproll.comment = By DICE, aggressive shoulder roll-off.
screen.renodx_neutwo = NeuTwo Settings
screen.renodx_neutwo.comment = Neutral tonemapper based on power of 2 (squared/sqrt).
screen.renodx_frostbite = Frostbite Settings
screen.renodx_frostbite.comment = DICE's perceptual tonemapper with saturation boost and hue preservation.
screen.renodx_daniele = Daniele Settings
screen.renodx_daniele.comment = Daniele Siragusano's compression curve with a lot of variables. 

# RenoDX Main
option.RENODX_ENABLED = Enabled
option.RENODX_ENABLED.comment = Enable HDR.

option.RENODX_BRIGHTNESS_USEREPORTED = Use Reported Brightness
option.RENODX_BRIGHTNESS_USEREPORTED.comment = Use values from mods that report the display's capabilities, automatically configuring brightness values. (If sliders don't work, then values are found.)
value.RENODX_BRIGHTNESS_USEREPORTED.RENODX_BRIGHTNESS_USEREPORTED_ON = If Available
value.RENODX_BRIGHTNESS_USEREPORTED.RENODX_BRIGHTNESS_USEREPORTED_OFF = Off

option.RENODX_UI_BRIGHTNESS = UI Brightness
option.RENODX_UI_BRIGHTNESS.comment = In nits. Requires a shader after UI for this to take effect (i.e. ReShade, remember to sync value.)

option.RENODX_GAME_BRIGHTNESS = Game Brightness
option.RENODX_GAME_BRIGHTNESS.comment = In nits, aka paper white.

option.RENODX_PEAK_BRIGHTNESS = Peak Brightness
option.RENODX_PEAK_BRIGHTNESS.comment = Maximum brightness in nits.

option.RENODX_SHOULDER_START = Shoulder Start
option.RENODX_SHOULDER_START.comment = Beginning of tonemap's shoulder in nits.

option.RENODX_WHITE_CLIP = White Clip Threshold
option.RENODX_WHITE_CLIP.comment = Aka the expected maximum of color to tonemap.

option.RENODX_CLAMP_COLORSPACE = Color Space Clamp
option.RENODX_CLAMP_COLORSPACE.comment = Clamp right at final output.
value.RENODX_CLAMP_COLORSPACE.RENODX_CS_NONE    = None
value.RENODX_CLAMP_COLORSPACE.RENODX_CS_BT709   = BT709
value.RENODX_CLAMP_COLORSPACE.RENODX_CS_BT2020  = BT2020
value.RENODX_CLAMP_COLORSPACE.RENODX_CS_AP1     = AP1

option.RENODX_GAMMACORRECTION = Gamma Correction
option.RENODX_GAMMACORRECTION.comment = Apply EOTF / Gamma Correction before tonemapping. This is a last resort solution, since UI is not included!
value.RENODX_GAMMACORRECTION.RENODX_GAMMA_NONE = Off
value.RENODX_GAMMACORRECTION.RENODX_GAMMA_22  = 2.2
value.RENODX_GAMMACORRECTION.RENODX_GAMMA_24 = 2.4

option.RENODX_COLORGRADE_CONTRAST = Contrast
option.RENODX_COLORGRADE_CONTRAST.comment = Stretches luminance both ways around mid gray.
option.RENODX_COLORGRADE_CONTRAST_MID = Contrast Mid Gray
option.RENODX_COLORGRADE_CONTRAST_MID.comments = The pivot point of the effect.

option.RENODX_COLORGRADE_HIGHLIGHTS = Highlights
option.RENODX_COLORGRADE_HIGHLIGHTS.comment = Stretches luminance if above mid gray.
option.RENODX_COLORGRADE_HIGHLIGHTS_MID = Highlights Mid Gray
option.RENODX_COLORGRADE_HIGHLIGHTS_MID.comments = The pivot point of the effect.

option.RENODX_COLORGRADE_SHADOWS = Shadows
option.RENODX_COLORGRADE_SHADOWS.comment = Stretches luminance if below mid gray.
option.RENODX_COLORGRADE_SHADOWS_MID = Shadows Mid Gray
option.RENODX_COLORGRADE_SHADOWS_MID.comments = The pivot point of the effect.

option.RENODX_COLORGRADE_SATURATION = Saturation
option.RENODX_COLORGRADE_SATURATION.comment = OKLab saturation, in percentage. 100 is disabled.

option.RENODX_RCAS = RCAS
option.RENODX_RCAS.comment = Robust (4 samples) Constrast Adaptive Sharpening

option.RENODX_RCAS_DENOISE = RCAS Denoise
option.RENODX_RCAS.comment = Turn on RCAS's additional denoising.

# RenoDX Debug
option.RENODX_DEBUG = Debug Output
value.RENODX_DEBUG.RENODX_DEBUG_NONE = None
value.RENODX_DEBUG.RENODX_DEBUG_UNTONEMAPPED = Untonemapped
value.RENODX_DEBUG.RENODX_DEBUG_TONEMAPPED = Tonemapped
value.RENODX_DEBUG.RENODX_DEBUG_SPLIT = Untonemapped / Tonemapped

option.RENODX_WORKINGCOLORSPACE = Working Color Space
option.RENODX_WORKINGCOLORSPACE.comment = The color space of the shaderpack's rendering pipeline.

# RenoDX Upgrade
option.RENODX_UPGRADE_ENABLED = Upgrade Tone Map
option.RENODX_UPGRADE_ENABLED.comment = (BT709 Clamp, and maybe not available.) Naively map SDR chroma on HDR luma. This maintains all of SDR colorgrading, but will be quick to blowout.

option.RENODX_UPGRADE_AMOUNT = Upgrade Amount
option.RENODX_UPGRADE_AMOUNT.comment = How much should the HDR color be influenced by the SDR?

option.RENODX_UPGRADE_AUTO = Upgrade Auto Correct
option.RENODX_UPGRADE_AUTO.comment = How much should the HDR color be corrected by the SDR?

option.RENODX_APPC_AMOUNT = Per Channel Correction Amount
option.RENODX_APPC_AMOUNT.comment = (w/ Upgrade Tone Map) Attempt to reverse the blowout of SDR tonemapper. 
option.RENODX_APPC_POW = Per Channel Correction Highlights Only
option.RENODX_APPC_POW.comment = (w/ Upgrade Tone Map) Increase to target only highlights.

# RenoDX HDR Tone Map
option.RENODX_EXPOSURE = Exposure
option.RENODX_EXPOSURE.comment = Mutltiplier on color right before HDR Tone Map.

value.RENODX_SCALING.RENODX_SCALING_Y = Luminance
value.RENODX_SCALING.RENODX_SCALING_PERCHANNEL = Per Channel or Perceptual
value.RENODX_SCALING.RENODX_SCALING_MAXCHANNEL = Max Channel
value.RENODX_SCALING.RENODX_SCALING_AUTO = Automatic
option.RENODX_SCALING = Scaling
option.RENODX_SCALING.comment = Run the tonemapper for all rgb channels (blows out) or base it off of a pivot (luma or max channel)?

option.RENODX_HDRTONEMAP_TYPE = HDR Tone Map Type
option.RENODX_HDRTONEMAP_TYPE.comment = Read tooltip of each!
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_OFF = Off
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_REINHARD = Reinhard Piecewise
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_ACES = ACES
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_GT = Gran Turismo Sport
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_HERMITE = Hermite Spline
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_GT7 = Gran Turismo 7
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_HABLE = Uncharted 2 / Hable
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_EXPROLL = Exponential Roll-off
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_NEUTWO = NeuTwo
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_FROSTBITE = Frostbite
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_DANIELE = Daniele
value.RENODX_HDRTONEMAP_TYPE.RENODX_HDRTONEMAP_TYPE_DEFAULT = Default

option.RENODX_WORKINGCS_HDRTONEMAP = HDR Tone Map Working Color Space
option.RENODX_WORKINGCS_HDRTONEMAP.comment = The working color space of the tonemapper. The wider you go, the more blowout with maybe weird hue shift.
value.RENODX_WORKINGCS_HDRTONEMAP.RENODX_CS_BT709   = BT709
value.RENODX_WORKINGCS_HDRTONEMAP.RENODX_CS_BT2020  = BT2020
value.RENODX_WORKINGCS_HDRTONEMAP.RENODX_CS_AP1     = AP1

# RenoDX ACES
option.RENODX_ACES_RGC = Gamut Compression Blowout
option.RENODX_ACES_RGC.comment = Compressed color gamut, causing the ACES hue shift blowout.

option.RENODX_ACES_RRT = Reference Rendering Transform
option.RENODX_ACES_RRT.comment = Prepare colors for Output Device.

option.RENODX_ACES_MIDGRAY = Mid Gray
option.RENODX_ACES_MIDGRAY.comment = Mid gray output target.

# RenoDX GT
option.RENODX_GT_A = Contrast
option.RENODX_GT_A.comment = Effectively pow(color, contrast).

option.RENODX_GT_M = Linear Section Start (Mid Gray)
option.RENODX_GT_M.comment = Aka the end of toe.

option.RENODX_GT_L = Linear Section Length
option.RENODX_GT_L.comment = The length of the linear section before shoulder.

option.RENODX_GT_C = Toe Contrast
option.RENODX_GT_C.comment = How aggressive should toe pull colors to black?

option.RENODX_GT_B = Black Floor Raise
option.RENODX_GT_B.comment = Raises the black floor.

# RenoDX GT7
option.RENODX_GT7_A = White Clip Strength
option.RENODX_GT7_A.comment = Mask to clip highlights.

option.RENODX_GT7_M = Gray Point
option.RENODX_GT7_M.comment = Gray point for toe end.

option.RENODX_GT7_L = Linear Section Length
option.RENODX_GT7_L.comment = The length of the linear section before shoulder.

option.RENODX_GT7_C = Toe Contrast
option.RENODX_GT7_C.comment = How aggressive should toe pull colors to black.

option.RENODX_GT7_BLOW_AMOUNT = Blowout Amount
option.RENODX_GT7_BLOW_AMOUNT.comment = Blend intensity of chroma blowout.

option.RENODX_GT7_BLOW_START = Blowout Start
option.RENODX_GT7_BLOW_START.comment = Start of chroma blowout.

option.RENODX_GT7_BLOW_END = Blowout End
option.RENODX_GT7_BLOW_END.comment = End of chroma blowout.

#Hable
option.RENODX_HABLE_A = Shoulder Strength
option.RENODX_HABLE_B = Linear Section Strength
option.RENODX_HABLE_C = Linear Angle
option.RENODX_HABLE_D = Toe Strength
option.RENODX_HABLE_E = Toe Numerator
option.RENODX_HABLE_F = Toe Denominator
option.RENODX_HABLE_W = White Point
option.RENODX_HABLE_BLOWOUT = Blowout
option.RENODX_HABLE_BLOWOUT.comment = Blend SDR blowout on hightlights.

#Frostbite
option.RENODX_FROSTBITE_SAT = Saturation Boost
option.RENODX_FROSTBITE_HUE = Hue Preservation

#Daniele
option.RENODX_DANIELE_G = Surround/Contrast
option.RENODX_DANIELE_C = Scene-referred Gray
option.RENODX_DANIELE_C_D = Output Gray in nits
option.RENODX_DANIELE_W_G = Gray Change
option.RENODX_DANIELE_T_1 = Shadow Toe
option.RENODX_DANIELE_R_HIT_MIN = Minimum Hit Nits
option.RENODX_DANIELE_R_HIT_MAX = Maximum Hit Nits

#RenderIntermediatePass
option.RENODX_INTER_MODE = Intermediate Pass Mode
option.RENODX_INTER_MODE.comment = Encode color (eotf and colorspace) to what the final output is expected to be. Leave sRGB, unless something is super wrong!
value.RENODX_INTER_MODE.RENODX_INTER_MODE_SRGB = sRGB (BT709)
value.RENODX_INTER_MODE.RENODX_INTER_MODE_SCRGB = scRGB @ 203nits (BT709)
value.RENODX_INTER_MODE.RENODX_INTER_MODE_HDR10 = HDR10 @ 203nits (BT2020)

*/

//////////////////Defines: User
//Main
#define RENODX_ENABLED /* Deprecated */

//Brightness: Display
#define RENODX_BRIGHTNESS_USEREPORTED_OFF 0
#define RENODX_BRIGHTNESS_USEREPORTED_ON 1
#define RENODX_BRIGHTNESS_USEREPORTED RENODX_BRIGHTNESS_USEREPORTED_ON //[RENODX_BRIGHTNESS_USEREPORTED_OFF RENODX_BRIGHTNESS_USEREPORTED_ON]

//case: rrtt169 HDR Mod
#if RENODX_BRIGHTNESS_USEREPORTED == RENODX_BRIGHTNESS_USEREPORTED_ON && defined(HDR_MOD_INSTALLED) && defined(HDR_ENABLED)
  uniform float HdrGamePeakBrightness;
  uniform float HdrGamePaperWhiteBrightness;
  uniform float HdrUIBrightness;
  #define RENODX_PEAK_BRIGHTNESS HdrGamePeakBrightness
  #define RENODX_GAME_BRIGHTNESS HdrGamePaperWhiteBrightness
  #define RENODX_UI_BRIGHTNESS HdrUIBrightness
//case: none
#else
  #define RENODX_UI_BRIGHTNESS 203.0 //[5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0 45.0 50.0 55.0 60.0 65.0 70.0 75.0 80.0 85.0 90.0 95.0 100.0 105.0 110.0 115.0 120.0 125.0 130.0 135.0 140.0 145.0 150.0 155.0 160.0 165.0 170.0 175.0 180.0 185.0 190.0 195.0 200.0 203.0 205.0 210.0 215.0 220.0 225.0 230.0 235.0 240.0 245.0 250.0 255.0 260.0 265.0 270.0 275.0 280.0 285.0 290.0 295.0 300.0 305.0 310.0 315.0 320.0 325.0 330.0 335.0 340.0 345.0 350.0 355.0 360.0 365.0 370.0 375.0 380.0 385.0 390.0 395.0 400.0 405.0 410.0 415.0 420.0 425.0 430.0 435.0 440.0 445.0 450.0 455.0 460.0 465.0 470.0 475.0 480.0 485.0 490.0 495.0 500.0 505.0 510.0 515.0 520.0 525.0 530.0 535.0 540.0 545.0 550.0 555.0 560.0 565.0 570.0 575.0 580.0 585.0 590.0 595.0 600.0 605.0 610.0 615.0 620.0 625.0 630.0 635.0 640.0 645.0 650.0 655.0 660.0 665.0 670.0 675.0 680.0 685.0 690.0 695.0 700.0 705.0 710.0 715.0 720.0 725.0 730.0 735.0 740.0 745.0 750.0 755.0 760.0 765.0 770.0 775.0 780.0 785.0 790.0 795.0 800.0 805.0 810.0 815.0 820.0 825.0 830.0 835.0 840.0 845.0 850.0 855.0 860.0 865.0 870.0 875.0 880.0 885.0 890.0 895.0 900.0 905.0 910.0 915.0 920.0 925.0 930.0 935.0 940.0 945.0 950.0 955.0 960.0 965.0 970.0 975.0 980.0 985.0 990.0 995.0 1000.0]
  #define RENODX_PEAK_BRIGHTNESS 1000.0 //[203.0 210.0 215.0 220.0 225.0 230.0 235.0 240.0 245.0 250.0 255.0 260.0 265.0 270.0 275.0 280.0 285.0 290.0 295.0 300.0 305.0 310.0 315.0 320.0 325.0 330.0 335.0 340.0 345.0 350.0 355.0 360.0 365.0 370.0 375.0 380.0 385.0 390.0 395.0 400.0 405.0 410.0 415.0 420.0 425.0 430.0 435.0 440.0 445.0 450.0 455.0 460.0 465.0 470.0 475.0 480.0 485.0 490.0 495.0 500.0 505.0 510.0 515.0 520.0 525.0 530.0 535.0 540.0 545.0 550.0 555.0 560.0 565.0 570.0 575.0 580.0 585.0 590.0 595.0 600.0 605.0 610.0 615.0 620.0 625.0 630.0 635.0 640.0 645.0 650.0 655.0 660.0 665.0 670.0 675.0 680.0 685.0 690.0 695.0 700.0 705.0 710.0 715.0 720.0 725.0 730.0 735.0 740.0 745.0 750.0 755.0 760.0 765.0 770.0 775.0 780.0 785.0 790.0 795.0 800.0 805.0 810.0 815.0 820.0 825.0 830.0 835.0 840.0 845.0 850.0 855.0 860.0 865.0 870.0 875.0 880.0 885.0 890.0 895.0 900.0 905.0 910.0 915.0 920.0 925.0 930.0 935.0 940.0 945.0 950.0 955.0 960.0 965.0 970.0 975.0 980.0 985.0 990.0 995.0 1000.0 1005.0 1010.0 1015.0 1020.0 1025.0 1030.0 1035.0 1040.0 1045.0 1050.0 1055.0 1060.0 1065.0 1070.0 1075.0 1080.0 1085.0 1090.0 1095.0 1100.0 1105.0 1110.0 1115.0 1120.0 1125.0 1130.0 1135.0 1140.0 1145.0 1150.0 1155.0 1160.0 1165.0 1170.0 1175.0 1180.0 1185.0 1190.0 1195.0 1200.0 1205.0 1210.0 1215.0 1220.0 1225.0 1230.0 1235.0 1240.0 1245.0 1250.0 1255.0 1260.0 1265.0 1270.0 1275.0 1280.0 1285.0 1290.0 1295.0 1300.0 1305.0 1310.0 1315.0 1320.0 1325.0 1330.0 1335.0 1340.0 1345.0 1350.0 1355.0 1360.0 1365.0 1370.0 1375.0 1380.0 1385.0 1390.0 1395.0 1400.0 1405.0 1410.0 1415.0 1420.0 1425.0 1430.0 1435.0 1440.0 1445.0 1450.0 1455.0 1460.0 1465.0 1470.0 1475.0 1480.0 1485.0 1490.0 1495.0 1500.0 1505.0 1510.0 1515.0 1520.0 1525.0 1530.0 1535.0 1540.0 1545.0 1550.0 1555.0 1560.0 1565.0 1570.0 1575.0 1580.0 1585.0 1590.0 1595.0 1600.0 1605.0 1610.0 1615.0 1620.0 1625.0 1630.0 1635.0 1640.0 1645.0 1650.0 1655.0 1660.0 1665.0 1670.0 1675.0 1680.0 1685.0 1690.0 1695.0 1700.0 1705.0 1710.0 1715.0 1720.0 1725.0 1730.0 1735.0 1740.0 1745.0 1750.0 1755.0 1760.0 1765.0 1770.0 1775.0 1780.0 1785.0 1790.0 1795.0 1800.0 1805.0 1810.0 1815.0 1820.0 1825.0 1830.0 1835.0 1840.0 1845.0 1850.0 1855.0 1860.0 1865.0 1870.0 1875.0 1880.0 1885.0 1890.0 1895.0 1900.0 1905.0 1910.0 1915.0 1920.0 1925.0 1930.0 1935.0 1940.0 1945.0 1950.0 1955.0 1960.0 1965.0 1970.0 1975.0 1980.0 1985.0 1990.0 1995.0 2000.0 2005.0 2010.0 2015.0 2020.0 2025.0 2030.0 2035.0 2040.0 2045.0 2050.0 2055.0 2060.0 2065.0 2070.0 2075.0 2080.0 2085.0 2090.0 2095.0 2100.0 2105.0 2110.0 2115.0 2120.0 2125.0 2130.0 2135.0 2140.0 2145.0 2150.0 2155.0 2160.0 2165.0 2170.0 2175.0 2180.0 2185.0 2190.0 2195.0 2200.0 2205.0 2210.0 2215.0 2220.0 2225.0 2230.0 2235.0 2240.0 2245.0 2250.0 2255.0 2260.0 2265.0 2270.0 2275.0 2280.0 2285.0 2290.0 2295.0 2300.0 2305.0 2310.0 2315.0 2320.0 2325.0 2330.0 2335.0 2340.0 2345.0 2350.0 2355.0 2360.0 2365.0 2370.0 2375.0 2380.0 2385.0 2390.0 2395.0 2400.0 2405.0 2410.0 2415.0 2420.0 2425.0 2430.0 2435.0 2440.0 2445.0 2450.0 2455.0 2460.0 2465.0 2470.0 2475.0 2480.0 2485.0 2490.0 2495.0 2500.0 2505.0 2510.0 2515.0 2520.0 2525.0 2530.0 2535.0 2540.0 2545.0 2550.0 2555.0 2560.0 2565.0 2570.0 2575.0 2580.0 2585.0 2590.0 2595.0 2600.0 2605.0 2610.0 2615.0 2620.0 2625.0 2630.0 2635.0 2640.0 2645.0 2650.0 2655.0 2660.0 2665.0 2670.0 2675.0 2680.0 2685.0 2690.0 2695.0 2700.0 2705.0 2710.0 2715.0 2720.0 2725.0 2730.0 2735.0 2740.0 2745.0 2750.0 2755.0 2760.0 2765.0 2770.0 2775.0 2780.0 2785.0 2790.0 2795.0 2800.0 2805.0 2810.0 2815.0 2820.0 2825.0 2830.0 2835.0 2840.0 2845.0 2850.0 2855.0 2860.0 2865.0 2870.0 2875.0 2880.0 2885.0 2890.0 2895.0 2900.0 2905.0 2910.0 2915.0 2920.0 2925.0 2930.0 2935.0 2940.0 2945.0 2950.0 2955.0 2960.0 2965.0 2970.0 2975.0 2980.0 2985.0 2990.0 2995.0 3000.0 3005.0 3010.0 3015.0 3020.0 3025.0 3030.0 3035.0 3040.0 3045.0 3050.0 3055.0 3060.0 3065.0 3070.0 3075.0 3080.0 3085.0 3090.0 3095.0 3100.0 3105.0 3110.0 3115.0 3120.0 3125.0 3130.0 3135.0 3140.0 3145.0 3150.0 3155.0 3160.0 3165.0 3170.0 3175.0 3180.0 3185.0 3190.0 3195.0 3200.0 3205.0 3210.0 3215.0 3220.0 3225.0 3230.0 3235.0 3240.0 3245.0 3250.0 3255.0 3260.0 3265.0 3270.0 3275.0 3280.0 3285.0 3290.0 3295.0 3300.0 3305.0 3310.0 3315.0 3320.0 3325.0 3330.0 3335.0 3340.0 3345.0 3350.0 3355.0 3360.0 3365.0 3370.0 3375.0 3380.0 3385.0 3390.0 3395.0 3400.0 3405.0 3410.0 3415.0 3420.0 3425.0 3430.0 3435.0 3440.0 3445.0 3450.0 3455.0 3460.0 3465.0 3470.0 3475.0 3480.0 3485.0 3490.0 3495.0 3500.0 3505.0 3510.0 3515.0 3520.0 3525.0 3530.0 3535.0 3540.0 3545.0 3550.0 3555.0 3560.0 3565.0 3570.0 3575.0 3580.0 3585.0 3590.0 3595.0 3600.0 3605.0 3610.0 3615.0 3620.0 3625.0 3630.0 3635.0 3640.0 3645.0 3650.0 3655.0 3660.0 3665.0 3670.0 3675.0 3680.0 3685.0 3690.0 3695.0 3700.0 3705.0 3710.0 3715.0 3720.0 3725.0 3730.0 3735.0 3740.0 3745.0 3750.0 3755.0 3760.0 3765.0 3770.0 3775.0 3780.0 3785.0 3790.0 3795.0 3800.0 3805.0 3810.0 3815.0 3820.0 3825.0 3830.0 3835.0 3840.0 3845.0 3850.0 3855.0 3860.0 3865.0 3870.0 3875.0 3880.0 3885.0 3890.0 3895.0 3900.0 3905.0 3910.0 3915.0 3920.0 3925.0 3930.0 3935.0 3940.0 3945.0 3950.0 3955.0 3960.0 3965.0 3970.0 3975.0 3980.0 3985.0 3990.0 3995.0 4000.0 5000.0 6000.0 7000.0 8000.0 9000.0 10000.0]
  #define RENODX_GAME_BRIGHTNESS 203.0 //[5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0 45.0 50.0 55.0 60.0 65.0 70.0 75.0 80.0 85.0 90.0 95.0 100.0 105.0 110.0 115.0 120.0 125.0 130.0 135.0 140.0 145.0 150.0 155.0 160.0 165.0 170.0 175.0 180.0 185.0 190.0 195.0 200.0 203.0 205.0 210.0 215.0 220.0 225.0 230.0 235.0 240.0 245.0 250.0 255.0 260.0 265.0 270.0 275.0 280.0 285.0 290.0 295.0 300.0 305.0 310.0 315.0 320.0 325.0 330.0 335.0 340.0 345.0 350.0 355.0 360.0 365.0 370.0 375.0 380.0 385.0 390.0 395.0 400.0 405.0 410.0 415.0 420.0 425.0 430.0 435.0 440.0 445.0 450.0 455.0 460.0 465.0 470.0 475.0 480.0 485.0 490.0 495.0 500.0 505.0 510.0 515.0 520.0 525.0 530.0 535.0 540.0 545.0 550.0 555.0 560.0 565.0 570.0 575.0 580.0 585.0 590.0 595.0 600.0 605.0 610.0 615.0 620.0 625.0 630.0 635.0 640.0 645.0 650.0 655.0 660.0 665.0 670.0 675.0 680.0 685.0 690.0 695.0 700.0 705.0 710.0 715.0 720.0 725.0 730.0 735.0 740.0 745.0 750.0 755.0 760.0 765.0 770.0 775.0 780.0 785.0 790.0 795.0 800.0 805.0 810.0 815.0 820.0 825.0 830.0 835.0 840.0 845.0 850.0 855.0 860.0 865.0 870.0 875.0 880.0 885.0 890.0 895.0 900.0 905.0 910.0 915.0 920.0 925.0 930.0 935.0 940.0 945.0 950.0 955.0 960.0 965.0 970.0 975.0 980.0 985.0 990.0 995.0 1000.0]
#endif

//Brightness: Other
#define RENODX_SHOULDER_START 36.0 //[0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0 31.0 32.0 33.0 34.0 35.0 36.0 37.0 38.0 39.0 40.0 41.0 42.0 43.0 44.0 45.0 46.0 47.0 48.0 49.0 50.0 51.0 52.0 53.0 54.0 55.0 56.0 57.0 58.0 59.0 60.0 61.0 62.0 63.0 64.0 65.0 66.0 67.0 68.0 69.0 70.0 71.0 72.0 73.0 74.0 75.0 76.0 77.0 78.0 79.0 80.0 81.0 82.0 83.0 84.0 85.0 86.0 87.0 88.0 89.0 90.0 91.0 92.0 93.0 94.0 95.0 96.0 97.0 98.0 99.0 100.0 101.0 102.0 103.0 104.0 105.0 106.0 107.0 108.0 109.0 110.0 111.0 112.0 113.0 114.0 115.0 116.0 117.0 118.0 119.0 120.0 121.0 122.0 123.0 124.0 125.0 126.0 127.0 128.0 129.0 130.0 131.0 132.0 133.0 134.0 135.0 136.0 137.0 138.0 139.0 140.0 141.0 142.0 143.0 144.0 145.0 146.0 147.0 148.0 149.0 150.0 151.0 152.0 153.0 154.0 155.0 156.0 157.0 158.0 159.0 160.0 161.0 162.0 163.0 164.0 165.0 166.0 167.0 168.0 169.0 170.0 171.0 172.0 173.0 174.0 175.0 176.0 177.0 178.0 179.0 180.0 181.0 182.0 183.0 184.0 185.0 186.0 187.0 188.0 189.0 190.0 191.0 192.0 193.0 194.0 195.0 196.0 197.0 198.0 199.0 200.0 201.0 202.0 203.0 204.0 205.0 206.0 207.0 208.0 209.0 210.0 211.0 212.0 213.0 214.0 215.0 216.0 217.0 218.0 219.0 220.0 221.0 222.0 223.0 224.0 225.0 226.0 227.0 228.0 229.0 230.0 231.0 232.0 233.0 234.0 235.0 236.0 237.0 238.0 239.0 240.0 241.0 242.0 243.0 244.0 245.0 246.0 247.0 248.0 249.0 250.0 251.0 252.0 253.0 254.0 255.0 256.0 257.0 258.0 259.0 260.0 261.0 262.0 263.0 264.0 265.0 266.0 267.0 268.0 269.0 270.0 271.0 272.0 273.0 274.0 275.0 276.0 277.0 278.0 279.0 280.0 281.0 282.0 283.0 284.0 285.0 286.0 287.0 288.0 289.0 290.0 291.0 292.0 293.0 294.0 295.0 296.0 297.0 298.0 299.0 300.0 301.0 302.0 303.0 304.0 305.0 306.0 307.0 308.0 309.0 310.0 311.0 312.0 313.0 314.0 315.0 316.0 317.0 318.0 319.0 320.0 321.0 322.0 323.0 324.0 325.0 326.0 327.0 328.0 329.0 330.0 331.0 332.0 333.0 334.0 335.0 336.0 337.0 338.0 339.0 340.0 341.0 342.0 343.0 344.0 345.0 346.0 347.0 348.0 349.0 350.0 351.0 352.0 353.0 354.0 355.0 356.0 357.0 358.0 359.0 360.0 361.0 362.0 363.0 364.0 365.0 366.0 367.0 368.0 369.0 370.0 371.0 372.0 373.0 374.0 375.0 376.0 377.0 378.0 379.0 380.0 381.0 382.0 383.0 384.0 385.0 386.0 387.0 388.0 389.0 390.0 391.0 392.0 393.0 394.0 395.0 396.0 397.0 398.0 399.0 400.0 401.0 402.0 403.0 404.0 405.0 406.0 407.0 408.0 409.0 410.0 411.0 412.0 413.0 414.0 415.0 416.0 417.0 418.0 419.0 420.0 421.0 422.0 423.0 424.0 425.0 426.0 427.0 428.0 429.0 430.0 431.0 432.0 433.0 434.0 435.0 436.0 437.0 438.0 439.0 440.0 441.0 442.0 443.0 444.0 445.0 446.0 447.0 448.0 449.0 450.0 451.0 452.0 453.0 454.0 455.0 456.0 457.0 458.0 459.0 460.0 461.0 462.0 463.0 464.0 465.0 466.0 467.0 468.0 469.0 470.0 471.0 472.0 473.0 474.0 475.0 476.0 477.0 478.0 479.0 480.0 481.0 482.0 483.0 484.0 485.0 486.0 487.0 488.0 489.0 490.0 491.0 492.0 493.0 494.0 495.0 496.0 497.0 498.0 499.0 500.0]
#define RENODX_WHITE_CLIP 15000.0 //[1000.0 1100.0 1200.0 1300.0 1400.0 1500.0 1600.0 1700.0 1800.0 1900.0 2000.0 2100.0 2200.0 2300.0 2400.0 2500.0 2600.0 2700.0 2800.0 2900.0 3000.0 3100.0 3200.0 3300.0 3400.0 3500.0 3600.0 3700.0 3800.0 3900.0 4000.0 4100.0 4200.0 4300.0 4400.0 4500.0 4600.0 4700.0 4800.0 4900.0 5000.0 5100.0 5200.0 5300.0 5400.0 5500.0 5600.0 5700.0 5800.0 5900.0 6000.0 6100.0 6200.0 6300.0 6400.0 6500.0 6600.0 6700.0 6800.0 6900.0 7000.0 7100.0 7200.0 7300.0 7400.0 7500.0 7600.0 7700.0 7800.0 7900.0 8000.0 8100.0 8200.0 8300.0 8400.0 8500.0 8600.0 8700.0 8800.0 8900.0 9000.0 9100.0 9200.0 9300.0 9400.0 9500.0 9600.0 9700.0 9800.0 9900.0 10000.0 10100.0 10200.0 10300.0 10400.0 10500.0 10600.0 10700.0 10800.0 10900.0 11000.0 11100.0 11200.0 11300.0 11400.0 11500.0 11600.0 11700.0 11800.0 11900.0 12000.0 12100.0 12200.0 12300.0 12400.0 12500.0 12600.0 12700.0 12800.0 12900.0 13000.0 13100.0 13200.0 13300.0 13400.0 13500.0 13600.0 13700.0 13800.0 13900.0 14000.0 14100.0 14200.0 14300.0 14400.0 14500.0 14600.0 14700.0 14800.0 14900.0 15000.0 15100.0 15200.0 15300.0 15400.0 15500.0 15600.0 15700.0 15800.0 15900.0 16000.0 16100.0 16200.0 16300.0 16400.0 16500.0 16600.0 16700.0 16800.0 16900.0 17000.0 17100.0 17200.0 17300.0 17400.0 17500.0 17600.0 17700.0 17800.0 17900.0 18000.0 18100.0 18200.0 18300.0 18400.0 18500.0 18600.0 18700.0 18800.0 18900.0 19000.0 19100.0 19200.0 19300.0 19400.0 19500.0 19600.0 19700.0 19800.0 19900.0 20000.0 20100.0 20200.0 20300.0 20400.0 20500.0 20600.0 20700.0 20800.0 20900.0 21000.0 21100.0 21200.0 21300.0 21400.0 21500.0 21600.0 21700.0 21800.0 21900.0 22000.0 22100.0 22200.0 22300.0 22400.0 22500.0 22600.0 22700.0 22800.0 22900.0 23000.0 23100.0 23200.0 23300.0 23400.0 23500.0 23600.0 23700.0 23800.0 23900.0 24000.0 24100.0 24200.0 24300.0 24400.0 24500.0 24600.0 24700.0 24800.0 24900.0 25000.0 25100.0 25200.0 25300.0 25400.0 25500.0 25600.0 25700.0 25800.0 25900.0 26000.0 26100.0 26200.0 26300.0 26400.0 26500.0 26600.0 26700.0 26800.0 26900.0 27000.0 27100.0 27200.0 27300.0 27400.0 27500.0 27600.0 27700.0 27800.0 27900.0 28000.0 28100.0 28200.0 28300.0 28400.0 28500.0 28600.0 28700.0 28800.0 28900.0 29000.0 29100.0 29200.0 29300.0 29400.0 29500.0 29600.0 29700.0 29800.0 29900.0 30000.0 30100.0 30200.0 30300.0 30400.0 30500.0 30600.0 30700.0 30800.0 30900.0 31000.0 31100.0 31200.0 31300.0 31400.0 31500.0 31600.0 31700.0 31800.0 31900.0 32000.0 32100.0 32200.0 32300.0 32400.0 32500.0 32600.0 32700.0 32800.0 32900.0 33000.0 33100.0 33200.0 33300.0 33400.0 33500.0 33600.0 33700.0 33800.0 33900.0 34000.0 34100.0 34200.0 34300.0 34400.0 34500.0 34600.0 34700.0 34800.0 34900.0 35000.0 35100.0 35200.0 35300.0 35400.0 35500.0 35600.0 35700.0 35800.0 35900.0 36000.0 36100.0 36200.0 36300.0 36400.0 36500.0 36600.0 36700.0 36800.0 36900.0 37000.0 37100.0 37200.0 37300.0 37400.0 37500.0 37600.0 37700.0 37800.0 37900.0 38000.0 38100.0 38200.0 38300.0 38400.0 38500.0 38600.0 38700.0 38800.0 38900.0 39000.0 39100.0 39200.0 39300.0 39400.0 39500.0 39600.0 39700.0 39800.0 39900.0 40000.0]

//Upgrade
#define RENODX_UPGRADE_AMOUNT 1.0 //[0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define RENODX_UPGRADE_AUTO 0.0 //[0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]

//ApplyPerChannelCorrection
#define RENODX_APPC_AMOUNT 65 //[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 100]
#define RENODX_APPC_POW 0.00 //[0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90 1.95 2.00 2.05 2.10 2.15 2.20 2.25 2.30 2.35 2.40 2.45 2.50 2.55 2.60 2.65 2.70 2.75 2.80 2.85 2.90 2.95 3.00]

//Debug
#define RENODX_DEBUG_NONE 0
#define RENODX_DEBUG_UNTONEMAPPED 1
#define RENODX_DEBUG_TONEMAPPED 2
#define RENODX_DEBUG_SPLIT 3
#define RENODX_DEBUG RENODX_DEBUG_NONE //[RENODX_DEBUG_NONE RENODX_DEBUG_UNTONEMAPPED RENODX_DEBUG_TONEMAPPED RENODX_DEBUG_SPLIT]

//Color Spaces
#define RENODX_CS_NONE -1
#define RENODX_CS_BT709 0
#define RENODX_CS_BT2020 1
#define RENODX_CS_AP1 2

//Color Grade
#define RENODX_COLORGRADE_HIGHLIGHTS 100 //[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200]
#define RENODX_COLORGRADE_HIGHLIGHTS_MID 0.36 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_COLORGRADE_SHADOWS 100 //[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200]
#define RENODX_COLORGRADE_SHADOWS_MID 0.36 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_COLORGRADE_CONTRAST 100 //[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200]
#define RENODX_COLORGRADE_CONTRAST_MID 0.36 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_COLORGRADE_SATURATION 100 //[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200]
#if RENODX_COLORGRADE_HIGHLIGHTS != 100 || RENODX_COLORGRADE_SHADOWS != 100 || RENODX_COLORGRADE_CONTRAST != 100
  #define RENODX_COLORGRADE_ENABLED 1
#else
  #define RENODX_COLORGRADE_ENABLED 0
#endif

//Gamma
#define RENODX_GAMMA_NONE -1
#define RENODX_GAMMA_SRGB 0
#define RENODX_GAMMA_20 1
#define RENODX_GAMMA_22 2
#define RENODX_GAMMA_24 3

//RCAS
#define RENODX_RCAS 0 //[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100]
//#define RENODX_RCAS_DENOISE

//HDRTONEMAP

#define RENODX_SCALING_Y 0
#define RENODX_SCALING_MAXCHANNEL 1
#define RENODX_SCALING_PERCHANNEL 2
#ifdef RENODX_UPGRADE_ENABLED
  #define RENODX_SCALING_AUTO RENODX_SCALING_MAXCHANNEL
#else
  #define RENODX_SCALING_AUTO RENODX_SCALING_PERCHANNEL
#endif 
#define RENODX_SCALING RENODX_SCALING_AUTO //[RENODX_SCALING_AUTO RENODX_SCALING_Y RENODX_SCALING_PERCHANNEL RENODX_SCALING_MAXCHANNEL]

#define RENODX_EXPOSURE 1.00 //[0.00 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00 1.01 1.02 1.03 1.04 1.05 1.06 1.07 1.08 1.09 1.10 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.20 1.21 1.22 1.23 1.24 1.25 1.26 1.27 1.28 1.29 1.30 1.31 1.32 1.33 1.34 1.35 1.36 1.37 1.38 1.39 1.40 1.41 1.42 1.43 1.44 1.45 1.46 1.47 1.48 1.49 1.50 1.51 1.52 1.53 1.54 1.55 1.56 1.57 1.58 1.59 1.60 1.61 1.62 1.63 1.64 1.65 1.66 1.67 1.68 1.69 1.70 1.71 1.72 1.73 1.74 1.75 1.76 1.77 1.78 1.79 1.80 1.81 1.82 1.83 1.84 1.85 1.86 1.87 1.88 1.89 1.90 1.91 1.92 1.93 1.94 1.95 1.96 1.97 1.98 1.99 2.00 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0]
#define RENODX_GAMMACORRECTION RENODX_GAMMA_NONE //[RENODX_GAMMA_NONE RENODX_GAMMA_22 RENODX_GAMMA_24]

#define RENODX_HDRTONEMAP_TYPE_OFF 0
#define RENODX_HDRTONEMAP_TYPE_REINHARD 1
#define RENODX_HDRTONEMAP_TYPE_ACES 2
#define RENODX_HDRTONEMAP_TYPE_GT 3
#define RENODX_HDRTONEMAP_TYPE_HERMITE 4
#define RENODX_HDRTONEMAP_TYPE_GT7 5
#define RENODX_HDRTONEMAP_TYPE_HABLE 6
#define RENODX_HDRTONEMAP_TYPE_EXPROLL 7
#define RENODX_HDRTONEMAP_TYPE_CUSTOM1 8
#define RENODX_HDRTONEMAP_TYPE_NEUTWO 9
#define RENODX_HDRTONEMAP_TYPE_FROSTBITE 10
#define RENODX_HDRTONEMAP_TYPE_DANIELE 11
#define RENODX_HDRTONEMAP_TYPE_PSYCHO11 12

//Tonemap: Reinhard
//nothing 

//Tonemap: ACES
#define RENODX_ACES_RGC
#define RENODX_ACES_RRT
#define RENODX_ACES_MIDGRAY 0.18 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]

//Tonemap: GT
#define RENODX_GT_A 1.0 //[0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0]
#define RENODX_GT_M 0.22 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_GT_L 0.40 //[0.00 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99]
#define RENODX_GT_C 1.15 //[1.00 1.01 1.02 1.03 1.04 1.05 1.06 1.07 1.08 1.09 1.10 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.20 1.21 1.22 1.23 1.24 1.25 1.26 1.27 1.28 1.29 1.30 1.31 1.32 1.33 1.34 1.35 1.36 1.37 1.38 1.39 1.40 1.41 1.42 1.43 1.44 1.45 1.46 1.47 1.48 1.49 1.50 1.51 1.52 1.53 1.54 1.55 1.56 1.57 1.58 1.59 1.60 1.61 1.62 1.63 1.64 1.65 1.66 1.67 1.68 1.69 1.70 1.71 1.72 1.73 1.74 1.75 1.76 1.77 1.78 1.79 1.80 1.81 1.82 1.83 1.84 1.85 1.86 1.87 1.88 1.89 1.90 1.91 1.92 1.93 1.94 1.95 1.96 1.97 1.98 1.99 2.00]
#define RENODX_GT_B 0.000 //[0.000 0.001 0.002 0.003 0.004 0.005 0.006 0.007 0.008 0.009 0.010 0.011 0.012 0.013 0.014 0.015 0.016 0.017 0.018 0.019 0.020 0.021 0.022 0.023 0.024 0.025 0.026 0.027 0.028 0.029 0.030 0.031 0.032 0.033 0.034 0.035 0.036 0.037 0.038 0.039 0.040 0.041 0.042 0.043 0.044 0.045 0.046 0.047 0.048 0.049 0.050 0.051 0.052 0.053 0.054 0.055 0.056 0.057 0.058 0.059 0.060 0.061 0.062 0.063 0.064 0.065 0.066 0.067 0.068 0.069 0.070 0.071 0.072 0.073 0.074 0.075 0.076 0.077 0.078 0.079 0.080 0.081 0.082 0.083 0.084 0.085 0.086 0.087 0.088 0.089 0.090 0.091 0.092 0.093 0.094 0.095 0.096 0.097 0.098 0.099 0.100 0.101 0.102 0.103 0.104 0.105 0.106 0.107 0.108 0.109 0.110 0.111 0.112 0.113 0.114 0.115 0.116 0.117 0.118 0.119 0.120 0.121 0.122 0.123 0.124 0.125 0.126 0.127 0.128 0.129 0.130 0.131 0.132 0.133 0.134 0.135 0.136 0.137 0.138 0.139 0.140 0.141 0.142 0.143 0.144 0.145 0.146 0.147 0.148 0.149 0.150 0.151 0.152 0.153 0.154 0.155 0.156 0.157 0.158 0.159 0.160 0.161 0.162 0.163 0.164 0.165 0.166 0.167 0.168 0.169 0.170 0.171 0.172 0.173 0.174 0.175 0.176 0.177 0.178 0.179 0.180 0.181 0.182 0.183 0.184 0.185 0.186 0.187 0.188 0.189 0.190 0.191 0.192 0.193 0.194 0.195 0.196 0.197 0.198 0.199 0.200]

//Tonemap: GT7
#define RENODX_GT7_A 0.01 //[0.01 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95]
#define RENODX_GT7_M 0.18 //[0.10 0.15 0.18 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.538 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95]
#define RENODX_GT7_L 0.444 //[0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.444 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95]
#define RENODX_GT7_C 1.10 //[1.00 1.05 1.10 1.15 1.20 1.25 1.280 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90]
#define RENODX_GT7_BLOW_AMOUNT 0.6 //[0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define RENODX_GT7_BLOW_START 0.98 //[0.50 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 0.98 1.00 1.05 1.10 1.15 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90]
#define RENODX_GT7_BLOW_END 1.60 //[0.50 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.16 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90 1.95 2.00 2.05 2.10 2.15 2.20 2.25 2.30 2.35 2.40 2.45 2.50] 

//Tonemap: Hable
#define RENODX_HABLE_A 0.22 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_HABLE_B 0.30 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_HABLE_C 0.10 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_HABLE_D 0.20 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_HABLE_E 0.01 //[0.001 0.002 0.003 0.004 0.005 0.006 0.007 0.008 0.009 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50]
#define RENODX_HABLE_F 0.30 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_HABLE_W 11.00 //[0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90 1.95 2.00 2.05 2.10 2.15 2.20 2.25 2.30 2.35 2.40 2.45 2.50 2.55 2.60 2.65 2.70 2.75 2.80 2.85 2.90 2.95 3.00 3.05 3.10 3.15 3.20 3.25 3.30 3.35 3.40 3.45 3.50 3.55 3.60 3.65 3.70 3.75 3.80 3.85 3.90 3.95 4.00 4.05 4.10 4.15 4.20 4.25 4.30 4.35 4.40 4.45 4.50 4.55 4.60 4.65 4.70 4.75 4.80 4.85 4.90 4.95 5.00 5.05 5.10 5.15 5.20 5.25 5.30 5.35 5.40 5.45 5.50 5.55 5.60 5.65 5.70 5.75 5.80 5.85 5.90 5.95 6.00 6.05 6.10 6.15 6.20 6.25 6.30 6.35 6.40 6.45 6.50 6.55 6.60 6.65 6.70 6.75 6.80 6.85 6.90 6.95 7.00 7.05 7.10 7.15 7.20 7.25 7.30 7.35 7.40 7.45 7.50 7.55 7.60 7.65 7.70 7.75 7.80 7.85 7.90 7.95 8.00 8.05 8.10 8.15 8.20 8.25 8.30 8.35 8.40 8.45 8.50 8.55 8.60 8.65 8.70 8.75 8.80 8.85 8.90 8.95 9.00 9.05 9.10 9.15 9.20 9.25 9.30 9.35 9.40 9.45 9.50 9.55 9.60 9.65 9.70 9.75 9.80 9.85 9.90 9.95 10.00 10.05 10.10 10.15 10.20 10.25 10.30 10.35 10.40 10.45 10.50 10.55 10.60 10.65 10.70 10.75 10.80 10.85 10.90 10.95 11.00 11.05 11.10 11.15 11.20 11.25 11.30 11.35 11.40 11.45 11.50 11.55 11.60 11.65 11.70 11.75 11.80 11.85 11.90 11.95 12.00 12.05 12.10 12.15 12.20 12.25 12.30 12.35 12.40 12.45 12.50 12.55 12.60 12.65 12.70 12.75 12.80 12.85 12.90 12.95 13.00 13.05 13.10 13.15 13.20 13.25 13.30 13.35 13.40 13.45 13.50 13.55 13.60 13.65 13.70 13.75 13.80 13.85 13.90 13.95 14.00 14.05 14.10 14.15 14.20 14.25 14.30 14.35 14.40 14.45 14.50 14.55 14.60 14.65 14.70 14.75 14.80 14.85 14.90 14.95 15.00 15.05 15.10 15.15 15.20 15.25 15.30 15.35 15.40 15.45 15.50 15.55 15.60 15.65 15.70 15.75 15.80 15.85 15.90 15.95 16.00 16.05 16.10 16.15 16.20 16.25 16.30 16.35 16.40 16.45 16.50 16.55 16.60 16.65 16.70 16.75 16.80 16.85 16.90 16.95 17.00 17.05 17.10 17.15 17.20 17.25 17.30 17.35 17.40 17.45 17.50 17.55 17.60 17.65 17.70 17.75 17.80 17.85 17.90 17.95 18.00 18.05 18.10 18.15 18.20 18.25 18.30 18.35 18.40 18.45 18.50 18.55 18.60 18.65 18.70 18.75 18.80 18.85 18.90 18.95 19.00 19.05 19.10 19.15 19.20 19.25 19.30 19.35 19.40 19.45 19.50 19.55 19.60 19.65 19.70 19.75 19.80 19.85 19.90 19.95 20.00]

//Tonemap: Exponential Rolloff
//nothing

//Tonemap: NeuTwo
//nothing

//Tonemap: Frostbite
#define RENODX_FROSTBITE_SAT 1.00 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]
#define RENODX_FROSTBITE_HUE 1.00 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80 0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00]

//Tonemap: Daniele
#define RENODX_DANIELE_G 1.1 //[0.01 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20 1.25 1.280 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90]
#define RENODX_DANIELE_C 0.18 //[0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20]
#define RENODX_DANIELE_C_D 10.0 //[0.01 0.1 0.5 1.0 2.0 5.0 10.0 20.0 50.0 100.0 200.0 500.0 1000.0]
#define RENODX_DANIELE_W_G 10.013 //[0.01 0.1 0.5 1.0 2.0 5.0 10.013 20.0 50.0 100.0 200.0 500.0 1000.0]
#define RENODX_DANIELE_T_1 0.01 //[0.001 0.005 0.01 0.05 0.1 0.5 1.0 5.0 10.0]
#define RENODX_DANIELE_R_HIT_MIN 128.0 //[0.01 0.1 1.0 10.0 50.0 100.0 128.0 150.0 200.0 256.0 300.0 500.0 1000.0]
#define RENODX_DANIELE_R_HIT_MAX 256.0 //[0.01 0.1 1.0 10.0 50.0 100.0 128.0 150.0 200.0 256.0 300.0 500.0 1000.0]

//RenderIntermediatePass
#define RENODX_INTER_MODE RENODX_INTER_MODE_SRGB //[RENODX_INTER_MODE_SRGB RENODX_INTER_MODE_HDR10 RENODX_INTER_MODE_SCRGB]
#define RENODX_INTER_MODE_SRGB 1
#define RENODX_INTER_MODE_HDR10 2
#define RENODX_INTER_MODE_SCRGB 3

//https://github.com/clshortfuse/renodx/tree/main/src/shaders 
//https://github.com/clshortfuse/renodx/blob/15b815062f31695be78d7746ec03f9028d724549/src/games/indygreatcircle/include/common.glsl#L4

// Mats (Mat * Color) ///////////////////////////////////////////////////////////////////////////
const mat3 BT709_TO_BT2020_MAT = mat3(
    vec3(0.6274039149284363, 0.06909728795289993, 0.0163914393633604),
    vec3(0.3292830288410187, 0.9195404052734375, 0.08801330626010895),
    vec3(0.04331306740641594, 0.01136231515556574, 0.8955952525138855));

const mat3 BT2020_TO_BT709_MAT = mat3(
    vec3(1.6604909896850586, -0.12455047667026520, -0.01815076358616352),
    vec3(-0.5876411199569702, 1.1328998804092407, -0.10057889670133591),
    vec3(-0.07284986227750778, -0.00834942236542702, 1.1187297105789185));

// color conversion matrices
const mat3 BT709_TO_XYZ_MAT = mat3(
    vec3(0.4123907993, 0.2126390059, 0.0193308187),
    vec3(0.3575843394, 0.7151686788, 0.1191947798),
    vec3(0.1804807884, 0.0721923154, 0.9505321522));

const mat3 XYZ_TO_BT709_MAT = mat3(
    vec3(3.2409699419, -0.9692436363, 0.0556300797),
    vec3(-1.5373831776, 1.8759675015, -0.2039769589),
    vec3(-0.4986107603, 0.0415550574, 1.0569715142));

const mat3 BT2020_TO_XYZ_MAT = mat3(
    vec3(0.6369580483, 0.2627002120, 0.0000000000),
    vec3(0.1446169036, 0.6779980715, 0.0280726930),
    vec3(0.1688809752, 0.0593017165, 1.0609850577));

const mat3 XYZ_TO_BT2020_MAT = mat3(
    vec3(1.7166511880, -0.6666843518, 0.0176398574),
    vec3(-0.3556707838, 1.6164812366, -0.0427706133),
    vec3(-0.2533662814, 0.0157685458, 0.9421031212));

const mat3 AP0_TO_XYZ_MAT = mat3(
    vec3(0.9525523959, 0.3439664498, 0.0000000000),
    vec3(0.0000000000, 0.7281660966, 0.0000000000),
    vec3(0.0000936786, -0.0721325464, 1.0088251844));

const mat3 XYZ_TO_AP0_MAT = mat3(
    vec3(1.0498110175, -0.4959030231, 0.0000000000),
    vec3(0.0000000000, 1.3733130458, 0.0000000000),
    vec3(-0.0000974845, 0.0982400361, 0.9912520182));

const mat3 AP1_TO_XYZ_MAT = mat3(
    vec3(0.6624541811, 0.2722287168, -0.0055746495),
    vec3(0.1340042065, 0.6740817658, 0.0040607335),
    vec3(0.1561876870, 0.0536895174, 1.0103391003));

const mat3 XYZ_TO_AP1_MAT = mat3(
    vec3(1.6410233797, -0.6636628587, 0.0117218943),
    vec3(-0.3248032942, 1.6153315917, -0.0082844420),
    vec3(-0.2364246952, 0.0167563477, 0.9883948585));

// With Bradford
const mat3 AP1_TO_BT709_MAT = mat3(
    vec3(1.7050509927, -0.1302564175, -0.0240033568),
    vec3(-0.6217921207, 1.1408047366, -0.1289689761),
    vec3(-0.0832588720, -0.0105483191, 1.1529723329));

// With Bradford
const mat3 AP1_TO_BT2020_MAT = mat3(
    vec3(1.0258247477, -0.0022343695, -0.0050133515),
    vec3(-0.0200531908, 1.0045865019, -0.0252900718),
    vec3(-0.0057715568, -0.0023521324, 1.0303034233));

const mat3 BT709_TO_AP1_MAT = mat3(
    vec3(0.6130974024, 0.0701937225, 0.0206155929),
    vec3(0.3395231462, 0.9163538791, 0.1095697729),
    vec3(0.0473794514, 0.0134523985, 0.8698146342));

// With Bradford
const mat3 BT2020_TO_AP1_MAT = mat3(
    vec3(0.9748949779, 0.0021795628, 0.0047972397),
    vec3(0.0195991086, 0.9955354689, 0.0245320166),
    vec3(0.0055059134, 0.0022849683, 0.9706707437));

// chromatic adaptation method: von Kries
// chromatic adaptation transform: Bradford
const mat3 D65_TO_D60_CAT = mat3(
    vec3(1.01303493, 0.00769822997, -0.00284131732),
    vec3(0.00610525766, 0.998163342, 0.00468515651),
    vec3(-0.0149709433, -0.00503203831, 0.924506127));

// chromatic adaptation method: von Kries
// chromatic adaptation transform: Bradford
const mat3 D60_TO_D65_MAT = mat3(
    vec3(0.987223982, -0.00759837171, 0.00307257706),
    vec3(-0.00611322838, 1.00186145, -0.00509596150),
    vec3(0.0159532874, 0.00533003592, 1.08168065));

const mat3 AP1_TO_BT709D60_MAT = XYZ_TO_BT709_MAT * AP1_TO_XYZ_MAT;
const mat3 AP1_TO_BT2020D60_MAT = XYZ_TO_BT2020_MAT * AP1_TO_XYZ_MAT;
const mat3 AP1_TO_AP1D65_MAT = XYZ_TO_AP1_MAT * (D60_TO_D65_MAT * AP1_TO_XYZ_MAT);

const mat3 BT709_TO_AP0_MAT = XYZ_TO_AP0_MAT * (D65_TO_D60_CAT * BT709_TO_XYZ_MAT);

const mat3 AP0_TO_AP1_MAT = XYZ_TO_AP1_MAT * AP0_TO_XYZ_MAT;
const mat3 AP1_TO_AP0_MAT = XYZ_TO_AP0_MAT * AP1_TO_XYZ_MAT;

const mat3 RRT_SAT_MAT = mat3(
    vec3(0.9708890, 0.0108892, 0.0108892),
    vec3(0.0269633, 0.9869630, 0.0269633),
    vec3(0.00214758, 0.00214758, 0.96214800));

const mat3 ODT_SAT_MAT = mat3(
    vec3(0.949056, 0.019056, 0.019056),
    vec3(0.0471857, 0.9771860, 0.0471857),
    vec3(0.00375827, 0.00375827, 0.93375800));

const mat3 M_renodx = mat3(
    vec3(0.5, -1.0, 0.5),
    vec3(-1.0, 1.0, 0.5),
    vec3(0.5, 0.0, 0.0));

// Y ///////////////////////////////////////////////////////////////////////////
//const mat3 BT709_TO_XYZ_MAT = mat3(
//    0.4123907993f, 0.3575843394f, 0.1804807884f, 
//    0.2126390059f, 0.7151686788f, 0.0721923154f,
//    0.0193308187f, 0.1191947798f, 0.9505321522f
//);
const vec3 BT709_TO_Y_VEC = vec3(0.2126390059f, 0.7151686788f, 0.0721923154f);
const vec3 BT2020_TO_Y_VEC = vec3( 0.2627002120f, 0.6779980715f, 0.0593017165f);
const vec3 AP1_TO_Y_VEC = vec3(0.2722287168f, 0.6740817658f, 0.0536895174f);

float YFromBT709(vec3 x) {
  return dot(x, BT709_TO_Y_VEC);
}
float YFromBT2020(vec3 x) {
  return dot(x, BT2020_TO_Y_VEC);
}
float YFromAP1(vec3 x) {
  return dot(x, AP1_TO_Y_VEC);
}

float YFromCS(vec3 x, int cs) {
  if (cs == RENODX_CS_BT709) return YFromBT709(x);
  else if (cs == RENODX_CS_BT2020) return YFromBT2020(x);
  else if (cs == RENODX_CS_AP1) return YFromAP1(x);
  return 0.0;
}

float YOrMaxChannelForHDRTonemap(vec3 x, int cs) {
  #if RENODX_SCALING == RENODX_SCALING_Y
    return YFromCS(x, cs);
  #elif RENODX_SCALING_MAXCHANNEL
    return max(x.x, max(x.y, x.z));
  #endif
}
float YOrMaxChannelForHDRTonemap(vec3 x) {
  return YOrMaxChannelForHDRTonemap(x, RENODX_WORKINGCS_HDRTONEMAP);
}

// Inv Lerp / Rescale //////////////////////////////////////////////////////////////

// //https://github.com/ronja-tutorials/ShaderTutorials/blob/master/Assets/047_InverseInterpolationAndRemap/Interpolation.cginc
float InvLerp(float from, float to, float value) {
  return (value - from) / (to - from);
}
vec3 InvLerp(vec3 from, vec3 to, vec3 value) {
  return (value - from) / (to - from);
}
float Rescale(float origFrom, float origTo, float targetFrom, float targetTo, float value){
  float rel = InvLerp(origFrom, origTo, value);
  return mix(targetFrom, targetTo, rel);
}
vec3 Rescale(vec3 origFrom, vec3 origTo, vec3 targetFrom, vec3 targetTo, vec3 value){
  vec3 rel = InvLerp(origFrom, origTo, value);
  return mix(targetFrom, targetTo, rel);
}

// Safe ///////////////////////////////////////////////////////////////////////////
float DivideSafe(float a, float b, float safe) {
  return b != 0 ? a / b : safe;
}

vec3 Sign(vec3 x) {
  // return (clamp((x * (1.0 / 0.0) + 0.5f), vec3(0), vec3(1)) * 2.f + -1.f);
  return sign(x);
}

float Sign(float x) {
  // return (clamp((x * (1.0 / 0.0) + 0.5f), (0), (1)) * 2.f + -1.f);
  return sign(x);
}

vec3 SignPow(vec3 x, float exponent) {
  return Sign(x) * pow(abs(x), vec3(exponent));
}

float SignPow(float x, float exponent) {
  return Sign(x) * pow(abs(x), (exponent));
}

vec3 SignSqrt(vec3 x) {
  return Sign(x) * sqrt(abs(x));
}

float SignSqrt(float x) {
  return Sign(x) * sqrt(abs(x));
}

// sRGB ///////////////////////////////////////////////////////////////////////////
//https://gamedev.stackexchange.com/questions/92015/optimized-linear-to-srgb-glsl
vec3 SrgbEncode(vec3 x) {
  bvec3 cutoff = lessThan(x, vec3(0.0031308));
  vec3 higher = vec3(1.055) * pow(x, vec3(1.0 / 2.4)) - vec3(0.055);
  vec3 lower = x * vec3(12.92);

  return mix(higher, lower, cutoff);
}

vec3 SrgbEncodeSafe(vec3 x) {
  return Sign(x) * SrgbEncode(abs(x));
}

vec3 SrgbDecode(vec3 x) {
  bvec3 cutoff = lessThan(x, vec3(0.04045));
  vec3 higher = pow((x + vec3(0.055)) / vec3(1.055), vec3(2.4));
  vec3 lower = x / vec3(12.92);

  return mix(higher, lower, cutoff);
}

vec3 SrgbDecodeSafe(vec3 x) {
  return Sign(x) * SrgbDecode(abs(x));
}

////https://jvm-gaming.org/t/fast-srgb-conversion-glsl-snippet/57321
//vec3 SrgbEncode(vec3 color) {
//	float r = color.r < 0.0031308 ? 12.92 * color.r : 1.055 * pow(color.r, 1.0/2.4) - 0.055;
//	float g = color.g < 0.0031308 ? 12.92 * color.g : 1.055 * pow(color.g, 1.0/2.4) - 0.055;
//	float b = color.b < 0.0031308 ? 12.92 * color.b : 1.055 * pow(color.b, 1.0/2.4) - 0.055;
//	return vec3(r, g, b);
//}
//
//vec3 SrgbDecode(vec3 color) {
//	float r = color.r < 0.04045 ? (1.0 / 12.92) * color.r : pow((color.r + 0.055) * (1.0 / 1.055), 2.4);
//	float g = color.g < 0.04045 ? (1.0 / 12.92) * color.g : pow((color.g + 0.055) * (1.0 / 1.055), 2.4);
//	float b = color.b < 0.04045 ? (1.0 / 12.92) * color.b : pow((color.b + 0.055) * (1.0 / 1.055), 2.4);
//	return vec3(r, g, b);
//}

// Gamma ///////////////////////////////////////////////////////////////////////////
vec3 GammaDecode(vec3 linear, float gamma) {
 return pow(linear, vec3(gamma));
}

vec3 GammaEncode(vec3 linear, float gamma) {
 return pow(linear, vec3(1/gamma));
}

vec3 GammaEncodeSafe(vec3 x, float gamma) {
 return SignPow(x, 1 / gamma);
}

vec3 GammaDecodeSafe(vec3 x, float gamma) {
  return SignPow(x, gamma);
}

// Gamma Correction ///////////////////////////////////////////////////////////////////////////

vec3 GammeCorrect_SrgbToPow(vec3 c, float gamma) {
  c = SrgbEncode(c);
  c = GammaDecode(c, gamma);
  return c;
}

vec3 GammeCorrectSafe_SrgbToPow(vec3 c, float gamma) {
  vec3 s = Sign(c);
  c = abs(c);

  c = SrgbEncode(c);
  c = GammaDecode(c, gamma);

  c *= s;
  return c;
}

vec3 GammeCorrect_PowToSrgb(vec3 c, float gamma) {
  c = GammaEncode(c, gamma);
  c = SrgbDecode(c);
  return c;
}

vec3 GammeCorrectSafe_PowToSrgb(vec3 c, float gamma) {
  vec3 s = Sign(c);
  c = abs(c);

  c = GammaEncode(c, gamma);
  c = SrgbDecode(c);

  c *= s;
  return c;
}

//// scRGB ///////////////////////////////////////////////////////////////////////////

#define ScrgbDecode(x) (x * 80.)
#define ScrgbEncode(x) (x / 80.)

//// PQ ///////////////////////////////////////////////////////////////////////////
const vec3 M1 = vec3(2610.f / 16384.f);           // 0.1593017578125f;
const vec3 M2 = vec3(128.f * (2523.f / 4096.f));  // 78.84375f;
const vec3 C1 = vec3(3424.f / 4096.f);            // 0.8359375f;
const vec3 C2 = vec3(32.f * (2413.f / 4096.f));   // 18.8515625f;
const vec3 C3 = vec3(32.f * (2392.f / 4096.f));   // 18.6875f;
vec3 PqEncode(vec3 color, float scaling) {
 color *= (scaling / 10000.f);
 vec3 y_m1 = pow(color, M1);
 return pow((C1 + C2 * y_m1) / (1.f + C3 * y_m1), M2);
}

vec3 PqDecode(vec3 color, float scaling) {
 vec3 e_m12 = pow(color, 1.f / M2);
 vec3 out_color = pow(max(vec3(0), e_m12 - C1) / (C2 - C3 * e_m12), 1.f / M1);
 return out_color * (10000.f / scaling);
}

// CorrectLuminance ///////////////////////////////////////////////////////////////////////////
vec3 CorrectLuminance(vec3 color, float incorrect_y, float correct_y, float strength) {
  return color * mix(1., DivideSafe(correct_y, incorrect_y, 1.), strength);
}
vec3 CorrectLuminance(vec3 incorrect_color, vec3 correct_color, float strength) {
  return CorrectLuminance(incorrect_color, YFromBT709(incorrect_color), YFromBT709(correct_color), strength);
}

// Max Scale Clamp ///////////////////////////////////////////////////////////////////////////
vec3 ClampByMaxScaling(vec3 x, float peak) {
  float m = max(x.x, max(x.y, x.z));
  if (m > 0) 
    if (m > peak) 
      x *= peak / m;
  return x;
}

// ICtCp ///////////////////////////////////////////////////////////////////////////
const mat3 XYZ_TO_LMS_MAT = mat3(
    0.3592832590121217, -0.1920808463704993, 0.0070797844607479,
    0.6976051147779502, 1.1004767970374321, 0.0748396662186362,
    -0.0358915932320290, 0.0753748658519118, 0.8433265453898765);

const mat3 LMS_TO_XYZ_MAT = mat3(
    2.0701800566956132, 0.36498825003265756, -0.04959554223893212,
    -1.3264568761030210, 0.68046736285223520, -0.04942116118675749,
    0.20661600684785517, -0.045421753075853236, 1.1879959417328034);

vec3 ICtCpFromBT709(vec3 bt709_color) {
  vec3 xyz_color = BT709_TO_XYZ_MAT * bt709_color;
  vec3 lms_color = XYZ_TO_LMS_MAT * xyz_color;

  const mat3 ICTCP_LMS_TO_ICTCP_MAT = mat3(
      0.5000, 1.6137, 4.3780,
      0.5000, -3.3234, -4.2455,
      0.0000, 1.7097, -0.1325);

  return ICTCP_LMS_TO_ICTCP_MAT * PqEncode(lms_color, 100.0f);
}

vec3 BT709FromICtCp(vec3 col) {
  const mat3 ICTCP_TO_LMS_MAT = mat3(
      1.0, 1.0, 1.0,
      0.008605146, -0.008605146, 0.56004886,
      0.11103560, -0.11103560, -0.32063747);
  col = ICTCP_TO_LMS_MAT * col;

  // 1.0f = 100 nits, 100.0f = 10k nits
  col = PqDecode(col, 100.f);
  col = LMS_TO_XYZ_MAT * col;
  return XYZ_TO_BT709_MAT * col;
}

// Hue ///////////////////////////////////////////////////////////////////////////
vec3 CorrectHue(vec3 incorrect_color, vec3 correct_color, float strength) {
  if(strength == 0.) return incorrect_color;

  vec3 incorrect_lab = ICtCpFromBT709(incorrect_color);
  vec3 correct_lab = ICtCpFromBT709(correct_color);

  vec2 incorrect_ab = incorrect_lab.yz;
  vec2 correct_ab = correct_lab.yz;

  // Preserve original chrominance (magnitude of the a–b vector)
  float chrominance_pre_adjust = length(incorrect_ab);

  // Blend chrominance and hue by interpolating (a, b) components
  vec2 blended_ab = mix(incorrect_ab, correct_ab, strength);

  // Rescale to original chrominance to avoid saturation shift
  float chrominance_post_adjust = length(blended_ab);
  blended_ab *= DivideSafe(chrominance_pre_adjust, chrominance_post_adjust, 1.);

  incorrect_lab.yz = blended_ab;

  vec3 result = BT709FromICtCp(incorrect_lab);
  return result;
}


// Luma SetChrominance ///////////////////////////////////////////////////////////////////////////

//https://github.com/Filoppi/Luma-Framework/blob/main/Shaders/Includes/Color.hlsl
//approx colorspace agnostic saturation
vec3 Luma_SetChrominance(vec3 color, float chrominance)
{
    float maxVal = max(color.x, max(color.y, color.z));
    float minVal = min(color.x, min(color.y, color.z));
    float midVal = mix(minVal, maxVal, 0.5);
    return mix(vec3(midVal), color, vec3(chrominance));
}

// UpgradeToneMap ///////////////////////////////////////////////////////////////////////////
vec3 UpgradeToneMapBT709(
  vec3 color_untonemapped,
  vec3 color_tonemapped,
  vec3 color_tonemapped_graded,
  float post_process_strength,
  float auto_correction
) {
  float ratio = 1.;

  float y_untonemapped = YFromBT709(color_untonemapped);
  float y_tonemapped = YFromBT709(color_tonemapped);
  float y_tonemapped_graded = YFromBT709(color_tonemapped_graded);

  if(y_untonemapped < y_tonemapped) {
    // If substracting (user contrast or paperwhite) scale down instead
    // Should only apply on mismatched HDR
    ratio = y_untonemapped / y_tonemapped;
  } else {
    float y_delta = y_untonemapped - y_tonemapped;
    y_delta = max(0, y_delta);  // Cleans up NaN
    float y_new = y_tonemapped_graded + y_delta;

    bool y_valid = (y_tonemapped_graded > 0);  // Cleans up NaN and ignore black
    ratio = y_valid ? (y_new / y_tonemapped_graded) : 0;
  }
  float auto_correct_ratio = mix(1., ratio, clamp(y_untonemapped, 0, 1));
  ratio = mix(ratio, auto_correct_ratio, auto_correction);

  vec3 color_scaled = color_tonemapped_graded * ratio;
  // Match hue
  color_scaled = CorrectHue(color_scaled, color_tonemapped_graded, 1);
  return mix(color_untonemapped, color_scaled, post_process_strength);
}

// Working Color Space ////////////////////////////////////////////////////////////////

vec3 ColorSpaceConversion(vec3 x, int from, int to) {
  //BT709 to BT2020
  if(from == RENODX_CS_BT709 && to == RENODX_CS_BT2020) {
    x = BT709_TO_BT2020_MAT * x;
  }
  //BT709 to AP1
  else if(from == RENODX_CS_BT709 && to == RENODX_CS_AP1) {
    x = BT709_TO_AP1_MAT * x;
  }

  //BT2020 to BT709
  else if(from == RENODX_CS_BT2020 && to == RENODX_CS_BT709) {
    x = BT2020_TO_BT709_MAT * x;
  }
  //BT2020 to AP1
  else if(from == RENODX_CS_BT2020 && to == RENODX_CS_AP1) {
    x = BT2020_TO_AP1_MAT * x;
  }

  //AP1 to BT709
  else if(from == RENODX_CS_AP1 && to == RENODX_CS_BT709) {
    x = AP1_TO_BT709_MAT * x;
  }
  //AP1 to BT2020 
  else if(from == RENODX_CS_AP1 && to == RENODX_CS_BT2020) {
    x = AP1_TO_BT2020_MAT * x;
  }
  
  //noop
  return x;
}

vec3 WorkingColorSpace_Shaderpack_To_HDRTonemap(vec3 x) {
  return ColorSpaceConversion(x, RENODX_WORKINGCS_SHADERPACK, RENODX_WORKINGCS_HDRTONEMAP);
}

vec3 WorkingColorSpace_HDRTonemap_To_AfterTonemap(vec3 x) {
  return ColorSpaceConversion(x, RENODX_WORKINGCS_HDRTONEMAP, RENODX_WORKINGCS_AFTERTONEMAP);
}

vec3 WorkingColorSpace_RenderIntermediatePassInput_To_BT709(vec3 x) {
  return ColorSpaceConversion(x, RENODX_WORKINGCS_RENDERINTERMEDIATEPASSINPUT, RENODX_CS_BT709);
}

// Working Gamma //////////////////////////////////////////////////////////////////

vec3 WorkingGamma_Correction(vec3 x) {
  #if RENODX_GAMMACORRECTION == RENODX_GAMMA_22
    x = GammeCorrectSafe_SrgbToPow(x, 2.2);
  #elif RENODX_GAMMACORRECTION == RENODX_GAMMA_24
    x = GammeCorrectSafe_SrgbToPow(x, 2.4);
  #endif
  return x;
}

vec3 WorkingGamma_Encode(vec3 x, int target) {
  if (target == RENODX_GAMMA_20) {
    vec3 signX = Sign(x);
    vec3 absX = abs(x);
    x = signX * sqrt(absX);
  }
  else if (target == RENODX_GAMMA_22) {
    x = GammaEncodeSafe(x, 2.2);
  }
  else if (target == RENODX_GAMMA_24) {
    x = GammaEncodeSafe(x, 2.4);
  }
  else if (target == RENODX_GAMMA_SRGB) {
    x = SrgbEncodeSafe(x);
  }
  return x;
}

vec3 WorkingGamma_Decode(vec3 x, int target) {
  if (target == RENODX_GAMMA_20) {
    vec3 signX = Sign(x);
    vec3 absX = abs(x);
    x = signX * (absX * absX);
  }
  else if (target == RENODX_GAMMA_22) {
    x = GammaDecodeSafe(x, 2.2);
  }
  else if (target == RENODX_GAMMA_24) {
    x = GammaDecodeSafe(x, 2.4);
  }
  else if (target == RENODX_GAMMA_SRGB) {
    x = SrgbDecodeSafe(x);
  }
  return x;
}

vec3 WorkingGamma_AfterTonemap_Encode(vec3 x) {
  return WorkingGamma_Encode(x, RENODX_WORKINGGAMMA_AFTERTONEMAP);
}

vec3 WorkingGamma_RenderIntermediatePass_Decode(vec3 x) {
  return WorkingGamma_Decode(x, RENODX_WORKINGGAMMA_RENDERINTERMEDIATEPASSINPUT);
}

// ColorGrade ///////////////////////////////////////////////////////////////////////////
//from RenoDX clshortfuse
float RenoDX_Contrast(float x, float contrast, float mid_gray) {
  return pow(max(0, x / mid_gray), contrast) * mid_gray;
}

float RenoDX_Shadows(float x, float shadows, float mid_gray) {
  float value;
  if (shadows > 1.f) {
    value = max(x, x * (1.f + (x * mid_gray / pow(x / mid_gray, shadows))));
  } else if (shadows < 1.f) {
    value = clamp(x * (1.f - (x * mid_gray / pow(x / mid_gray, 2.f - shadows))), 0.f , x);
  } else {
    value = x;
  }
  return value;
}
float RenoDX_Highlights(float x, float highlights, float mid_gray) {
  float value;
  if (highlights > 1.f) {
    value = max(x, mix(x, mid_gray * pow(x / mid_gray, highlights), x));
  } else if (highlights < 1.f) {
    value = min(x, x / (1.f + mid_gray * pow(x / mid_gray, 2.f - highlights) - x));
  } else {
    value = x;
  }
  return value;
}

vec3 ColorGradePass(vec3 color) {
  #if RENODX_COLORGRADE_ENABLED 
    float y = YFromCS(color, RENODX_WORKINGCS_SHADERPACK);
    if (y > 0) {
      float y1 = y;

      #if RENODX_COLORGRADE_CONTRAST != 100
        y1 = RenoDX_Contrast(y1, RENODX_COLORGRADE_CONTRAST / 100., RENODX_COLORGRADE_CONTRAST_MID);
      #endif

      #if RENODX_COLORGRADE_HIGHLIGHTS != 100
        y1 = RenoDX_Highlights(y1, RENODX_COLORGRADE_HIGHLIGHTS / 100., RENODX_COLORGRADE_HIGHLIGHTS_MID);
      #endif
      
      #if RENODX_COLORGRADE_SHADOWS != 100
        y1 = RenoDX_Shadows(y1, RENODX_COLORGRADE_SHADOWS / 100., RENODX_COLORGRADE_SHADOWS_MID);
      #endif

      color *= y1/y;
    }
  #endif

  #if RENODX_COLORGRADE_SATURATION != 100
    color = Luma_SetChrominance(color, RENODX_COLORGRADE_SATURATION / 100.);
  #endif

  return color;
}

// RCAS ///////////////////////////////////////////////////////////////////////////

//https://github.com/Filoppi/Luma-Framework/blob/main/Shaders/Includes/RCAS.hlsl 

// RCAS (Robust Contrast-Adaptive Sharpening) 
// https://github.com/GPUOpen-Effects/FidelityFX-FSR2?tab=readme-ov-file#robust-contrast-adaptive-sharpening-rcas
// 
// Our implementation is based on: "https://github.com/cdozdil/OptiScaler/blob/master/OptiScaler/shaders/rcas/precompile/rcas.hlsl",
// which is in turn based on: "https://github.com/RdenBlaauwen/RCAS-for-ReShade",
// which is in turn based on AMD FSR 1 RCAS: https://github.com/GPUOpen-Effects/FidelityFX-FSR/blob/master/ffx-fsr/ffx_fsr1.h

// Try to detect noise or grain and not over sharpen it. This seemengly assumes the input is in the SDR 0-1 range.
#ifdef RENODX_RCAS_DENOISE
  #define RCAS_DENOISE 1
#else
  #define RCAS_DENOISE 0
#endif
// Lower means less artifacts and "less sharpening", it's unclear what 16 stands for here, and whether this is meant for linear or gamma space (probably gamma space)
// This is set at the limit of providing unnatural results for sharpening (anything more generates artifacts).
#ifndef RCAS_LIMIT
#define RCAS_LIMIT (0.25-(1.0/16.0))
#endif
// This should look better, avoid hue shifts and be more compatible with HDR (scRGB, which can have negative values). This appears to have stronger sharpening when enabled, but also causes more black dots to appear at extreme sharpening values.
// For now it's disabled by default as there's not enough proof to justify it, sharpening is a perceptual trick so hue shifts don't really matter (in fact, possibly they make it better).
#define RCAS_LUMINANCE_BASED 0

float RCASRenoDX_getRCASLuma(vec3 rgb) //(named weird to avoid conflict w/ shaderpack)
{
#if 0 // LUMA FT: changed to use the proper Rec.709 luminance formula (but multiplied by 2 as the sum of the original RCAS formula is 2)
    return GetLuminance(rgb) * 2;
#else
    return dot(rgb, vec3(0.5, 1.0, 0.5));
#endif
}

vec3 RCASRenoDX(sampler2D colorTex, ivec2 uv , float sharpness, float paperWhite, int renodx_gamma, int renodx_cs)
{
    if (sharpness > 1.f) sharpness /= 100.f; //assume mistake and scale down
    
    // RCAS is always "pixel based" (the next 4 pixels)
    //    b
    //  d e f
    //    h
    // We check for "maxPixelCoord" and "minPixelCoord" to support dynamic resolution scaling. We assume "pixelCoord" is already within the limits.
    vec3 e = WorkingGamma_Encode(texelFetch(colorTex, uv + ivec2( 0, 0), 0).rgb, renodx_gamma);
    if (sharpness <= 0.0f) return e;
    e /= paperWhite;
    vec3 b = WorkingGamma_Encode(texelFetch(colorTex, uv + ivec2( 0,-1), 0).rgb, renodx_gamma) / paperWhite;
    vec3 d = WorkingGamma_Encode(texelFetch(colorTex, uv + ivec2(-1, 0), 0).rgb, renodx_gamma) / paperWhite;
    vec3 f = WorkingGamma_Encode(texelFetch(colorTex, uv + ivec2( 1, 0), 0).rgb, renodx_gamma) / paperWhite;
    vec3 h = WorkingGamma_Encode(texelFetch(colorTex, uv + ivec2( 0, 1), 0).rgb, renodx_gamma) / paperWhite;

#if RCAS_DENOISE >= 1
    // Get lumas times 2. Should use luma weights that are twice as large as normal.
    float bL = RCASRenoDX_getRCASLuma(b);
    float dL = RCASRenoDX_getRCASLuma(d);
    float eL = RCASRenoDX_getRCASLuma(e);
    float fL = RCASRenoDX_getRCASLuma(f);
    float hL = RCASRenoDX_getRCASLuma(h);

    // denoise
    float nz = (bL + dL + fL + hL) * 0.25 - eL;
    float range = max(max(max(bL, dL), max(hL, fL)), eL) - min(min(min(bL, dL), min(eL, fL)), hL);
    nz = clamp(abs(nz) * (1/(range)),0,1);
    nz = -0.5 * nz + 1.0;
#endif

    const float samplesNum = 4.0; // There's 4 (5) colors to be mixed
    // Immediate constants for peak range.
    const vec2 peakC = vec2(1.0, -samplesNum);

#if RCAS_LUMINANCE_BASED
    // These should all be >= 0, but tiny values below zero shouldn't hurt anyway, still, we clip them below as they'd be clipped by the screen anyway
    float bLum = YFromCS(b, renodx_cs);
    float dLum = YFromCS(d, renodx_cs);
    float eLum = YFromCS(e, renodx_cs);
    float fLum = YFromCS(f, renodx_cs);
    float hLum = YFromCS(h, renodx_cs);

    float minLum = max(min(min(bLum, dLum), min(fLum, hLum)), 0.0);
    float maxLum = max(max(bLum, dLum), max(fLum, hLum));

    float hitMin = minLum * (1/(samplesNum * maxLum));
    float hitMax = (peakC.x - maxLum) * (1/(samplesNum * minLum + peakC.y));

    float localLobe = max(-hitMin, hitMax);
#else // !RCAS_LUMINANCE_BASED
    // Min and max of ring.
    vec3 minRGB = min(min(b, d), min(f, h));
    vec3 maxRGB = max(max(b, d), max(f, h));

#if 0 // It seems like it's ok if these values aren't in the 0-1 range, the code below will still work as expected, and avoid clipping HDR (or anyway ignoring values beyond SDR) //TODOFT3: Lilium claims the code below can break for negative rgb (scRGB) values, investigate and fix it
    minRGB = clamp(minRGB, 0, 1);
    maxRGB = clamp(maxRGB, 0, 1);
#endif

    // Limiters, these need to use high precision reciprocal operations.
    // Decided to use standard rcp for now in hopes of optimizing it.
    // It's fine if either of these can go below zero!
    vec3 hitMin = minRGB * (1/(samplesNum * maxRGB));
    vec3 hitMax = (peakC.xxx - maxRGB) * (1/(samplesNum * minRGB + peakC.yyy));

    vec3 lobeRGB = max(-hitMin, hitMax);
#if 0 // An attempt to make this code, which branches by r g b channel, color space agnostic. Without this, the result heavily depends by where the rgb coordinates are in the CIE color graph, and by how luminous they are. Unfortunately this drastically reduces the sharpening intensity, even if it makes it look even more natural.
    float localLobe = max(lobeRGB.r * Rec709_Luminance.r * 3.0, max(lobeRGB.g * Rec709_Luminance.g * 3.0, lobeRGB.b * Rec709_Luminance.b * 3.0));
#else
    float localLobe = max(lobeRGB.r, max(lobeRGB.g, lobeRGB.b));
#endif
#endif // RCAS_LUMINANCE_BASED

    float lobe = max(-RCAS_LIMIT, min(localLobe, 0.0)) * sharpness;

#if RCAS_DENOISE >= 1
    // denoise
    lobe *= nz;
#endif

    // Resolve, which needs medium precision rcp approximation to avoid visible tonality changes.
    float rcpL = (1/(samplesNum * lobe + 1.0));
    
#if RCAS_LUMINANCE_BASED
    float outputLum = ((bLum + dLum + hLum + fLum) * lobe + eLum) * rcpL;
    // Questionable choice: in case the source center pixel had luminance zero (even if the rgb ratio wasn't flat), elevate it to grey and match the target luminance, or we'd have a division by zero.
    // The alternative would be to keep "e" intact, but that then wouldn't have applied any sharpening.
    vec3 output1 = eLum != 0 ? (e * (outputLum / eLum)) : outputLum;
#else // !RCAS_LUMINANCE_BASED
    vec3 output1 = ((b + d + f + h) * lobe + e) * rcpL;
#endif // RCAS_LUMINANCE_BASED

#if 0 // Debug
    if (dynamicSharpening)
    {
        if (originalSharpness < sharpness)
            output.r *= 1 + (12.0 * (sharpness - originalSharpness));
        else
            output.g *= 1 + (12.0 * (originalSharpness - sharpness));
    }
#endif

    output1 *= paperWhite;

    return output1;
}

#endif // SRC_RCAS_HLSL

// Apply Per Channel Correction ////////////////////////////////////////////////////////
struct ApplyPerChannelCorrectionResult {
  vec3 corrected_color;
  float tonemapped_luminance;
};
ApplyPerChannelCorrectionResult ApplyPerChannelCorrection_Internal(
    vec3 untonemapped,
    vec3 per_channel_color,
    float blowout_restoration,
    float hue_correction_strength,
    float chrominance_correction_strength,
    float hue_shift_strength) {
  ApplyPerChannelCorrectionResult result;

  float tonemapped_luminance = YFromBT709(abs(per_channel_color));
  result.tonemapped_luminance = tonemapped_luminance;

  const float AUTO_CORRECT_BLACK = 0.02;
  // Fix near black
  float untonemapped_luminance = YFromBT709(abs(untonemapped));
  float ratio = tonemapped_luminance / untonemapped_luminance;
  float auto_correct_ratio = mix(ratio, 1.0,
                                 clamp(untonemapped_luminance / AUTO_CORRECT_BLACK, 0.0, 1.0));
  untonemapped *= auto_correct_ratio;

  vec3 tonemapped_perceptual = ICtCpFromBT709(per_channel_color);
  vec3 untonemapped_perceptual = ICtCpFromBT709(untonemapped);

  vec2 untonemapped_chromas = untonemapped_perceptual.yz;
  vec2 tonemapped_chromas = tonemapped_perceptual.yz;

  float untonemapped_chrominance = length(untonemapped_perceptual.yz);  // eg: 0.80
  float tonemapped_chrominance = length(tonemapped_perceptual.yz);      // eg: 0.20

  // clamp saturation loss
  float chrominance_ratio = min(DivideSafe(tonemapped_chrominance,
                                           untonemapped_chrominance, 1.0),
                                1.0);
  chrominance_ratio = max(chrominance_ratio, blowout_restoration);

  // Untonemapped hue, tonemapped chrominance (with limit)
  vec2 reduced_untonemapped_chromas = untonemapped_chromas * chrominance_ratio;

  // pick chroma based on per-channel luminance (supports not oversaturating crushed areas)
  vec2 reduced_hue_shifted = mix(
      tonemapped_chromas,
      reduced_untonemapped_chromas,
      clamp(tonemapped_luminance / 0.36, 0.0, 1.0));

  // Tonemapped hue, restored chrominance (with limit)
  vec2 blowout_restored_chromas = tonemapped_chromas * DivideSafe(length(reduced_hue_shifted), length(tonemapped_chromas), 1.0);

  vec2 hue_shifted_chromas = mix(reduced_hue_shifted,
                                       blowout_restored_chromas,
                                       hue_shift_strength);

  // Pick untonemapped hues for shadows/midtones
  vec2 hue_correct_chromas = untonemapped_chromas * DivideSafe(length(hue_shifted_chromas), length(untonemapped_chromas), 1.0);

  vec2 selectable_hue_correct_range = mix(
      hue_correct_chromas,
      hue_shifted_chromas,
      clamp(tonemapped_luminance / 0.36, 0.0, 1.0));

  vec2 hue_corrected_chromas = mix(hue_shifted_chromas,
                                         selectable_hue_correct_range,
                                         hue_correction_strength);

  vec2 chroma_correct_chromas = hue_corrected_chromas * DivideSafe(length(untonemapped_chromas), length(hue_corrected_chromas), 1.0);

  vec2 selectable_chroma_correct_range = mix(
      chroma_correct_chromas,
      hue_corrected_chromas,
      clamp(tonemapped_luminance / 0.36, 0.0, 1.0));

  vec2 chroma_corrected_chromas = mix(
      hue_correct_chromas,
      selectable_chroma_correct_range,
      chrominance_correction_strength);

  vec2 final_chromas = chroma_corrected_chromas;

  result.corrected_color = BT709FromICtCp(vec3(
      tonemapped_perceptual.x,
      final_chromas));

  return result;
}

//For UpgradeToneMap() pairing. 
// Run me right after SDT tonemap to restore lost chroma from quick SDR blowouts.
// Requires both BT709.
vec3 ApplyPerChannelCorrection(vec3 untonemapped, vec3 per_channel_color) {
#if RENODX_APPC_AMOUNT == 0
  return per_channel_color;
#else
  ApplyPerChannelCorrectionResult result = ApplyPerChannelCorrection_Internal(
      untonemapped,
      per_channel_color,
      RENODX_APPC_AMOUNT / 100.,
      1.0,
      1.0,
      0.6);

  result.corrected_color = max(vec3(0), result.corrected_color);
  result.corrected_color = ClampByMaxScaling(result.corrected_color, 1);

  #if RENODX_APPC_POW > 0
    float strength = result.tonemapped_luminance;
    strength = clamp(strength, 0.0, 1.0);
    strength = pow(strength, RENODX_APPC_POW);
    return mix(per_channel_color, result.corrected_color, strength);
  #else
    return result.corrected_color;
  #endif
#endif
}


// Reinhard ///////////////////////////////////////////////////////////////////////////
vec3 ReinhardInv(vec3 x) {
  return x / (1.0 - x);
}
vec3 ReinhardInv(vec3 x, float peak) {
  return x / (1.0 - x / peak);
}
float ReinhardInv(float x) {
  return x / (1.0 - x);
}
float ReinhardInv(float x, float peak) {
  return x / (1.0 - x / peak);
}

vec3 Reinhard(vec3 x) {
  return x / (x + 1.0);
}
vec3 Reinhard(vec3 x, float peak) {
  return x / (x / peak + 1.);
}
vec3 ReinhardExtended(vec3 color, float white_max, float peak) {
  return Reinhard(color, peak) * (1. + (peak * color) / (white_max * white_max));
}

float Reinhard(float x, float peak) {
  return x / (x / peak + 1.);
}
float ReinhardExtended(float color, float white_max, float peak) {
  return Reinhard(color, peak) * (1. + (peak * color) / (white_max * white_max));
}

float ComputeReinhardExtendableScale(float w, float p, float m, float x, float y) {
  return p * (w * w * y - (p * x * x)) / (w * w * x * (p - y));
}

// vec3 ReinhardScalableExtended(vec3 x, float white_max, float x_max, float x_min, float gray_in, float gray_out) {
//   float exposure = ComputeReinhardExtendableScale(white_max, x_max, x_min, gray_in, gray_out);
//   vec3 extended = ReinhardExtended(x * exposure, white_max * exposure, x_max);
//   return min(extended, x_max);
// }
// vec3 ReinhardScalableExtendedY(vec3 x, float white_max, float x_max, float x_min, float gray_in, float gray_out) {
//   float y0 = YFromBT709(x);
//   float y1 = y0;
// 
//   float exposure = ComputeReinhardExtendableScale(white_max, x_max, x_min, gray_in, gray_out);
//   float extended = ReinhardExtended(y1 * exposure, white_max * exposure, x_max);
//   y1 = min(extended, x_max);
// 
//   //color_output = input_color * (y_original > 0 ? (y_new / y_original) : 0);
//   x = x * (y0 > 0 ? (y1 / y0) : 0);
//   return x;
// }
vec3 ReinhardPiecewiseExtended(vec3 x, float white_max, float x_max, float shoulder) //From Musa (I think)
{
   const float x_min = 0.f;
   float exposure = ComputeReinhardExtendableScale(white_max, x_max, x_min, shoulder, shoulder);
   vec3 extended = ReinhardExtended(x * exposure, white_max * exposure, x_max);
   extended = min(extended, x_max);

   return mix(x, extended, step(shoulder, x));
}
float ReinhardPiecewiseExtended(float x, float white_max, float x_max, float shoulder) //From Musa (I think)
{
   const float x_min = 0.f;
   float exposure = ComputeReinhardExtendableScale(white_max, x_max, x_min, shoulder, shoulder);
   float extended = ReinhardExtended(x * exposure, white_max * exposure, x_max);
   extended = min(extended, x_max);

   return mix(x, extended, step(shoulder, x));
}

vec3 ToneMapPass_Reinhard(vec3 color) {
  //setup
  float peak = (RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS);
  float shoulder = (RENODX_SHOULDER_START / RENODX_GAME_BRIGHTNESS);
  float white_clip = RENODX_WHITE_CLIP / RENODX_GAME_BRIGHTNESS;

  //do
  color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
  #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
    float y = YOrMaxChannelForHDRTonemap(color);
    if (y > 0) {
      float y1 = ReinhardPiecewiseExtended(y, white_clip, peak, shoulder);
      color *= y1 / max(y, 0.000001);
    }
  #else
    vec3 s = Sign(color);
    color = abs(color);
    color = ReinhardPiecewiseExtended(color, white_clip, peak, shoulder);
    color *= s;
  #endif
  color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);

  return color;
}

// GT Sport //////////////////////////////////////////////////////////////////////////////
#if RENODX_HDRTONEMAP_TYPE RENODX_HDRTONEMAP_TYPE_GT
vec3 GTTonemap(
  vec3 x, 
  float P /* = 1.f */, 
  float a /* = 1.f */,
  float m /* = 0.22f */, 
  float l /* = 0.4f */, 
  float c /* = 1.33f */, 
  float b /* = 0.f */,
  float l0,
  float L0,
  float L1
) {                            
  vec3 S0 = vec3(m + l0);                    
  vec3 S1 = vec3(m + a * l0);                
  vec3 C2 = vec3((a * P) / (P - S1));        
  vec3 CP = vec3(-C2 / P);                   
                                       
  vec3 w0 = 1.0f - smoothstep(vec3(0.0f), vec3(m), x);     
  vec3 w2 = step(vec3(m + l0), x); 
  vec3 w1 = vec3(1.0f) - w0 - w2;
  
  vec3 T_ = m * pow(x / m, vec3(c)) + b;
  vec3 S_ = P - (P - S1) * exp(CP * (x - S0));
  vec3 L_ = m + a * (x - m); 
                                            
  return T_ * w0 + L_ * w1 + S_ * w2;
}

float GTTonemapY(
  float x, 
  float P /* = 1.f */, 
  float a /* = 1.f */,
  float m /* = 0.22f */, 
  float l /* = 0.4f */, 
  float c /* = 1.33f */, 
  float b /* = 0.f */,
  float l0,
  float L0,
  float L1
) {                 
  float S0 = m + l0;                    
  float S1 = m + a * l0;                
  float C2 = (a * P) / (P - S1);        
  float CP = -C2 / P;                   
                                       
  float w0 = 1.0f - smoothstep(0.0f, m, x);     
  float w2 = step(m + l0, x); 
  float w1 = 1.0f - w0 - w2;
                                            
  float T_ = m * pow(x / m, c) + b;
  float S_ = P - (P - S1) * exp(CP * (x - S0));
  float L_ = m + a * (x - m); 
                                            
  return T_ * w0 + L_ * w1 + S_ * w2;
}                                        

vec3 ToneMapPass_GTTonemap(vec3 color) {
  //setup
  float P = RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS;   // Peak (nits)
  float a = RENODX_GT_A; //1;   // Contrast 1.f
  float m = RENODX_GT_M; //0.18f;   //Linear section start (mid gray, real) 0.22f
  float l = RENODX_GT_L; //0.4;  //Linear section length 0.4f (0-1)
  float c = RENODX_GT_C; //1.15; //Black tightness 1.33f (> 1)
  float b = RENODX_GT_B; //0.; //black floor raise (real) 0.f

  float l0 = ((P - m) * l) / a;
  float L0 = m - (m / a);
  float L1 = m + (1.0f - m) / a;

  //do
  color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
  #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
    float y = YOrMaxChannelForHDRTonemap(color);
    if (y > 0) {
      float y1 = GTTonemapY(y, P, a, m, l, c, b, l0, L0, L1);
      color *= y1 / y;
    }
  #else
    vec3 s = Sign(color);
    color = abs(color);
    color = GTTonemap(color, P, a, m, l, c, b, l0, L0, L1);
    color *= s;
  #endif
  color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);
  
  return color;
}
#endif

// ACES /////////////////////////////////////////////////////////////////////////////////

#if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_ACES
// Define log10_renodx function for single float
float log10_renodx(float x) {
  return log(x) / log(10.0);
}

// Define log10_renodx function for vec2
vec2 log10_renodx(vec2 v) {
  return log(v) / log(10.0);
}

float Rgb2Yc(vec3 rgb) {
  const float yc_radius_weight = 1.75;
  // Converts RGB to a luminance proxy, here called YC
  // YC is ~ Y + K * Chroma
  // Constant YC is a cone-shaped surface in RGB space, with the tip on the
  // neutral axis, towards white.
  // YC is normalized: RGB 1 1 1 maps to YC = 1
  //
  // ycRadiusWeight defaults to 1.75, although can be overridden in function
  // call to rgb_2_yc
  // ycRadiusWeight = 1 -> YC for pure cyan, magenta, yellow == YC for neutral
  // of same value
  // ycRadiusWeight = 2 -> YC for pure red, green, blue  == YC for  neutral of
  // same value.

  float r = rgb[0];
  float g = rgb[1];
  float b = rgb[2];

  float chroma = sqrt(b * (b - g) + g * (g - r) + r * (r - b));

  return (b + g + r + yc_radius_weight * chroma) / 3.0;
}

float Rgb2Saturation(vec3 rgb) {
  float minrgb = min(min(rgb.r, rgb.g), rgb.b);
  float maxrgb = max(max(rgb.r, rgb.g), rgb.b);
  return (max(maxrgb, 1e-10) - max(minrgb, 1e-10)) / max(maxrgb, 1e-2);
}

// Sigmoid function in the range 0 to 1 spanning -2 to +2.
float SigmoidShaper(float x) {
  float t = max(1.0 - abs(0.5 * x), 0.0);
  float y = 1.0 + sign(x) * (1.0 - t * t);
  return 0.5 * y;
}

float GlowFwd(float yc_in, float glow_gain_in, float glow_mid) {
  float glow_gain_out;

  if (yc_in <= 2.0 / 3.0 * glow_mid) {
    glow_gain_out = glow_gain_in;
  } else if (yc_in >= 2.0 * glow_mid) {
    glow_gain_out = 0.0;
  } else {
    glow_gain_out = glow_gain_in * (glow_mid / yc_in - 0.5);
  }

  return glow_gain_out;
}

// Transformations from RGB to other color representations
float Rgb2Hue(vec3 rgb) {
  const float aces_pi = 3.14159265359;
  // Returns a geometric hue angle in degrees (0-360) based on RGB values.
  // For neutral colors, hue is undefined and the function will return a quiet NaN value.
  float hue;
  if (rgb.r == rgb.g && rgb.g == rgb.b) {
    hue = 0.0;  // RGB triplets where RGB are equal have an undefined hue
  } else {
    hue = (180.0 / aces_pi) * atan(sqrt(3.0) * (rgb.g - rgb.b), 2.0 * rgb.r - rgb.g - rgb.b);
  }

  if (hue < 0.0) {
    hue = hue + 360.0;
  }

  return clamp(hue, 0.0, 360.0);
}

float CenterHue(float hue, float center_h) {
  float hue_centered = hue - center_h;
  if (hue_centered < -180.0) {
    hue_centered += 360.0;
  } else if (hue_centered > 180.0) {
    hue_centered -= 360.0;
  }
  return hue_centered;
}

vec3 YToLinCV(vec3 y, float y_max, float y_min) {
  return (y - y_min) / (y_max - y_min);
}

vec4 YToLinCV(vec4 y, float y_max, float y_min) {
  return (y - y_min) / (y_max - y_min);
}

// Transformations between CIE XYZ tristimulus values and CIE x,y
// chromaticity coordinates
vec3 XYZToXyY(vec3 xyz) {
  vec3 xy_y;
  float divisor = (xyz[0] + xyz[1] + xyz[2]);
  if (divisor == 0.0)
    divisor = 1e-10;
  xy_y[0] = xyz[0] / divisor;
  xy_y[1] = xyz[1] / divisor;
  xy_y[2] = xyz[1];

  return xy_y;
}

vec3 XyYToXYZ(vec3 xy_y) {
  vec3 xyz;
  xyz[0] = xy_y[0] * xy_y[2] / max(xy_y[1], 1e-10);
  xyz[1] = xy_y[2];
  xyz[2] = (1.0 - xy_y[0] - xy_y[1]) * xy_y[2] / max(xy_y[1], 1e-10);

  return xyz;
}

const float DIM_SURROUND_GAMMA = 0.9811;

vec3 DarkToDim(vec3 xyz, float dim_surround_gamma) {
  vec3 xy_y = XYZToXyY(xyz);
  xy_y.z = clamp(xy_y.z, 0.0, 65504.0);
  xy_y.z = pow(xy_y.z, DIM_SURROUND_GAMMA);
  return XyYToXYZ(xy_y);
}

const float MIN_STOP_SDR = -6.5;
const float MAX_STOP_SDR = 6.5;

const float MIN_STOP_RRT = -15.0;
const float MAX_STOP_RRT = 18.0;

const float MIN_LUM_SDR = 0.02;
const float MAX_LUM_SDR = 48.0;

const float MIN_LUM_RRT = 0.0001;
const float MAX_LUM_RRT = 10000.0;

const mat2 MIN_LUM_TABLE = mat2(
    -4.0000000, -1.6989700, /* log10_renodx(0.0001), log10_renodx(0.02)  */
    MIN_STOP_RRT, MIN_STOP_SDR);

const mat2 MAX_LUM_TABLE = mat2(
    1.6812413, 4.0000000, /* log10_renodx(48.0),   log10_renodx(10000) */
    MAX_STOP_SDR, MAX_STOP_RRT);

float Interpolate1D(mat2 table, float p) {
  float x0 = table[0][0];
  float x1 = table[0][1];
  float y0 = table[1][0];
  float y1 = table[1][1];

  if (p < x0) return y0;
  if (p >= x1) return y1;

  float s = (p - x0) / (x1 - x0);  // 0-to-1 mix factor
  return mix(y0, y1, s);
}

vec3 LinCv2Y(vec3 lin_cv, float y_max, float y_min) {
  return lin_cv * (y_max - y_min) + y_min;
}

float LookUpAcesMin(float min_lum_log10) {
  return 0.18 * exp2(Interpolate1D(MIN_LUM_TABLE, min_lum_log10));
}

float LookUpAcesMax(float max_lum_log10) {
  return 0.18 * exp2(Interpolate1D(MAX_LUM_TABLE, max_lum_log10));
}

float SSTS(
    float x,
    vec3 y_min, vec3 y_mid, vec3 y_max,
    vec3 coefs_low_a, vec3 coefs_low_b,
    vec3 coefs_high_a, vec3 coefs_high_b) {
  const int N_KNOTS_LOW = 4;
  const int N_KNOTS_HIGH = 4;

  float coefs_low[6];

  coefs_low[0] = coefs_low_a.x;
  coefs_low[1] = coefs_low_a.y;
  coefs_low[2] = coefs_low_a.z;
  coefs_low[3] = coefs_low_b.x;
  coefs_low[4] = coefs_low_b.y;
  coefs_low[5] = coefs_low_b.z;
  float coefs_high[6];
  coefs_high[0] = coefs_high_a.x;
  coefs_high[1] = coefs_high_a.y;
  coefs_high[2] = coefs_high_a.z;
  coefs_high[3] = coefs_high_b.x;
  coefs_high[4] = coefs_high_b.y;
  coefs_high[5] = coefs_high_b.z;

  // Check for negatives or zero before taking the log. If negative or zero,
  // set to HALF_MIN.
  float log_x = log10_renodx(max(x, 1.17549435082228750797e-38));  // equivalent to asfloat(0x00800000)

  float log_y;

  if (log_x > y_max.x) {
    // Above max breakpoint (overshoot)
    // If MAX_PT slope is 0, this is just a straight line and always returns
    // maxLum
    // y = mx+b
    // log_y = computeGraphY(C.Max.z, log_x, (C.Max.y) - (C.Max.z * (C.Max.x)));
    log_y = y_max.y;
  } else if (log_x >= y_mid.x) {
    // Part of Midtones area (Must have slope)
    float knot_coord = float(N_KNOTS_HIGH - 1) * (log_x - y_mid.x) / (y_max.x - y_mid.x);
    int j = int(knot_coord);
    float t = knot_coord - float(j);

    vec3 cf = vec3(coefs_high[j], coefs_high[j + 1], coefs_high[j + 2]);

    vec3 monomials = vec3(t * t, t, 1.0);
    log_y = dot(monomials, M_renodx * cf);
  } else if (log_x > y_min.x) {
    float knot_coord = float(N_KNOTS_LOW - 1) * (log_x - y_min.x) / (y_mid.x - y_min.x);
    int j = int(knot_coord);
    float t = knot_coord - float(j);

    vec3 cf = vec3(coefs_low[j], coefs_low[j + 1], coefs_low[j + 2]);

    vec3 monomials = vec3(t * t, t, 1.0);
    log_y = dot(monomials, M_renodx * cf);
  } else {  //(log_x <= (C.Min.x))
    // Below min breakpoint (undershoot)
    // log_y = computeGraphY(C.Min.z, log_x, ((C.Min.y) - C.Min.z * (C.Min.x)));
    log_y = y_min.y;
  }

  return pow(10.0, log_y);
}

const float LIM_CYAN = 1.147;
const float LIM_MAGENTA = 1.264;
const float LIM_YELLOW = 1.312;
const float THR_CYAN = 0.815;
const float THR_MAGENTA = 0.803;
const float THR_YELLOW = 0.880;
const float PWR = 1.2;

float GamutCompressChannel(float dist, float lim, float thr, float pwr) {
  float compr_dist;
  float scl;
  float nd;
  float p;

  if (dist < thr) {
    compr_dist = dist;  // No compression below threshold
  } else {
    // Calculate scale factor for y = 1 intersect
    scl = (lim - thr) / pow(pow((1.0 - thr) / (lim - thr), -pwr) - 1.0, 1.0 / pwr);

    // Normalize distance outside threshold by scale factor
    nd = (dist - thr) / scl;
    p = pow(nd, pwr);

    compr_dist = thr + scl * nd / (pow(1.0 + p, 1.0 / pwr));  // Compress
  }

  return compr_dist;
}

vec3 GamutCompress(vec3 lin_ap1) {
  // Achromatic axis
  float ach = max(lin_ap1.r, max(lin_ap1.g, lin_ap1.b));
  float abs_ach = abs(ach);
  // Distance from the achromatic axis for each color component aka inverse RGB ratios
  vec3 dist = ach != 0.0 ? (ach - lin_ap1) / abs_ach : vec3(0.0);

  // Compress distance with parameterized shaper function
  vec3 compr_dist = vec3(
      GamutCompressChannel(dist.r, LIM_CYAN, THR_CYAN, PWR),
      GamutCompressChannel(dist.g, LIM_MAGENTA, THR_MAGENTA, PWR),
      GamutCompressChannel(dist.b, LIM_YELLOW, THR_YELLOW, PWR));

  // Recalculate RGB from compressed distance and achromatic
  vec3 compr_lin_ap1 = ach - compr_dist * abs_ach;

  return compr_lin_ap1;
}

vec3 RRT(vec3 aces) {
  const vec3 AP1_RGB2Y = vec3(0.2722287168, 0.6740817658, 0.0536895174);

  // --- Glow module --- //
  // "Glow" module constants
  const float RRT_GLOW_GAIN = 0.05;
  const float RRT_GLOW_MID = 0.08;
  float saturation = Rgb2Saturation(aces);
  float yc_in = Rgb2Yc(aces);
  /* const */ float s = SigmoidShaper((saturation - 0.4) / 0.2);
  float added_glow = 1.0 + GlowFwd(yc_in, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
  aces *= added_glow;

  // --- Red modifier --- //
  // Red modifier constants
  const float RRT_RED_SCALE = 0.82;
  const float RRT_RED_PIVOT = 0.03;
  const float RRT_RED_HUE = 0.0;
  const float RRT_RED_WIDTH = 135.0;
  float hue = Rgb2Hue(aces);
  /* const */ float centered_hue = CenterHue(hue, RRT_RED_HUE);
  float hue_weight;
  {
    // hueWeight = cubic_basis_shaper(centeredHue, RRT_RED_WIDTH);
    hue_weight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centered_hue / RRT_RED_WIDTH));
    hue_weight *= hue_weight;
  }

  aces.r += hue_weight * saturation * (RRT_RED_PIVOT - aces.r) * (1.0 - RRT_RED_SCALE);

  // --- ACES to RGB rendering space --- //
  aces = clamp(aces, 0.0, 65535.0);
  vec3 rgb_pre = AP0_TO_AP1_MAT * aces;
  rgb_pre = clamp(rgb_pre, 0.0, 65504.0);

  // --- Global desaturation --- //
  // rgb_pre = RRT_SAT_MAT * rgb_pre;
  const float RRT_SAT_FACTOR = 0.96;
  rgb_pre = mix(vec3(dot(rgb_pre, AP1_RGB2Y)), rgb_pre, RRT_SAT_FACTOR);

  return rgb_pre;
}

vec3 ACES_ODTToneMap(vec3 rgb_pre, float min_y, float max_y) {
  // Aces-dev has more expensive version
  // AcesParams PARAMS = init_aces_params(minY, maxY);

  const mat2 BENDS_LOW_TABLE = mat2(
      MIN_STOP_RRT, MIN_STOP_SDR,
      0.18, 0.35);

  const mat2 BENDS_HIGH_TABLE = mat2(
      MAX_STOP_SDR, MAX_STOP_RRT,
      0.89, 0.90);

  float min_lum_log10 = log10_renodx(min_y);
  float max_lum_log10 = log10_renodx(max_y);
  float aces_min = LookUpAcesMin(min_lum_log10);
  float aces_max = LookUpAcesMax(max_lum_log10);

  // vec3 MIN_PT = vec3(lookup_ACESmin(minLum), minLum, 0.0);
  const vec3 MID_PT = vec3(0.18, 4.8, 1.55);
  // vec3 MAX_PT = vec3(lookup_ACESmax(maxLum), maxLum, 0.0);

  vec3 coefs_low_a;
  vec3 coefs_low_b;
  vec3 coefs_high_a;
  vec3 coefs_high_b;

  vec2 log_min = vec2(log10_renodx(aces_min), min_lum_log10);
  vec2 LOG_MID = vec2(log10_renodx(MID_PT.xy));
  vec2 log_max = vec2(log10_renodx(aces_max), max_lum_log10);

  float knot_inc_low = (LOG_MID.x - log_min.x) / 3.0;

  // Determine two lowest coefficients (straddling minPt)
  coefs_low_a.x = log_min.y;
  coefs_low_a.y = coefs_low_a.x;

  // Determine two highest coefficients (straddling midPt)
  float min_coef = (LOG_MID.y - MID_PT.z * LOG_MID.x);
  coefs_low_b.x = (MID_PT.z * (LOG_MID.x - 0.5 * knot_inc_low)) + (LOG_MID.y - MID_PT.z * LOG_MID.x);
  coefs_low_b.y = (MID_PT.z * (LOG_MID.x + 0.5 * knot_inc_low)) + (LOG_MID.y - MID_PT.z * LOG_MID.x);
  coefs_low_b.z = coefs_low_b.y;

  // Middle coefficient (which defines the "sharpness of the bend") is linearly interpolated
  float pct_low = Interpolate1D(BENDS_LOW_TABLE, log2(aces_min / 0.18));
  coefs_low_a.z = log_min.y + pct_low * (LOG_MID.y - log_min.y);

  float knot_inc_high = (log_max.x - LOG_MID.x) / 3.0;

  // Determine two lowest coefficients (straddling midPt)
  // float minCoef = ( logMid.y - MID_PT.z * logMid.x);
  coefs_high_a.x = (MID_PT.z * (LOG_MID.x - 0.5 * knot_inc_high)) + min_coef;
  coefs_high_a.y = (MID_PT.z * (LOG_MID.x + 0.5 * knot_inc_high)) + min_coef;

  // Determine two highest coefficients (straddling maxPt)
  // coefs_high[3] = (MAX_PT.z * (log_max.x-0.5*knotIncHigh)) + ( log_max.y - MAX_PT.z * log_max.x);
  // coefs_high[4] = (MAX_PT.z * (log_max.x+0.5*knotIncHigh)) + ( log_max.y - MAX_PT.z * log_max.x);
  // NOTE: if slope=0, then the above becomes just
  coefs_high_b.x = log_max.y;
  coefs_high_b.y = coefs_high_b.x;
  coefs_high_b.z = coefs_high_b.y;
  // leaving it as a variable for now in case we decide we need non-zero slope extensions

  // Middle coefficient (which defines the "sharpness of the bend") is linearly interpolated

  float pct_high = Interpolate1D(BENDS_HIGH_TABLE, log2(aces_max / 0.18));
  coefs_high_a.z = LOG_MID.y + pct_high * (log_max.y - LOG_MID.y);

  vec3 rgb_post = vec3(
      SSTS(rgb_pre.x, vec3(log_min.x, log_min.y, 0.0), vec3(LOG_MID.x, LOG_MID.y, MID_PT.z), vec3(log_max.x, log_max.y, 0.0), coefs_low_a, coefs_low_b, coefs_high_a, coefs_high_b),
      SSTS(rgb_pre.y, vec3(log_min.x, log_min.y, 0.0), vec3(LOG_MID.x, LOG_MID.y, MID_PT.z), vec3(log_max.x, log_max.y, 0.0), coefs_low_a, coefs_low_b, coefs_high_a, coefs_high_b),
      SSTS(rgb_pre.z, vec3(log_min.x, log_min.y, 0.0), vec3(LOG_MID.x, LOG_MID.y, MID_PT.z), vec3(log_max.x, log_max.y, 0.0), coefs_low_a, coefs_low_b, coefs_high_a, coefs_high_b));

  // Nits to Linear
  vec3 linear_cv = YToLinCV(rgb_post, max_y, min_y);
  return clamp(rgb_post, 0.0, 65535.0);
}

vec4 ACES_ODTToneMap(vec4 rgb_pre, float min_y, float max_y) {
  // Aces-dev has more expensive version
  // AcesParams PARAMS = init_aces_params(minY, maxY);

  const mat2 BENDS_LOW_TABLE = mat2(
      MIN_STOP_RRT, MIN_STOP_SDR,
      0.18, 0.35);

  const mat2 BENDS_HIGH_TABLE = mat2(
      MAX_STOP_SDR, MAX_STOP_RRT,
      0.89, 0.90);

  float min_lum_log10 = log10_renodx(min_y);
  float max_lum_log10 = log10_renodx(max_y);
  float aces_min = LookUpAcesMin(min_lum_log10);
  float aces_max = LookUpAcesMax(max_lum_log10);

  // vec3 MIN_PT = vec3(lookup_ACESmin(minLum), minLum, 0.0);
  const vec3 MID_PT = vec3(0.18, 4.8, 1.55);
  // vec3 MAX_PT = vec3(lookup_ACESmax(maxLum), maxLum, 0.0);

  vec3 coefs_low_a;
  vec3 coefs_low_b;
  vec3 coefs_high_a;
  vec3 coefs_high_b;

  vec2 log_min = vec2(log10_renodx(aces_min), min_lum_log10);
  vec2 LOG_MID = vec2(log10_renodx(MID_PT.xy));
  vec2 log_max = vec2(log10_renodx(aces_max), max_lum_log10);

  float knot_inc_low = (LOG_MID.x - log_min.x) / 3.0;

  // Determine two lowest coefficients (straddling minPt)
  coefs_low_a.x = log_min.y;
  coefs_low_a.y = coefs_low_a.x;

  // Determine two highest coefficients (straddling midPt)
  float min_coef = (LOG_MID.y - MID_PT.z * LOG_MID.x);
  coefs_low_b.x = (MID_PT.z * (LOG_MID.x - 0.5 * knot_inc_low)) + (LOG_MID.y - MID_PT.z * LOG_MID.x);
  coefs_low_b.y = (MID_PT.z * (LOG_MID.x + 0.5 * knot_inc_low)) + (LOG_MID.y - MID_PT.z * LOG_MID.x);
  coefs_low_b.z = coefs_low_b.y;

  // Middle coefficient (which defines the "sharpness of the bend") is linearly interpolated
  float pct_low = Interpolate1D(BENDS_LOW_TABLE, log2(aces_min / 0.18));
  coefs_low_a.z = log_min.y + pct_low * (LOG_MID.y - log_min.y);

  float knot_inc_high = (log_max.x - LOG_MID.x) / 3.0;

  // Determine two lowest coefficients (straddling midPt)
  // float minCoef = ( logMid.y - MID_PT.z * logMid.x);
  coefs_high_a.x = (MID_PT.z * (LOG_MID.x - 0.5 * knot_inc_high)) + min_coef;
  coefs_high_a.y = (MID_PT.z * (LOG_MID.x + 0.5 * knot_inc_high)) + min_coef;

  // Determine two highest coefficients (straddling maxPt)
  // coefs_high[3] = (MAX_PT.z * (log_max.x-0.5*knotIncHigh)) + ( log_max.y - MAX_PT.z * log_max.x);
  // coefs_high[4] = (MAX_PT.z * (log_max.x+0.5*knotIncHigh)) + ( log_max.y - MAX_PT.z * log_max.x);
  // NOTE: if slope=0, then the above becomes just
  coefs_high_b.x = log_max.y;
  coefs_high_b.y = coefs_high_b.x;
  coefs_high_b.z = coefs_high_b.y;
  // leaving it as a variable for now in case we decide we need non-zero slope extensions

  // Middle coefficient (which defines the "sharpness of the bend") is linearly interpolated

  float pct_high = Interpolate1D(BENDS_HIGH_TABLE, log2(aces_max / 0.18));
  coefs_high_a.z = LOG_MID.y + pct_high * (log_max.y - LOG_MID.y);

  vec4 rgb_post = vec4(
      SSTS(rgb_pre.x, vec3(log_min.x, log_min.y, 0.0), vec3(LOG_MID.x, LOG_MID.y, MID_PT.z), vec3(log_max.x, log_max.y, 0.0), coefs_low_a, coefs_low_b, coefs_high_a, coefs_high_b),
      SSTS(rgb_pre.y, vec3(log_min.x, log_min.y, 0.0), vec3(LOG_MID.x, LOG_MID.y, MID_PT.z), vec3(log_max.x, log_max.y, 0.0), coefs_low_a, coefs_low_b, coefs_high_a, coefs_high_b),
      SSTS(rgb_pre.z, vec3(log_min.x, log_min.y, 0.0), vec3(LOG_MID.x, LOG_MID.y, MID_PT.z), vec3(log_max.x, log_max.y, 0.0), coefs_low_a, coefs_low_b, coefs_high_a, coefs_high_b),
      SSTS(rgb_pre.w, vec3(log_min.x, log_min.y, 0.0), vec3(LOG_MID.x, LOG_MID.y, MID_PT.z), vec3(log_max.x, log_max.y, 0.0), coefs_low_a, coefs_low_b, coefs_high_a, coefs_high_b));

  // Nits to Linear
  vec4 linear_cv = YToLinCV(rgb_post, max_y, min_y);
  return clamp(rgb_post, 0.0, 65535.0);
}

vec3 ACES_ODT(vec3 rgb_pre, float min_y, float max_y) {
  vec3 tonescaled = ACES_ODTToneMap(rgb_pre, min_y, max_y);
  return tonescaled;
}

//Requires AP1
vec3 ACES_RGCAndRRTAndODT(vec3 color, float min_y, float max_y) {
  #ifdef RENODX_ACES_RGC
    color = GamutCompress(color); // Compresses to AP1 (BLOWOUT)
  #endif
  
  #ifdef RENODX_ACES_RRT
    color = AP1_TO_AP0_MAT * color; // Convert to AP0
    color = RRT(color);        // RRT AP0 => AP1
  #endif

  color = ACES_ODT(color, min_y, max_y);  // ACES_ODT AP1 => Matrix
  return color;
}

vec3 ToneMapPass_ACES(vec3 color) {
  const float ACES_MID_GRAY = 0.10f;
  const float ACES_MIN = 0.0001f;
  const float mid_gray_scale = (RENODX_ACES_MIDGRAY / ACES_MID_GRAY);

  float aces_min = ACES_MIN / RENODX_GAME_BRIGHTNESS;
  float aces_max = (RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS);
  aces_max /= mid_gray_scale;
  aces_min /= mid_gray_scale;

  // color = GammeCorrectSafe_PowToSrgb(color, 2.6);
  color = ColorSpaceConversion(color, RENODX_WORKINGCS_SHADERPACK, RENODX_CS_AP1);
  color = ACES_RGCAndRRTAndODT(color, aces_min * 48.f, aces_max * 48.f);
  color /= 48.f;
  color *= mid_gray_scale;
  color = ColorSpaceConversion(color, RENODX_CS_AP1, RENODX_WORKINGCS_AFTERTONEMAP);


  return color;
}
#endif

// Hermite ////////////////////////////////////////////////////////////////////////////////
#if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_HERMITE
float HermiteSplineRolloff(float x, float target_white /* = 1.f */, float max_white /* = 20.f */) {
  float l_w = max_white;
  // float l_b = min_black;
  // float l_min = target_black;
  float l_max = target_white;
  float e_1 = Rescale(0, l_w, 0, 1, x);
  // float min_lum = renodx::math::Rescale(l_min, l_b, l_w);
  float max_lum = Rescale(0, l_w, 0, 1, l_max);
  float knee_start = 1.5f * max_lum - 0.5f;
  // float b = min_lum;
  float t_b = Rescale(knee_start, 1.f, 0, 1, e_1);

  // float p_e1 = (((2 * t_b * t_b * t_b) - (3 * t_b * t_b) + 1) * knee_start)
  //              + (((t_b * t_b * t_b) - (2 * t_b * t_b) + t_b) * (1.f - knee_start))
  //              + ((-(2 * t_b * t_b * t_b) + (3 * t_b * t_b)) * max_lum);
  float t_b_squared = t_b * t_b;
  float t_b_cubed = t_b_squared * t_b;
  float two_t_b_cubed = 2.f * t_b_cubed;
  float three_t_b_squared = 3.f * t_b_squared;
  float p_e1_h00 = (two_t_b_cubed - three_t_b_squared + 1.f);
  float p_e1_h10 = (t_b_cubed - 2.f * t_b_squared + t_b);
  float p_e1_h01 = (-two_t_b_cubed + three_t_b_squared);
  // float p_e1_h11 = (t_b_cubed - t_b_squared); // Not used since derivative is 0 at max_lum

  float p_e1 = p_e1_h00 * knee_start
               + p_e1_h10 * (1.f - knee_start)
               + p_e1_h01 * max_lum;

  float e_2 = (e_1 < knee_start) ? e_1 : p_e1;

  // float e_3 = e_2 + b * pow(1-e_2, 4);
  // float e_3a1 = (1 - e_2) * (1 - e_2);
  // float e_3a2 = e_3a1 * (1 - e_2);
  float e_3 = e_2;

  // Custom: clamp before lerp
  // e_3 = saturate(e_3);

  // float e_4 = lerp(l_b, l_w, e_3);
  float e_4 = l_w * e_3;

  return min(e_4, target_white);
}

vec3 HermiteSplineLuminanceRolloff(vec3 color, float target_white /* = 1.f */, float max_white /* = 20.f */) {
  float y = YOrMaxChannelForHDRTonemap(color);
  float new_y = HermiteSplineRolloff(y, target_white, max_white);
  vec3 new_color = CorrectLuminance(color, y, new_y, 1);
  return new_color;
}

vec3 HermiteSplinePerChannelRolloff(vec3 x, float target_white /* = 1.f */, float max_white /* = 20.f */) {
  float target_white_log2 = log2(target_white);
  float max_white_log2 = log2(max_white);
  vec3 scaled = vec3(
      x.r == 0 ? 0 : exp2(HermiteSplineRolloff(log2(x.r), target_white_log2, max_white_log2)),
      x.g == 0 ? 0 : exp2(HermiteSplineRolloff(log2(x.g), target_white_log2, max_white_log2)),
      x.b == 0 ? 0 : exp2(HermiteSplineRolloff(log2(x.b), target_white_log2, max_white_log2))
      );
  return scaled;
}

vec3 ToneMapPass_Hermite(vec3 color) {
  //setup
  float p = RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS; // Peak (nits)
  float w = RENODX_WHITE_CLIP / RENODX_GAME_BRIGHTNESS; // white clip

  //do
  color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
  #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
    color = HermiteSplineLuminanceRolloff(color, p, w);
  #else
    vec3 s = Sign(color);
    color = abs(color);
    color = HermiteSplinePerChannelRolloff(color, p, w);
    color *= s;
  #endif
  color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);

  return color;
} 
#endif

// GT7 ///////////////////////////////////////////////////////////////////////////////////

#if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_GT7
// the higher the scaling, the more it blows out.
#define PHYSICAL_SCALING 100
vec3 GT7_FrameBufferValueToPhysicalValue(vec3 fbValue)
{
  // Converts linear frame-buffer value to physical luminance (cd/m^2)
  // where 1.0 corresponds to REFERENCE_LUMINANCE (e.g., 100 cd/m^2).
  return fbValue * PHYSICAL_SCALING;
}
vec3 GT7_PhysicalValueToFrameBufferValue(vec3 physical)
{
  // Converts physical luminance (cd/m^2) to a linear frame-buffer value,
  // where 1.0 corresponds to REFERENCE_LUMINANCE (e.g., 100 cd/m^2).
  return physical / PHYSICAL_SCALING;
}

vec3 GT7_EotfSt2084(vec3 n, float exponentScaleFactor /* = 1 */, float scaling /* = 10000 */)
{
  n = clamp(n, 0, 1);

  // Base functions from SMPTE ST 2084:2014
  // Converts from normalized PQ (0-1) to absolute luminance in cd/m^2 (linear light)
  // Assumes float input; does not handle integer encoding (Annex)
  // Assumes full-range signal (0-1)
  const float m1  = 0.1593017578125f;                // (2610 / 4096) / 4
  /* const */ float m2  = 78.84375f * exponentScaleFactor; // (2523 / 4096) * 128
  const float c1  = 0.8359375f;                      // 3424 / 4096
  const float c2  = 18.8515625f;                     // (2413 / 4096) * 32
  const float c3  = 18.6875f;                        // (2392 / 4096) * 32
  /* const */ float pqC = scaling;                         // Maximum luminance supported by PQ (cd/m^2)

  // Does not handle signal range from 2084 - assumes full range (0-1)
  vec3 np = pow(n, vec3(1.0 / (m2)));
  vec3 l  = np - c1;

  l = max(vec3(0), l);

  l = l / (c2 - c3 * np);
  l = pow(l, vec3(1.0 / (m1)));

  // Convert absolute luminance (cd/m^2) into the frame-buffer linear scale.
  return GT7_PhysicalValueToFrameBufferValue(l * pqC);
}

vec3 GT7_InverseEotfSt2084(vec3 v, float exponentScaleFactor /* = 1 */, float scaling /* = 10000 */)
{
  const float m1  = 0.1593017578125f;
  /* const */ float m2  = 78.84375f * exponentScaleFactor;
  const float c1  = 0.8359375f;
  const float c2  = 18.8515625f;
  const float c3  = 18.6875f;
  /* const */ float pqC = scaling;

  // Convert the frame-buffer linear scale into absolute luminance (cd/m^2).
  vec3 physical = GT7_FrameBufferValueToPhysicalValue(v);
  vec3 y        = physical / pqC; // Normalize for the ST-2084 curve

  vec3 ym = pow(y, vec3(m1));
  return exp2(m2 * (log2(c1 + c2 * ym) - log2(1.0f + c3 * ym)));
}

// -----------------------------------------------------------------------------
// Jzazbz conversion.
// Reference:
// Muhammad Safdar, Guihua Cui, Youn Jin Kim, and Ming Ronnier Luo,
// "Perceptually uniform color space for image signals including high dynamic
// range and wide gamut," Opt. Express 25, 15131-15151 (2017)
// Note: Coefficients adjusted for linear Rec.2020
// -----------------------------------------------------------------------------

vec3 GT7_RgbToICtCp(vec3 rgb)  // Input: linear Rec.2020
{
  vec3 lms;
  lms.x = (rgb.x * 1688.0f + rgb.y * 2146.0f + rgb.z * 262.0f) / 4096.0f;
  lms.y = (rgb.x * 683.0f + rgb.y * 2951.0f + rgb.z * 462.0f) / 4096.0f;
  lms.z = (rgb.x * 99.0f + rgb.y * 309.0f + rgb.z * 3688.0f) / 4096.0f;
  // lms = mul(renodx::color::BT2020_TO_XYZ_MAT, rgb);
  // lms = mul(renodx::color::ictcp::XYZ_TO_DOLBY_LMS_MAT, lms);
  // lms /= 4096.0f;

  vec3 lmsPQ = GT7_InverseEotfSt2084(lms, 1, 10000);

  vec3 ictCp;
  ictCp.x = (2048.0f * lmsPQ.x + 2048.0f * lmsPQ.y) / 4096.0f;
  ictCp.y = (6610.0f * lmsPQ.x - 13613.0f * lmsPQ.y + 7003.0f * lmsPQ.z) / 4096.0f;
  ictCp.z = (17933.0f * lmsPQ.x - 17390.0f * lmsPQ.y - 543.0f * lmsPQ.z) / 4096.0f;
  // ictCp = mul(renodx::color::ictcp::PLMS_TO_ICTCP_MAT, lms);
  // ictCp /= 4096.0f;

  return ictCp;
}

vec3 GT7_iCtCpToRgb(vec3 ictCp) // Output: linear Rec.2020
{
  vec3 lms;
  lms.x = ictCp.x + 0.00860904f * ictCp.y + 0.11103f * ictCp.z;
  lms.y = ictCp.x - 0.00860904f * ictCp.y - 0.11103f * ictCp.z;
  lms.z = ictCp.x + 0.560031f * ictCp.y - 0.320627f * ictCp.z;
  // lms = mul(renodx::color::ictcp::XYZ_TO_DOLBY_LMS_MAT, ictCp); //bruh, this needs invert?
  // lms = mul(renodx::color::XYZ_TO_BT2020_MAT, lms);

  vec3 lmsLin = GT7_EotfSt2084(lms, 1, 10000);

  vec3 rgb;
  rgb.x = max(3.43661f * lmsLin.x - 2.50645f * lmsLin.y + 0.0698454f * lmsLin.z, 0.0f);
  rgb.y = max(-0.79133f * lmsLin.x + 1.9836f * lmsLin.y - 0.192271f * lmsLin.z, 0.0f);
  rgb.z = max(-0.0259499f * lmsLin.x - 0.0989137f * lmsLin.y + 1.12486f * lmsLin.z, 0.0f);
  // rgb = mul(renodx::color::ictcp::PLMS_TO_ICTCP_MAT, lmsLin);
  return rgb;
}

// #define JZAZBZ_EXPONENT_SCALE_FACTOR 0.9f // Scale factor for exponent
// vec3 GT7_RgbToJzazbz(vec3 rgb) // Input: linear Rec.2020
// {
//   float l = rgb.x * 0.530004f + rgb.y * 0.355704f + rgb.z * 0.086090f;
//   float m = rgb.x * 0.289388f + rgb.y * 0.525395f + rgb.z * 0.157481f;
//   float s = rgb.x * 0.091098f + rgb.y * 0.147588f + rgb.z * 0.734234f;

//   // float lPQ = inverseEotfSt2084(l, JZAZBZ_EXPONENT_SCALE_FACTOR);
//   // float mPQ = inverseEotfSt2084(m, JZAZBZ_EXPONENT_SCALE_FACTOR);
//   // float sPQ = inverseEotfSt2084(s, JZAZBZ_EXPONENT_SCALE_FACTOR);
//   // vec3 lms = PQEncode(vec3(l,m,s));
//   vec3 lms = inverseEotfSt2084(vec3(l,m,s), JZAZBZ_EXPONENT_SCALE_FACTOR);
//   float lPQ = lms.x;
//   float mPQ = lms.y;
//   float sPQ = lms.z;
  
//   float iz = 0.5f * lPQ + 0.5f * mPQ;
  
//   vec3 jab;
//   jab.x = (0.44f * iz) / (1.0f - 0.56f * iz) - 1.6295499532821566e-11f;
//   jab.y = 3.524000f * lPQ - 4.066708f * mPQ + 0.542708f * sPQ;
//   jab.z = 0.199076f * lPQ + 1.096799f * mPQ - 1.295875f * sPQ;
//   return jab;
//   }
// vec3 GT7_JzazbzToRgb(vec3 jab) // Output: linear Rec.2020
// {
//   float jz = jab.x + 1.6295499532821566e-11f;
//   float iz = jz / (0.44f + 0.56f * jz);
//   float a  = jab.y;
//   float b  = jab.z;
  
//   float l = iz + a * 1.386050432715393e-1f + b * 5.804731615611869e-2f;
//   float m = iz + a * -1.386050432715393e-1f + b * -5.804731615611869e-2f;
//   float s = iz + a * -9.601924202631895e-2f + b * -8.118918960560390e-1f;
  
//   // float lLin = eotfSt2084(l, JZAZBZ_EXPONENT_SCALE_FACTOR);
//   // float mLin = eotfSt2084(m, JZAZBZ_EXPONENT_SCALE_FACTOR);
//   // float sLin = eotfSt2084(s, JZAZBZ_EXPONENT_SCALE_FACTOR);
//   // vec3 lms = PQDecode(vec3(l,m,s));
//   vec3 lms = eotfSt2084(vec3(l,m,s), JZAZBZ_EXPONENT_SCALE_FACTOR);
//   float lLin = lms.x;
//   float mLin = lms.y;
//   float sLin = lms.z;

//   vec3 rgb;
//   rgb.x = lLin * 2.990669f + mLin * -2.049742f + sLin * 0.088977f;
//   rgb.y = lLin * -1.634525f + mLin * 3.145627f + sLin * -0.483037f;
//   rgb.z = lLin * -0.042505f + mLin * -0.377983f + sLin * 1.448019f;
//   return rgb;
// }

struct GT7_CurveK {
  float k, kA, kB, kC, alpha, grayPoint, linearSection, toeStrength, peak;
}; 

GT7_CurveK GT7_CreateCurve(float peak, float alpha /* = 0.25f */, float grayPoint /* = 0.538f */, float linearSection /* = 0.444f */, float toeStrength /* = 1.280f */) {
  GT7_CurveK curveK;

  curveK.peak = peak;
  curveK.alpha = alpha;
  curveK.grayPoint = grayPoint;
  curveK.linearSection = linearSection;
  curveK.toeStrength = toeStrength;
  
  curveK.k = (curveK.linearSection - 1.0f) / (curveK.alpha - 1.0f);
  curveK.kA = curveK.peak * curveK.linearSection + curveK.peak * curveK.k;
  curveK.kB = -curveK.peak * curveK.k * exp(curveK.linearSection / curveK.k);
  curveK.kC = -1.0 / (curveK.k * curveK.peak);

  return curveK;
}

float GT7_Curve(float x, GT7_CurveK curvek) {
  if (x < 0.0f)
  {
    return 0.0f;
  }
  if (x < curvek.linearSection * curvek.peak)
  {
    float weightLinear = smoothstep(0.0f, curvek.grayPoint, x);
    float weightToe    = 1.0f - weightLinear;

    float toeMapped = curvek.grayPoint * pow(x / curvek.grayPoint, curvek.toeStrength);
    return weightToe * toeMapped + weightLinear * x;
  }
  else
  {
    float shoulder = curvek.kA + curvek.kB * exp(x * curvek.kC);
    return shoulder;
  }
}

vec3 GT7_Curve(vec3 x, GT7_CurveK curvek){
  return vec3(
    GT7_Curve(x.x, curvek),
    GT7_Curve(x.y, curvek),
    GT7_Curve(x.z, curvek)
  );
}

float GT7_ChromaCurve(float x, float a, float b)
{
  return 1.0f - smoothstep(a, b, x);
}

struct GT7_Config {
  float peak;
  float grayPoint;   // gray point for toe end
  float alpha;   // white clip strength
  float linearSection;   // the length of linear section
  float toeStrength;  // toe contrast
  float blowoutAmount;  // blend intensity
  float blowoutStart;  // blowout start
  float blowoutEnd;  // blowout end
} config;

GT7_Config GT7_BuildConfig() {
  // Config config;
  config.peak = (RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS);
  config.grayPoint =      RENODX_GT7_M;            // gray point for toe end
  config.alpha =          RENODX_GT7_A;            // white clip strength
  config.linearSection =  RENODX_GT7_L;            // the length of linear section
  config.toeStrength =    RENODX_GT7_C;            // toe contrast
  config.blowoutAmount =  RENODX_GT7_BLOW_AMOUNT;  // blend intensity
  config.blowoutStart =   RENODX_GT7_BLOW_START;   // blowout start
  config.blowoutEnd =     RENODX_GT7_BLOW_END;     // blowout end
  return config;
} 

// Input:  linear Rec.2020 RGB (frame buffer values)
// Output: tone-mapped RGB (frame buffer values);
//         - in SDR mode: mapped to [0, 1], ready for sRGB OETF
//         - in HDR mode: mapped to [0, framebufferLuminanceTarget_], ready for PQ inverse-EOTF
// Note: framebufferLuminanceTarget_ represents the display's target peak luminance converted to a frame buffer value.
//       The returned values are suitable for applying the appropriate OETF to generate final output signal.
vec3 GT7_BT2020(vec3 rgb, GT7_Config config) {
    float framebufferLuminanceTarget = config.peak;
    float framebufferLuminanceTargetUcs = GT7_RgbToICtCp(vec3(framebufferLuminanceTarget)).x; // Use the first UCS component (I or Jz) as luminance

    // Per-channel tone mapping ("skewed" color).
    GT7_CurveK curveK = GT7_CreateCurve(config.peak, config.alpha, config.grayPoint, config.linearSection, config.toeStrength);
    vec3 skewedRgb = GT7_Curve(rgb, curveK);

    // Convert to UCS to separate luminance and chroma.
    vec3 ucs = GT7_RgbToICtCp(rgb);
    
    vec3 skewedUcs = GT7_RgbToICtCp(skewedRgb);
    
    float chromaScale = GT7_ChromaCurve(ucs.x / framebufferLuminanceTargetUcs, config.blowoutStart, config.blowoutEnd);

     vec3 scaledUcs = vec3( skewedUcs.x,           // Luminance from skewed color
                            ucs.y * chromaScale,   // Scaled chroma components
                            ucs.z * chromaScale );

    // Convert back to RGB.
    vec3 scaledRgb = GT7_iCtCpToRgb(scaledUcs);

    // Final blend between per-channel and UCS-scaled results.
    vec3 blended = (1.0f - vec3(config.blowoutAmount)) * skewedRgb + vec3(config.blowoutAmount) * scaledRgb;

    return blended;
}

vec3 ToneMapPass_GT7(vec3 color) {
  color = ColorSpaceConversion(color, RENODX_WORKINGCS_SHADERPACK, RENODX_CS_BT2020);
  color = max(vec3(0), color);

  //do
  GT7_Config config = GT7_BuildConfig();
  color = GT7_BT2020(color, config);

  //clamp overshoot
  // color = min(vec3(config.peak), color); 
  // color = ClampByMaxScaling(color, config.peak);

  color = ColorSpaceConversion(color, RENODX_CS_BT2020, RENODX_WORKINGCS_AFTERTONEMAP);

  return color;
}
#endif

// Exponential Rolloff ///////////////////////////////////////////////////////////////////////////

//DICE
vec3 ExponentialRollOff(vec3 x, float rolloff_start, float output_max) { 
  vec3 rolloff_size = vec3(output_max - rolloff_start);
  vec3 overage = -max(vec3(0), x - rolloff_start);
  vec3 rolloff_value = vec3(1.0f) - exp(overage / rolloff_size);     
  vec3 new_overage = rolloff_size * rolloff_value + overage;
  return x + new_overage;
}
float ExponentialRollOff(float x, float rolloff_start, float output_max) { 
  float rolloff_size = output_max - rolloff_start;
  float overage = -max(0, x - rolloff_start);
  float rolloff_value = 1.0f - exp(overage / rolloff_size);
  float new_overage = rolloff_size * rolloff_value + overage;
  return x + new_overage;
}

vec3 ToneMapPass_ExponentialRollOff(vec3 color) {
  //setup
  float peak = (RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS);
  float shoulder = (RENODX_SHOULDER_START / RENODX_GAME_BRIGHTNESS);

  //do
  color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
  #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
    float y = YOrMaxChannelForHDRTonemap(color);
    if (y > 0) {
      float y1 = ExponentialRollOff(y, shoulder, peak);
      color *= y1/y;
    }
  #else
    vec3 s = Sign(color);
    color = abs(color);
    color = ExponentialRollOff(color, shoulder, peak);
    color *= s;
  #endif
  color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);

  return color;
}


// Uncharted2 / Hable Extended //////////////////////////////////////////////////////////////////////////////
#if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_HABLE
//https://github.com/mqhaji/renodx/blob/main/src/games/batmanak/uncharted2extended.hlsli

float Hable_ApplyCurve(float x, float a, float b, float c, float d, float e, float f) {
  float numerator = x * (a * x + c * b) + d * e;  // x * (a * x + c * b) + d * e
  float denominator = x * (a * x + b) + d * f;    // x * (a * x + b) + d * f
  return (numerator / denominator) - (e / f);
}

vec3 Hable_ApplyCurve(vec3 x, float a, float b, float c, float d, float e, float f) {
  vec3 numerator = x * (a * x + c * b) + d * e;  // x * (a * x + c * b) + d * e
  vec3 denominator = x * (a * x + b) + d * f;    // x * (a * x + b) + d * f
  return (numerator / denominator) - (e / f);
}

float Hable_InverseUncharted2(
    float y, float W,
    float A, float B, float C, float D, float E, float F) {
  // 1. Recover raw ApplyCurve output: y_raw = y * ApplyCurve(W)
  float rawW = Hable_ApplyCurve(W, A, B, C, D, E, F);
  float y_raw = y * rawW;

  // 2. Solve inverse of ApplyCurve analytically (quadratic)
  float ef = E / F;
  float yp = y_raw + ef;

  // Quadratic coefficients:
  // A_q x^2 + B_q x + C_q = 0
  float A_q = A * (yp - 1.0);
  float B_q = B * (yp - C);
  float C_q = D * (F * yp - E);

  // Quadratic discriminant
  float disc = B_q * B_q - 4.0 * A_q * C_q;
  disc = max(disc, 0.0);
  float sqrtD = sqrt(disc);

  float x1 = (-B_q + sqrtD) / (2.0 * A_q);
  float x2 = (-B_q - sqrtD) / (2.0 * A_q);

  // pick the physically meaningful root (positive, usually x1)
  return max(x1, x2);
}

vec3 Hable_InverseUncharted2(
    vec3 color, float W,
    float A, float B, float C, float D, float E, float F) {
  return vec3(
      Hable_InverseUncharted2(color.r, W, A, B, C, D, E, F),
      Hable_InverseUncharted2(color.g, W, A, B, C, D, E, F),
      Hable_InverseUncharted2(color.b, W, A, B, C, D, E, F));
}

float Hable_Derivative(
    float x,
    float a, float b, float c,
    float d, float e, float f) {
  float num = -a * b * (c - 1.0) * x * x
              + 2.0 * a * d * (f - e) * x
              + b * d * (c * f - e);

  float den = x * (a * x + b) + d * f;
  den = den * den;

  return num / den;
}

// Root of f'(x) = 0 for the raw ApplyCurve, using quadratic formula.
// With a,b,c,d,e,f > 0 and 0 < c < 1, this is well-defined.
float Hable_FindDerivativeRoot(
    float a, float b, float c,
    float d, float e, float f) {
  // Quadratic coefficients for numerator of f'(x)
  // -a*b*(c - 1) * x^2 + 2*a*d*(f - e)*x + b*d*(c*f - e) = 0
  float Aq = a * b * (1.f - c);  // -a*b*(c-1)
  float Bq = 2.f * a * d * (f - e);
  float Cq = b * d * (c * f - e);

  // Discriminant
  float disc = Bq * Bq - 4.f * Aq * Cq;
  disc = max(disc, 0.f);  // just in case of tiny negatives

  float sqrtDisc = sqrt(disc);

  float r1 = (-Bq + sqrtDisc) / (2.f * Aq);
  float r2 = (-Bq - sqrtDisc) / (2.f * Aq);

  // Larger root of the quadratic
  float root = max(r1, r2);

  // Only care about non-negative x in our domain
  return max(root, 0.f);
}

// Analytic knee root of f'''(x) = 0 for Uncharted2/Hable ApplyCurve
// a,b,c,d,e,f > 0, typically 0 < c < 1.
// Returns the smallest positive real root ("first knee") in x > 0.
float Hable_FindThirdDerivativeRoot(float a, float b, float c, float d, float e, float f) {
  // sqrt(a b^2 c^2 - 2 a b^2 c + a b^2)
  float sqrt_ab = sqrt(
      a * b * b * c * c
      - 2.f * a * b * b * c
      + a * b * b);

  // sqrt(a d^2 e^2 - 2 a d^2 e f + a d^2 f^2
  //    + b^2 c^2 d f + b^2 (-c) d e - b^2 c d f + b^2 d e)
  float sqrt_df = sqrt(
      a * d * d * e * e
      - 2.f * a * d * d * e * f
      + a * d * d * f * f
      + b * b * c * c * d * f
      + b * b * (-c) * d * e
      - b * b * c * d * f
      + b * b * d * e);

  // Precompute (d e - d f)
  float de_df = d * e - d * f;

  // Inner big piece: sqrt_ab * (...) / (8 * sqrt_df)
  float term_top =
      32.f * (a * d * d * e * f - a * d * d * f * f + b * b * c * d * f - b * b * d * e)
      / (a * a * b * (c - 1.f));

  float term_mid =
      96.f * de_df * (c * d * f - d * e)
      / (a * b * (c - 1.f) * (c - 1.f));

  float de_df2 = de_df * de_df;
  float de_df3 = de_df2 * de_df;

  float term_tail =
      64.f * de_df3
      / (b * b * b * (c - 1.f) * (c - 1.f) * (c - 1.f));

  float Tfrac = sqrt_ab * (term_top - term_mid - term_tail)
                / (8.f * sqrt_df);

  // (12 a^2 b c d f - 12 a^2 b d e) / (6 (a^3 b c - a^3 b))
  float Tmid2_num = 12.f * a * a * b * c * d * f
                    - 12.f * a * a * b * d * e;
  float Tmid2_den = 6.f * (a * a * a * b * c - a * a * a * b);
  float Tmid2 = Tmid2_num / Tmid2_den;

  // (6 (c d f - d e))/(a (c - 1))
  float T3 = 6.f * (c * d * f - d * e)
             / (a * (c - 1.f));

  // (8 (d e - d f)^2)/(b^2 (c - 1)^2)
  float T4 = 8.f * de_df2
             / (b * b * (c - 1.f) * (c - 1.f));

  // Centers for the ± branches
  float centerNeg = -Tfrac + Tmid2 + T3 + T4;  // used with sqrt(-centerNeg)
  float centerPos = Tfrac + Tmid2 + T3 + T4;   // used with sqrt( centerPos)

  // Branch square roots: use SignSqrt for robustness and correct branch behaviour
  float sNeg = SignSqrt(-centerNeg);
  float sPos = SignSqrt(centerPos);

  // Shifts:
  //  - first two roots use:  - sqrt_df/sqrt_ab - (d e - d f)/(b (c - 1))
  //  - last two use:          sqrt_df/sqrt_ab - (d e - d f)/(b (c - 1))
  float shift1 = sqrt_df / sqrt_ab + de_df / (b * (c - 1.f));  // we subtract this
  float shift2 = sqrt_df / sqrt_ab - de_df / (b * (c - 1.f));  // we add this

  // The four analytic roots from WA, mapped to floats:
  float r1 = -0.5f * sNeg - shift1;  // -1/2 * sqrt(-centerNeg) - shift1
  float r2 = 0.5f * sNeg - shift1;   //  1/2 * sqrt(-centerNeg) - shift1
  float r3 = -0.5f * sPos + shift2;  // -1/2 * sqrt( centerPos) + shift2
  float r4 = 0.5f * sPos + shift2;   //  1/2 * sqrt( centerPos) + shift2

  // Max root seems to be always be the right one
  float root = clamp(max(r1, max(r2, max(r3, r4))), 0, 1);

  return root;
}

struct Hable_Uncharted2ExtendedConfig {
  float pivot_point;
  float white_precompute;
  float coeffs[6];  // A,B,C,D,E,F
};

Hable_Uncharted2ExtendedConfig Hable_CreateUncharted2ExtendedConfig(
    float pivot_point,
    float coeffs[6], float white_precompute) {
  Hable_Uncharted2ExtendedConfig cfg;
  cfg.pivot_point = pivot_point;
  cfg.white_precompute = white_precompute;
  cfg.coeffs = coeffs;

  return cfg;
}

Hable_Uncharted2ExtendedConfig Hable_CreateUncharted2ExtendedConfig(float coeffs[6], float white_precompute) {
  float pivot_point = Hable_FindThirdDerivativeRoot(coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5]);
  return Hable_CreateUncharted2ExtendedConfig(pivot_point, coeffs, white_precompute);
}

float Hable_ApplyExtended(                                                                             
    float x, float base, float pivot_point, float white_precompute,
    float A, float B, float C, float D, float E, float F) {                                  
  float pivot_x = pivot_point;
  float pivot_y = Hable_ApplyCurve(pivot_x, A, B, C, D, E, F) * white_precompute;
  float slope = Hable_Derivative(pivot_x, A, B, C, D, E, F) * white_precompute;
  float offset = pivot_y - slope * pivot_x;

  float extended = slope * x + offset; 

  return mix(base, extended, step(pivot_x, x));
}
float Hable_ApplyExtended(float x, float base, Hable_Uncharted2ExtendedConfig uc2_config) {
  return Hable_ApplyExtended(
      x, base, uc2_config.pivot_point, uc2_config.white_precompute,
      uc2_config.coeffs[0], uc2_config.coeffs[1], uc2_config.coeffs[2],
      uc2_config.coeffs[3], uc2_config.coeffs[4], uc2_config.coeffs[5]);
}
float Hable_ApplyExtended(float x, Hable_Uncharted2ExtendedConfig uc2_config) {
  float base =
      Hable_ApplyCurve(x, uc2_config.coeffs[0], uc2_config.coeffs[1], uc2_config.coeffs[2],
                                  uc2_config.coeffs[3], uc2_config.coeffs[4], uc2_config.coeffs[5])
      * uc2_config.white_precompute;
  return Hable_ApplyExtended(x, base, uc2_config);
}

vec3 Hable_ApplyExtended(                                                                             
    vec3 x, vec3 base, float pivot_point, float white_precompute,
    float A, float B, float C, float D, float E, float F) {                                  
  float pivot_x = pivot_point;
  float pivot_y = Hable_ApplyCurve(pivot_x, A, B, C, D, E, F) * white_precompute;
  float slope = Hable_Derivative(pivot_x, A, B, C, D, E, F) * white_precompute;
  vec3 offset = vec3(pivot_y - slope * pivot_x);

  vec3 extended = vec3(slope) * x + offset;

  return mix(base, extended, step(vec3(pivot_x), x));
}
vec3 Hable_ApplyExtended(vec3 x, vec3 base, Hable_Uncharted2ExtendedConfig uc2_config) {
  return Hable_ApplyExtended(
      x, base, uc2_config.pivot_point, uc2_config.white_precompute,
      uc2_config.coeffs[0], uc2_config.coeffs[1], uc2_config.coeffs[2],
      uc2_config.coeffs[3], uc2_config.coeffs[4], uc2_config.coeffs[5]);
}
vec3 Hable_ApplyExtended(vec3 x, Hable_Uncharted2ExtendedConfig uc2_config) {
  vec3 base =
      Hable_ApplyCurve(x, uc2_config.coeffs[0], uc2_config.coeffs[1], uc2_config.coeffs[2],
                                  uc2_config.coeffs[3], uc2_config.coeffs[4], uc2_config.coeffs[5])
      * uc2_config.white_precompute;
  return Hable_ApplyExtended(x, base, uc2_config);
}

vec3 ToneMapPass_Hable(vec3 color) {
  //boost
  color *= 2.0;

  //setup
  float[6] coeffs = float[6]( RENODX_HABLE_A, RENODX_HABLE_B, RENODX_HABLE_C, RENODX_HABLE_D, RENODX_HABLE_E, RENODX_HABLE_F );
  float white_precompute = 1.f / Hable_ApplyCurve(RENODX_HABLE_W, RENODX_HABLE_A, RENODX_HABLE_B, RENODX_HABLE_C, RENODX_HABLE_D, RENODX_HABLE_E, RENODX_HABLE_F);
  Hable_Uncharted2ExtendedConfig uc2_config = Hable_CreateUncharted2ExtendedConfig(coeffs, white_precompute);

  float peak = (RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS);
  float shoulder = (RENODX_SHOULDER_START / RENODX_GAME_BRIGHTNESS);

  //do
  color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
  #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
    float y = YOrMaxChannelForHDRTonemap(color);
    if (y > 0) {
      float y1 = y;
      y1 = Hable_ApplyExtended(yy1, uc2_config);
      y1 = ExponentialRollOff(y1, 0.8, peak);
      color *= y1/y;
    }
  #else
    vec3 s = Sign(color);
    color = abs(color);
    color = Hable_ApplyExtended(color, uc2_config);
    color = ReinhardPiecewiseExtended(color, 100, peak, shoulder); //good enough for blowout
    color *= s;
  #endif
  color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);

  return color;
}
#endif

// NeuTwo ///////////////////////////////////////////////////////////////////////////
#if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_NEUTWO
// https://github.com/clshortfuse/renodx/blob/main/src/shaders/tonemap/neutwo.hlsl

// Neutral tonemap
// Based on power of 2 (squared/sqrt)
// Naka-Rushton/Reinhard style tonemapper x/(x^2+k)^(1/2)
// Newton-Raphson friendly with rsqrt (faster than division)
// f'''(x) = 0 at x = 0.5 (half peak)
// https://www.desmos.com/calculator/gy1edro6nd
// Polar/Cartesian form of peak * cos(atan2(x, peak))
// Invertible with same complexity as forward

// f\left(x\right)=\frac{x}{\sqrt{xx+1}}
float Neutwo(float x) {
  // also written as x * rhypot(x, 1.0)
  float numerator = x;
  float denominator_squared = (x * x + 1.0);
  return numerator * inversesqrt(denominator_squared);
}

// f_{p}\left(x\right)=\frac{px}{\sqrt{xx+pp}}
float Neutwo(float x, float peak) {
  // also written as x * rhypot(x, peak)
  float p = peak;

  float numerator = p * x;
  float denominator_squared = (x * x + (p * p));
  return numerator * inversesqrt(denominator_squared);
}
vec3 Neutwo(vec3 x, float peak) {
  // also written as x * rhypot(x, peak)
  float p = peak;

  vec3 numerator = p * x;
  vec3 denominator_squared = (x * x + (p * p));
  return numerator * inversesqrt(denominator_squared);
}

// // f_{c}\left(x\right)=\frac{cpx}{\sqrt{xx\cdot\left(cc-pp\right)+\left(cc\cdot pp\right)}}
// float Neutwo(float x, float peak, float clip) {
//   float p = peak;
//   float c = clip;
//   float cc = c * c;
//   float pp = p * p;
//   float xx = x * x;
// 
//   float numerator = c * p * x;
//   float denominator_squared = (xx * (cc - pp) + (cc * pp));
// 
//   return numerator * inversesqrt(denominator_squared);
// }
// vec3 Neutwo(vec3 x, float peak, float clip) {
//   float p = peak;
//   float c = clip;
//   float cc = c * c;
//   float pp = p * p;
//   vec3 xx = x * x;
// 
//   vec3 numerator = c * p * x;
//   vec3 denominator_squared = (xx * (cc - pp) + (cc * pp));
// 
//   return numerator * inversesqrt(denominator_squared);
// }
// 
// // f_{g}\left(x\right)=\frac{pgx\left(cc-gg\right)}{\sqrt{\left(cc-gg\right)\cdot gg\cdot\left(xx\cdot\left(cc-pp\right)+cc\cdot\left(pp-gg\right)\right)}}
// float Neutwo(float x, float peak, float clip, float gray) {
//   float p = peak;
//   float g = gray;
//   float c = clip;
// 
//   float cc = c * c;
//   float pp = p * p;
//   float gg = g * g;
//   float xx = x * x;
//   float cc_minus_gg = cc - gg;
// 
//   float numerator = p * g * x * cc_minus_gg;
//   float denominator_squared = cc_minus_gg * gg * (xx * (cc - pp) + (cc * (pp - gg)));
//   return numerator * inversesqrt(denominator_squared);
// }
// vec3 Neutwo(vec3 x, float peak, float clip, float gray) {
//   float p = peak;
//   float g = gray;
//   float c = clip;
// 
//   float cc = c * c;
//   float pp = p * p;
//   float gg = g * g;
//   vec3 xx = x * x;
//   float cc_minus_gg = cc - gg;
// 
//   vec3 numerator = p * g * x * cc_minus_gg;
//   vec3 denominator_squared = cc_minus_gg * gg * (xx * (cc - pp) + (cc * (pp - gg)));
//   return numerator * inversesqrt(denominator_squared);
// }

// // f_{o}\left(x\right)=\frac{pox\left(cc-gg\right)}{\sqrt{\left(cc-gg\right)\cdot\left(xx\cdot\left(ccoo-ppgg\right)+ccgg\cdot\left(pp-oo\right)\right)}}
// float Neutwo(float x, float peak, float clip, float gray_in, float gray_out) {
//   float p = peak;
//   float g = gray_in;
//   float o = gray_out;
// 
//   float cc = clip * clip;
//   float pp = peak * peak;
//   float gg = g * g;
//   float oo = o * o;
//   float xx = x * x;
// 
//   float cc_minus_gg = cc - gg;
// 
//   float numerator = p * o * x * cc_minus_gg;
// 
//   float ccoo = cc * oo;
//   float ppgg = pp * gg;
//   float ccgg = cc * gg;
// 
//   float denominator_squared = cc_minus_gg * (xx * (ccoo - ppgg) + (ccgg * (pp - oo)));
// 
//   return numerator * inversesqrt(denominator_squared);
// }
//
// // f_{m}\left(x\right)=\frac{qzx\left(cc-gg\right)}{\sqrt{\left(cc-gg\right)\cdot\left(xx\cdot\left(cczz-qqgg\right)+ccgg\cdot\left(qq-zz\right)\right)}}+m
// float Neutwo(float x, float peak, float clip, float gray_in, float gray_out, float minimum) {
//   float m = minimum;
//   float g = gray_in;
//   float z = gray_out - m;
//   float q = peak - m;
//   float c = clip;
// 
//   float cc = c * c;
//   float gg = g * g;
//   float cc_minus_gg = cc - gg;
// 
//   float numerator = q * z * x * cc_minus_gg;
// 
//   float xx = x * x;
//   float zz = z * z;
//   float qq = q * q;
// 
//   float cczz = cc * zz;
//   float qqgg = qq * gg;
//   float ccgg = cc * gg;
// 
//   float denominator_squared = cc_minus_gg * (xx * (cczz - qqgg) + ccgg * (qq - zz));
// 
//   return (numerator * inversesqrt(denominator_squared) + m);
// }

vec3 ToneMapPass_NeuTwo(vec3 color) {
  //setup
  float p = RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS; // Peak (nits)

  //do
  color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
  #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
    float y = YOrMaxChannelForHDRTonemap(color);
    if (y > 0) {
      float y1 = Neutwo(y, p/* , w, 0.18 */);
      color *= y1 / y;
    }
  #else
    vec3 s = Sign(color);
    color = abs(color);
    color = Neutwo(color, p/* , w, 0.18 */);
    color *= s;
  #endif
  color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);

  return color;
}
#endif

// Frostbite ///////////////////////////////////////////////////////////////////////////
#if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_FROSTBITE
// https://www.ea.com/frostbite/news/high-dynamic-range-color-grading-and-display-in-frostbite
// Aplies exponential ("Photographic") luma compression
float Frostbite_RangeCompress(float x) {
  return 1.0 - exp(-x);
}

float Frostbite_RangeCompress(float val, float threshold, float max_value/*  = 1.f */) {
  if (val < threshold) {
    return val;
  } else if (max_value <= threshold) {
    return threshold;
  } else {
    float range = max_value - threshold;
    return threshold + range * Frostbite_RangeCompress((val - threshold) / range);
  }
}

vec3 Frostbite_RangeCompress(vec3 val, float threshold, float max_value/*  = 1.f */) {
  return vec3(
      Frostbite_RangeCompress(val.x, threshold, max_value),
      Frostbite_RangeCompress(val.y, threshold, max_value),
      Frostbite_RangeCompress(val.z, threshold, max_value));
}

vec3 Frostbite_BT709(vec3 col, float max_value/*  = 1.f */, float rolloff_start/* = 0.25f */, float saturation_boost_amount/* = 0.3f */, float hue_correct_amount/* = 0.6f */) {
  vec3 ictcp = ICtCpFromBT709(col);

  // Hue-preserving range compression requires desaturation in order to achieve a natural look. We adaptively desaturate the input based on its luminance.

  float saturationAmount = pow(smoothstep(1.0, 0.3, ictcp.x), 1.3);
  col = BT709FromICtCp(ictcp.xyz * vec3(1, saturationAmount, saturationAmount));

  // Only compress luminance starting at a certain point. Dimmer inputs are passed through without modification.
  float linearSegmentEnd = rolloff_start;

  // Hue-preserving mapping
  float maxCol = max(col.x, max(col.y, col.z));
  float mappedMax = Frostbite_RangeCompress(maxCol, linearSegmentEnd, max_value);
  vec3 compressedHuePreserving = col * mappedMax / maxCol;

  // Non-hue preserving mapping
  vec3 perChannelCompressed = Frostbite_RangeCompress(col, linearSegmentEnd, max_value);

  // Combine hue-preserving and non-hue-preserving colors. Absolute hue preservation looks unnatural, as bright colors *appear* to have been hue shifted.
  // Actually doing some amount of hue shifting looks more pleasing
  col = mix(perChannelCompressed, compressedHuePreserving, hue_correct_amount);

  vec3 ictcpMapped = ICtCpFromBT709(col);

  // Smoothly ramp off saturation as brightness increases, but keep some even for very bright input
  float postCompressionSaturationBoost = saturation_boost_amount * smoothstep(1.0, 0.5, ictcp.x);

  // Re-introduce some hue from the pre-compression color. Something similar could be accomplished by delaying the luma-dependent desaturation before range compression.
  // Doing it here however does a better job of preserving perceptual luminance of highly saturated colors. Because in the hue-preserving path we only range-compress the max channel,
  // saturated colors lose luminance. By desaturating them more aggressively first, compressing, and then re-adding some saturation, we can preserve their brightness to a greater extent.
  ictcpMapped.yz = mix(ictcpMapped.yz, ictcp.yz * ictcpMapped.x / max(1e-3, ictcp.x), postCompressionSaturationBoost);

  col = BT709FromICtCp(ictcpMapped);

  return col;
}

vec3 ToneMapPass_Frostbite(vec3 color) {
  //setup
  float p = RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS; // Peak (nits)
  float s = RENODX_SHOULDER_START / RENODX_GAME_BRIGHTNESS; //shoulder start

  //do
  color = ColorSpaceConversion(color, RENODX_WORKINGCS_SHADERPACK, RENODX_CS_BT709);
  // color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
  #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
    float y = YOrMaxChannelForHDRTonemap(color, RENODX_CS_BT709);
    if (y > 0) {
      float y1 = Frostbite_RangeCompress(y, s, p);
      color *= y1 / y;
    }
  #else
    // vec3 s = Sign(color);
    // color = abs(color);
    color = Frostbite_BT709(color, p, s, RENODX_FROSTBITE_SAT, RENODX_FROSTBITE_HUE);
    // color *= s;

    // color = ClampByMaxScaling(color, p);
    // color = min(color, vec3(p));
  #endif
  // color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);
  color = ColorSpaceConversion(color, RENODX_CS_BT709, RENODX_WORKINGCS_AFTERTONEMAP);

  return color;
}
#endif

// Daniele ///////////////////////////////////////////////////////////////////////////

// #if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_DANIELE
// // https://community.acescentral.com/t/output-transform-tone-scale/3498/213?page=12
// // https://www.desmos.com/calculator/d37z5t5lr5
// /*     
// float n_r = 100.f,        // reference nits
// float n = 1000.f,         // peak nits
// float g = 1.1f,           // surround/contrast
// float c = 0.18f,          // scene-referred gray
// float c_d = 10.f,         // output gray in nits
// float w_g = 10.013f,      // gray change
// float t_1 = 0.01f,        // shadow toe
// float r_hit_min = 128.f,  // minimum hit nits
// float r_hit_max = 256.f   // maximum hit nits 
// */
// float Daniele(float x,
//   float n_r,
//   float n, 
//   float g, 
//   float c, 
//   float c_d, 
//   float w_g, 
//   float t_1, 
//   float r_hit_min,
//   float r_hit_max
// ) {
//   float m_0 = (n / n_r);
// 
//   float m_1 = 0.5 * (m_0 + sqrt(m_0 * (m_0 + (4.0 * t_1))));
//   float r_hit = r_hit_min + ((r_hit_max - r_hit_min) * (log(m_0) / log(10000.0 / 100.0)));
// 
//   float u = pow((r_hit / m_1) / ((r_hit / m_1) + 1.0), g);
//   float m = m_1 / u;
//   float w_i = log(n / 100.0) / log(2.0);
//   float c_t = (c_d / n_r) * (1.0 + (w_i * w_g));
//   float g_ip = 0.5 * (c_t + sqrt(c_t * (c_t + (4.0 * t_1))));
//   float g_ipp2 = -m_1 * pow(g_ip / m, 1.0 / g) / (pow(g_ip / m, 1.0 / g) - 1.0);
//   float w_2 = c / g_ipp2;
//   float s_2 = w_2 * m_1;
//   float u_2 = pow((r_hit / m_1) / ((r_hit / m_1) + w_2), g);
//   float m_2 = m_1 / u_2;
// 
//   float ts = pow(max(0, x) / (x + s_2), g) * m_2;
//   float flared = max(0, (ts * ts) / (ts + t_1));
// 
//   return flared;
// }
// vec3 Daniele(vec3 x,
//   float n_r,
//   float n, 
//   float g, 
//   float c, 
//   float c_d, 
//   float w_g, 
//   float t_1, 
//   float r_hit_min,
//   float r_hit_max
// ) {
//   float m_0 = (n / n_r);
// 
//   float m_1 = 0.5 * (m_0 + sqrt(m_0 * (m_0 + (4.0 * t_1))));
//   float r_hit = r_hit_min + ((r_hit_max - r_hit_min) * (log(m_0) / log(10000.0 / 100.0)));
// 
//   float u = pow((r_hit / m_1) / ((r_hit / m_1) + 1.0), g);
//   float m = m_1 / u;
//   float w_i = log(n / 100.0) / log(2.0);
//   float c_t = (c_d / n_r) * (1.0 + (w_i * w_g));
//   float g_ip = 0.5 * (c_t + sqrt(c_t * (c_t + (4.0 * t_1))));
//   float g_ipp2 = -m_1 * pow(g_ip / m, 1.0 / g) / (pow(g_ip / m, 1.0 / g) - 1.0);
//   float w_2 = c / g_ipp2;
//   float s_2 = w_2 * m_1;
//   float u_2 = pow((r_hit / m_1) / ((r_hit / m_1) + w_2), g);
//   float m_2 = m_1 / u_2;
// 
//   vec3 ts = pow(max(vec3(0), x) / (x + s_2), vec3(g)) * m_2;
//   vec3 flared = max(vec3(0), (ts * ts) / (ts + t_1));
// 
//   return flared;
// }
// 
// vec3 ToneMapPass_Daniele(vec3 color) {
//   //setup
//   float n_r = 100.f / RENODX_GAME_BRIGHTNESS.;        // reference nits
//   float n = RENODX_PEAK_BRIGHTNESS.;           // peak nits
//   float g =         RENODX_DANIELE_G;          // surround/contrast
//   float c =         RENODX_DANIELE_C;          // scene-referred gray
//   float c_d =       RENODX_DANIELE_C_D;        // output gray in nits
//   float w_g =       RENODX_DANIELE_W_G;        // gray change
//   float t_1 =       RENODX_DANIELE_T_1;        // shadow toe
//   float r_hit_min = RENODX_DANIELE_R_HIT_MIN;  // minimum hit nits
//   float r_hit_max = RENODX_DANIELE_R_HIT_MAX;  // maximum hit nits
// 
//   //do
//   color = WorkingColorSpace_Shaderpack_To_HDRTonemap(color);
//   #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
//     float y = YOrMaxChannelForHDRTonemap(color);
//     if (y > 0) {
//       float y1 = Daniele(y, n_r, n, g, c, c_d, w_g, t_1, r_hit_min, r_hit_max);
//       color *= y1 / y;
//     }
//   #else
//     vec3 s = Sign(color);
//     color = abs(color);
//     color = Daniele(color, n_r, n, g, c, c_d, w_g, t_1, r_hit_min, r_hit_max);
//     color *= s;
//   #endif
//   color = WorkingColorSpace_HDRTonemap_To_AfterTonemap(color);
// 
//   return color;
// }
// #endif

// Psycho11 ///////////////////////////////////////////////////////////////////////////
// #if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_PSYCHO11
// #include "./renodx_psycho_test11.glsl"
// 
// vec3 ToneMapPass_Psycho11(vec3 color) {
// 
//   //do
//   color = ColorSpaceConversion(color, RENODX_WORKINGCS_SHADERPACK, RENODX_CS_BT2020);
//   color = max(vec3(0), color); //clamp
//   #if RENODX_SCALING == RENODX_SCALING_Y || RENODX_SCALING == RENODX_SCALING_MAXCHANNEL
//     color = vec3(1,0,1);
//   #else
//     color = psychotm_test11(
//       color,
//       /* peak_value = */ RENODX_PEAK_BRIGHTNESS / RENODX_GAME_BRIGHTNESS,
//       /* exposure = 1.f, */
//       /* highlights = */ 1.f,
//       /* shadows = */ 1.f,
//       /* contrast = */ 1.f,
//       /* purity_scale = */ 1.f,
//       /* bleaching_intensity = */ 0.f,
//       /* clip_point = */ 100.f/* RENODX_WHITE_CLIP / RENODX_GAME_BRIGHTNESS */,
//       /* hue_restore = */ 0.5f, //0.5 seems pushing it.
//       /* adaptation_contrast = */ 1.f,
//       /* white_curve_mode = */ 0, //0 aggressive, 1 gradual
//       /* cone_response_exponent = */ 1.f
//     );
//   #endif
//   color = ColorSpaceConversion(color, RENODX_CS_BT2020, RENODX_WORKINGCS_HDRTONEMAP);
// 
//   return color;
// }
// #endif

// None ///////////////////////////////////////////////////////////////////////////
vec3 ToneMapPass_None(vec3 color) {
  color = ColorSpaceConversion(color, RENODX_WORKINGCS_SHADERPACK, RENODX_WORKINGCS_AFTERTONEMAP);
  return color;
}

// ToneMapPass ///////////////////////////////////////////////////////////////////////////
//color_untonemapped CS must be: RENODX_WORKINGCS_SHADERPACK
//color_tonemapped CS must be: RENODX_CS_BT709
vec3 ToneMapPass(vec3 color_untonemapped, vec3 color_tonemapped, vec2 uv) {
  vec3 result;

#ifdef RENODX_ENABLED
  //Debug (returns)
  #if RENODX_DEBUG == RENODX_DEBUG_UNTONEMAPPED
    return result = color_untonemapped;
  #elif RENODX_DEBUG == RENODX_DEBUG_TONEMAPPED
    #ifndef RENODX_UPGRADE_ENABLED
      return result = vec3(0);
    #else
      return result = ColorSpaceConversion(color_tonemapped, RENODX_CS_BT709, RENODX_WORKINGCS_AFTERTONEMAP);;
    #endif
  #elif RENODX_DEBUG == RENODX_DEBUG_SPLIT
    return result = uv.x < 0.5 ? color_untonemapped : ColorSpaceConversion(color_tonemapped, RENODX_CS_BT709, RENODX_WORKINGCS_AFTERTONEMAP);
  #endif

  //Upgrade
  #ifndef RENODX_UPGRADE_ENABLED
    result = color_untonemapped;
  #else
    color_untonemapped = ColorSpaceConversion(color_untonemapped, RENODX_WORKINGCS_SHADERPACK, RENODX_CS_BT709);
    result = UpgradeToneMapBT709(color_untonemapped, Reinhard(max(vec3(0), color_untonemapped)), color_tonemapped, RENODX_UPGRADE_AMOUNT, RENODX_UPGRADE_AUTO); 
    result = ColorSpaceConversion(result, RENODX_CS_BT709, RENODX_WORKINGCS_SHADERPACK);
  #endif

  //Colorgrading
  result = ColorGradePass(result);

  //exposure
  result *= RENODX_EXPOSURE;

  //gamma correction
  result = WorkingGamma_Correction(result);
      
  //Tonemap HDR (working color space and gamma correction inside)
  #if RENODX_HDRTONEMAP_TYPE > RENODX_HDRTONEMAP_TYPE_OFF
    //do
    #if RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_REINHARD
      result = ToneMapPass_Reinhard(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_ACES
      result = ToneMapPass_ACES(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_GT
      result = ToneMapPass_GTTonemap(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_GT7
      result = ToneMapPass_GT7(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_HERMITE
      result = ToneMapPass_Hermite(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_HABLE
      result = ToneMapPass_Hable(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_EXPROLL
      result = ToneMapPass_ExponentialRollOff(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_NEUTWO
      result = ToneMapPass_NeuTwo(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_FROSTBITE
      result = ToneMapPass_Frostbite(result);
    // #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_DANIELE
    //   result = ToneMapPass_Daniele(result);
    #elif RENODX_HDRTONEMAP_TYPE == RENODX_HDRTONEMAP_TYPE_PSYCHO11
      result = ToneMapPass_Psycho11(result);
    #else
      result = ToneMapPass_None(result);
    #endif
  #endif
#else
  result = color_tonemapped;
#endif

  //clamp cs
  #if RENODX_WORKINGCS_AFTERTONEMAP_DOCLAMP > 0
    result = max(vec3(0), result);
  #endif

  //WorkingGamma AfterTonemap
  result = WorkingGamma_AfterTonemap_Encode(result);

  return result;
}

// RenderIntermediatePass ///////////////////////////////////////////////////////////////////////////
vec3 RenderIntermediatePass(vec3 color) {
  //to linear bt709
  color = WorkingGamma_RenderIntermediatePass_Decode(color);
  color = WorkingColorSpace_RenderIntermediatePassInput_To_BT709(color);

  //Intermediate Scaling (for UI)
  color *= RENODX_GAME_BRIGHTNESS / RENODX_UI_BRIGHTNESS;

  //Intermediate Encode (for "swapchain" shader to then decode)
  #if RENODX_INTER_MODE == RENODX_INTER_MODE_SRGB
    color = SrgbEncodeSafe(color); //to match UI
  #elif RENODX_INTER_MODE == RENODX_INTER_MODE_SCRGB
    color *= 203/80.;
  #elif RENODX_INTER_MODE == RENODX_INTER_MODE_HDR10
    color = BT709_TO_BT2020_MAT * color;
    color = max(vec3(0), color); //HDR10 is unorm
    color = PqEncode(color, 203);
  #endif
  
  return color;
}