Shader "Hidden/Universal Render Pipeline/LutBuilderLdr" { HLSLINCLUDE #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/Shaders/PostProcessing/Common.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" float4 _Lut_Params; // x: lut_height, y: 0.5 / lut_width, z: 0.5 / lut_height, w: lut_height / lut_height - 1 float4 _ColorBalance; // xyz: LMS coeffs, w: unused half4 _ColorFilter; // xyz: color, w: unused half4 _ChannelMixerRed; // xyz: rgb coeffs, w: unused half4 _ChannelMixerGreen; // xyz: rgb coeffs, w: unused half4 _ChannelMixerBlue; // xyz: rgb coeffs, w: unused float4 _HueSatCon; // x: hue shift, y: saturation, z: contrast, w: unused float4 _Lift; // xyz: color, w: unused float4 _Gamma; // xyz: color, w: unused float4 _Gain; // xyz: color, w: unused float4 _Shadows; // xyz: color, w: unused float4 _Midtones; // xyz: color, w: unused float4 _Highlights; // xyz: color, w: unused float4 _ShaHiLimits; // xy: shadows min/max, zw: highlight min/max half4 _SplitShadows; // xyz: color, w: balance half4 _SplitHighlights; // xyz: color, w: unused TEXTURE2D(_CurveMaster); TEXTURE2D(_CurveRed); TEXTURE2D(_CurveGreen); TEXTURE2D(_CurveBlue); TEXTURE2D(_CurveHueVsHue); TEXTURE2D(_CurveHueVsSat); TEXTURE2D(_CurveSatVsSat); TEXTURE2D(_CurveLumVsSat); half EvaluateCurve(TEXTURE2D(curve), float t) { half x = SAMPLE_TEXTURE2D(curve, sampler_LinearClamp, float2(t, 0.0)).x; return saturate(x); } half4 FragLutBuilderLdr(Varyings input) : SV_Target { float3 colorLinear = GetLutStripValue(input.texcoord, _Lut_Params); // White balance in LMS space float3 colorLMS = LinearToLMS(colorLinear); colorLMS *= _ColorBalance.xyz; colorLinear = LMSToLinear(colorLMS); // Do contrast in log after white balance float3 colorLog = LinearToLogC(colorLinear); colorLog = (colorLog - ACEScc_MIDGRAY) * _HueSatCon.z + ACEScc_MIDGRAY; colorLinear = LogCToLinear(colorLog); // Color filter is just an unclipped multiplier colorLinear *= _ColorFilter.xyz; // Do NOT feed negative values to the following color ops colorLinear = max(0.0, colorLinear); // Split toning // As counter-intuitive as it is, to make split-toning work the same way it does in Adobe // products we have to do all the maths in gamma-space... float balance = _SplitShadows.w; float3 colorGamma = PositivePow(colorLinear, 1.0 / 2.2); float luma = saturate(GetLuminance(saturate(colorGamma)) + balance); float3 splitShadows = lerp((0.5).xxx, _SplitShadows.xyz, 1.0 - luma); float3 splitHighlights = lerp((0.5).xxx, _SplitHighlights.xyz, luma); colorGamma = SoftLight(colorGamma, splitShadows); colorGamma = SoftLight(colorGamma, splitHighlights); colorLinear = PositivePow(colorGamma, 2.2); // Channel mixing (Adobe style) colorLinear = float3( dot(colorLinear, _ChannelMixerRed.xyz), dot(colorLinear, _ChannelMixerGreen.xyz), dot(colorLinear, _ChannelMixerBlue.xyz) ); // Shadows, midtones, highlights luma = GetLuminance(colorLinear); float shadowsFactor = 1.0 - smoothstep(_ShaHiLimits.x, _ShaHiLimits.y, luma); float highlightsFactor = smoothstep(_ShaHiLimits.z, _ShaHiLimits.w, luma); float midtonesFactor = 1.0 - shadowsFactor - highlightsFactor; colorLinear = colorLinear * _Shadows.xyz * shadowsFactor + colorLinear * _Midtones.xyz * midtonesFactor + colorLinear * _Highlights.xyz * highlightsFactor; // Lift, gamma, gain colorLinear = colorLinear * _Gain.xyz + _Lift.xyz; colorLinear = sign(colorLinear) * pow(abs(colorLinear), _Gamma.xyz); // HSV operations float satMult; float3 hsv = RgbToHsv(colorLinear); { // Hue Vs Sat satMult = EvaluateCurve(_CurveHueVsSat, hsv.x) * 2.0; // Sat Vs Sat satMult *= EvaluateCurve(_CurveSatVsSat, hsv.y) * 2.0; // Lum Vs Sat satMult *= EvaluateCurve(_CurveLumVsSat, Luminance(colorLinear)) * 2.0; // Hue Shift & Hue Vs Hue float hue = hsv.x + _HueSatCon.x; float offset = EvaluateCurve(_CurveHueVsHue, hue) - 0.5; hue += offset; hsv.x = RotateHue(hue, 0.0, 1.0); } colorLinear = HsvToRgb(hsv); // Global saturation luma = GetLuminance(colorLinear); colorLinear = luma.xxx + (_HueSatCon.yyy * satMult) * (colorLinear - luma.xxx); // YRGB curves { const float kHalfPixel = (1.0 / 128.0) / 2.0; float3 c = colorLinear; // Y (master) c += kHalfPixel.xxx; float mr = EvaluateCurve(_CurveMaster, c.r); float mg = EvaluateCurve(_CurveMaster, c.g); float mb = EvaluateCurve(_CurveMaster, c.b); c = float3(mr, mg, mb); // RGB c += kHalfPixel.xxx; float r = EvaluateCurve(_CurveRed, c.r); float g = EvaluateCurve(_CurveGreen, c.g); float b = EvaluateCurve(_CurveBlue, c.b); colorLinear = float3(r, g, b); } return half4(saturate(colorLinear), 1.0); } ENDHLSL SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"} LOD 100 ZTest Always ZWrite Off Cull Off Pass { Name "LutBuilderLdr" HLSLPROGRAM #pragma vertex Vert #pragma fragment FragLutBuilderLdr ENDHLSL } } }