Skip to content
This repository was archived by the owner on Nov 30, 2020. It is now read-only.

Commit f5b2a0a

Browse files
committed
Added support for HDR grading on platforms without compute/tex3d support
External grading via CUBE luts still require compute & tex3d support.
1 parent 354ff17 commit f5b2a0a

File tree

6 files changed

+351
-90
lines changed

6 files changed

+351
-90
lines changed

PostProcessing/Runtime/Effects/ColorGrading.cs

+117-42
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public sealed class GradingModeParameter : ParameterOverride<GradingMode> {}
2929
[Serializable]
3030
public sealed class TonemapperParameter : ParameterOverride<Tonemapper> {}
3131

32+
// TODO: Could use some refactoring, too much duplicated code here
3233
[Serializable]
3334
[PostProcess(typeof(ColorGradingRenderer), "Unity/Color Grading")]
3435
public sealed class ColorGrading : PostProcessEffectSettings
@@ -137,7 +138,7 @@ public sealed class ColorGrading : PostProcessEffectSettings
137138

138139
public override bool IsEnabledAndSupported(PostProcessRenderContext context)
139140
{
140-
if (gradingMode.value == GradingMode.HighDefinitionRange || gradingMode.value == GradingMode.External)
141+
if (gradingMode.value == GradingMode.External)
141142
{
142143
if (!SystemInfo.supports3DRenderTextures || !SystemInfo.supportsComputeShaders)
143144
return false;
@@ -153,6 +154,7 @@ enum Pass
153154
{
154155
LutGenLDRFromScratch,
155156
LutGenLDR,
157+
LutGenHDR2D
156158
}
157159

158160
Texture2D m_GradingCurves;
@@ -167,28 +169,30 @@ enum Pass
167169

168170
public override void Render(PostProcessRenderContext context)
169171
{
170-
switch (settings.gradingMode.value)
171-
{
172-
case GradingMode.External: RenderExternalPipeline(context);
173-
break;
174-
case GradingMode.LowDefinitionRange: RenderLDRPipeline(context);
175-
break;
176-
case GradingMode.HighDefinitionRange: RenderHDRPipeline(context);
177-
break;
178-
}
172+
var gradingMode = settings.gradingMode.value;
173+
var supportComputeTex3D = SystemInfo.supports3DRenderTextures && SystemInfo.supportsComputeShaders;
174+
175+
if (gradingMode == GradingMode.External)
176+
RenderExternalPipeline3D(context);
177+
else if (gradingMode == GradingMode.HighDefinitionRange && supportComputeTex3D)
178+
RenderHDRPipeline3D(context);
179+
else if (gradingMode == GradingMode.HighDefinitionRange)
180+
RenderHDRPipeline2D(context);
181+
else
182+
RenderLDRPipeline2D(context);
179183
}
180184

181185
// Do color grading using an externally authored 3D lut; it requires Texture3D support and
182186
// compute shaders in case blending is required - Desktop / Consoles / Some high-end mobiles
183-
void RenderExternalPipeline(PostProcessRenderContext context)
187+
void RenderExternalPipeline3D(PostProcessRenderContext context)
184188
{
185189
var lut = settings.externalLut.value;
186190

187191
if (lut == null)
188192
return;
189193

190194
var uberSheet = context.uberSheet;
191-
uberSheet.EnableKeyword("COLOR_GRADING_HDR");
195+
uberSheet.EnableKeyword("COLOR_GRADING_HDR_3D");
192196
uberSheet.properties.SetTexture(ShaderIDs.Lut3D, lut);
193197
uberSheet.properties.SetVector(ShaderIDs.Lut3D_Params, new Vector2(1f / lut.width, lut.width - 1f));
194198
uberSheet.properties.SetFloat(ShaderIDs.PostExposure, RuntimeUtilities.Exp2(settings.postExposure.value));
@@ -197,7 +201,8 @@ void RenderExternalPipeline(PostProcessRenderContext context)
197201

198202
// HDR color pipeline is rendered to a 3D lut; it requires Texture3D & compute shaders
199203
// support - Desktop / Consoles / Some high-end mobiles
200-
void RenderHDRPipeline(PostProcessRenderContext context)
204+
// TODO: Use ShaderIDs for compute once the compatible APIs go in
205+
void RenderHDRPipeline3D(PostProcessRenderContext context)
201206
{
202207
// Unfortunately because AnimationCurve doesn't implement GetHashCode and we don't have
203208
// any reliable way to figure out if a curve data is different from another one we can't
@@ -266,48 +271,118 @@ void RenderHDRPipeline(PostProcessRenderContext context)
266271
settings.toneCurveGamma.value
267272
);
268273

269-
var curve = new Vector4(m_HableCurve.inverseWhitePoint, m_HableCurve.x0, m_HableCurve.x1, 0f);
270-
cmd.SetComputeVectorParam(compute, "_CustomToneCurve", curve);
271-
272-
var toe = m_HableCurve.segments[0];
273-
var mid = m_HableCurve.segments[1];
274-
var sho = m_HableCurve.segments[2];
275-
var toeSegmentA = new Vector4(toe.offsetX, toe.offsetY, toe.scaleX, toe.scaleY);
276-
var toeSegmentB = new Vector4(toe.lnA, toe.B, 0f, 0f);
277-
var midSegmentA = new Vector4(mid.offsetX, mid.offsetY, mid.scaleX, mid.scaleY);
278-
var midSegmentB = new Vector4(mid.lnA, mid.B, 0f, 0f);
279-
var shoSegmentA = new Vector4(sho.offsetX, sho.offsetY, sho.scaleX, sho.scaleY);
280-
var shoSegmentB = new Vector4(sho.lnA, sho.B, 0f, 0f);
281-
cmd.SetComputeVectorParam(compute, "_ToeSegmentA", toeSegmentA);
282-
cmd.SetComputeVectorParam(compute, "_ToeSegmentB", toeSegmentB);
283-
cmd.SetComputeVectorParam(compute, "_MidSegmentA", midSegmentA);
284-
cmd.SetComputeVectorParam(compute, "_MidSegmentB", midSegmentB);
285-
cmd.SetComputeVectorParam(compute, "_ShoSegmentA", shoSegmentA);
286-
cmd.SetComputeVectorParam(compute, "_ShoSegmentB", shoSegmentB);
274+
cmd.SetComputeVectorParam(compute, "_CustomToneCurve", m_HableCurve.uniforms.curve);
275+
cmd.SetComputeVectorParam(compute, "_ToeSegmentA", m_HableCurve.uniforms.toeSegmentA);
276+
cmd.SetComputeVectorParam(compute, "_ToeSegmentB", m_HableCurve.uniforms.toeSegmentB);
277+
cmd.SetComputeVectorParam(compute, "_MidSegmentA", m_HableCurve.uniforms.midSegmentA);
278+
cmd.SetComputeVectorParam(compute, "_MidSegmentB", m_HableCurve.uniforms.midSegmentB);
279+
cmd.SetComputeVectorParam(compute, "_ShoSegmentA", m_HableCurve.uniforms.shoSegmentA);
280+
cmd.SetComputeVectorParam(compute, "_ShoSegmentB", m_HableCurve.uniforms.shoSegmentB);
287281
}
288282

289283
// Generate the lut
290-
context.command.BeginSample("HdrColorGradingLut");
284+
context.command.BeginSample("HdrColorGradingLut3D");
291285
cmd.DispatchCompute(compute, kernel, groupSizeXY, groupSizeXY, groupSizeZ);
292-
context.command.EndSample("HdrColorGradingLut");
286+
context.command.EndSample("HdrColorGradingLut3D");
293287
}
294288

295289
var lut = m_InternalLogLut;
296290
var uberSheet = context.uberSheet;
297-
uberSheet.EnableKeyword("COLOR_GRADING_HDR");
291+
uberSheet.EnableKeyword("COLOR_GRADING_HDR_3D");
298292
uberSheet.properties.SetTexture(ShaderIDs.Lut3D, lut);
299293
uberSheet.properties.SetVector(ShaderIDs.Lut3D_Params, new Vector2(1f / lut.width, lut.width - 1f));
300294
uberSheet.properties.SetFloat(ShaderIDs.PostExposure, RuntimeUtilities.Exp2(settings.postExposure.value));
301295

302296
context.logLut = lut;
303297
}
304298

299+
// HDR color pipeline is rendered to a 2D strip lut (works on HDR platforms without compute
300+
// and 3D texture support). Precision is sliiiiiiightly lower than when using a 3D texture
301+
// LUT (33^3 -> 32^3) but most of the time it's imperceptible.
302+
void RenderHDRPipeline2D(PostProcessRenderContext context)
303+
{
304+
// For the same reasons as in RenderHDRPipeline3D, regen LUT on evey frame
305+
{
306+
CheckInternalStripLut();
307+
308+
// Lut setup
309+
var lutSheet = context.propertySheets.Get(context.resources.shaders.lut2DBaker);
310+
lutSheet.ClearKeywords();
311+
312+
lutSheet.properties.SetVector(ShaderIDs.Lut2D_Params, new Vector4(k_Lut2DSize, 0.5f / (k_Lut2DSize * k_Lut2DSize), 0.5f / k_Lut2DSize, k_Lut2DSize / (k_Lut2DSize - 1f)));
313+
314+
var colorBalance = ColorUtilities.ComputeColorBalance(settings.temperature.value, settings.tint.value);
315+
lutSheet.properties.SetVector(ShaderIDs.ColorBalance, colorBalance);
316+
lutSheet.properties.SetVector(ShaderIDs.ColorFilter, settings.colorFilter.value);
317+
318+
float hue = settings.hueShift.value / 360f; // Remap to [-0.5;0.5]
319+
float sat = settings.saturation.value / 100f + 1f; // Remap to [0;2]
320+
float con = settings.contrast.value / 100f + 1f; // Remap to [0;2]
321+
lutSheet.properties.SetVector(ShaderIDs.HueSatCon, new Vector3(hue, sat, con));
322+
323+
var channelMixerR = new Vector3(settings.mixerRedOutRedIn, settings.mixerRedOutGreenIn, settings.mixerRedOutBlueIn);
324+
var channelMixerG = new Vector3(settings.mixerGreenOutRedIn, settings.mixerGreenOutGreenIn, settings.mixerGreenOutBlueIn);
325+
var channelMixerB = new Vector3(settings.mixerBlueOutRedIn, settings.mixerBlueOutGreenIn, settings.mixerBlueOutBlueIn);
326+
lutSheet.properties.SetVector(ShaderIDs.ChannelMixerRed, channelMixerR / 100f); // Remap to [-2;2]
327+
lutSheet.properties.SetVector(ShaderIDs.ChannelMixerGreen, channelMixerG / 100f);
328+
lutSheet.properties.SetVector(ShaderIDs.ChannelMixerBlue, channelMixerB / 100f);
329+
330+
var lift = ColorUtilities.ColorToLift(settings.lift.value * 0.2f);
331+
var gain = ColorUtilities.ColorToGain(settings.gain.value * 0.8f);
332+
var invgamma = ColorUtilities.ColorToInverseGamma(settings.gamma.value * 0.8f);
333+
lutSheet.properties.SetVector(ShaderIDs.Lift, lift);
334+
lutSheet.properties.SetVector(ShaderIDs.InvGamma, invgamma);
335+
lutSheet.properties.SetVector(ShaderIDs.Gain, gain);
336+
337+
lutSheet.properties.SetTexture(ShaderIDs.Curves, GetCurveTexture(false));
338+
339+
var tonemapper = settings.tonemapper.value;
340+
if (tonemapper == Tonemapper.Custom)
341+
{
342+
lutSheet.EnableKeyword("TONEMAPPING_CUSTOM");
343+
344+
m_HableCurve.Init(
345+
settings.toneCurveToeStrength.value,
346+
settings.toneCurveToeLength.value,
347+
settings.toneCurveShoulderStrength.value,
348+
settings.toneCurveShoulderLength.value,
349+
settings.toneCurveShoulderAngle.value,
350+
settings.toneCurveGamma.value
351+
);
352+
353+
lutSheet.properties.SetVector(ShaderIDs.CustomToneCurve, m_HableCurve.uniforms.curve);
354+
lutSheet.properties.SetVector(ShaderIDs.ToeSegmentA, m_HableCurve.uniforms.toeSegmentA);
355+
lutSheet.properties.SetVector(ShaderIDs.ToeSegmentB, m_HableCurve.uniforms.toeSegmentB);
356+
lutSheet.properties.SetVector(ShaderIDs.MidSegmentA, m_HableCurve.uniforms.midSegmentA);
357+
lutSheet.properties.SetVector(ShaderIDs.MidSegmentB, m_HableCurve.uniforms.midSegmentB);
358+
lutSheet.properties.SetVector(ShaderIDs.ShoSegmentA, m_HableCurve.uniforms.shoSegmentA);
359+
lutSheet.properties.SetVector(ShaderIDs.ShoSegmentB, m_HableCurve.uniforms.shoSegmentB);
360+
}
361+
else if (tonemapper == Tonemapper.ACES)
362+
lutSheet.EnableKeyword("TONEMAPPING_ACES");
363+
else if (tonemapper == Tonemapper.Neutral)
364+
lutSheet.EnableKeyword("TONEMAPPING_NEUTRAL");
365+
366+
// Generate the lut
367+
context.command.BeginSample("HdrColorGradingLut2D");
368+
context.command.BlitFullscreenTriangle(BuiltinRenderTextureType.None, m_InternalLdrLut, lutSheet, (int)Pass.LutGenHDR2D);
369+
context.command.EndSample("HdrColorGradingLut2D");
370+
}
371+
372+
var lut = m_InternalLdrLut;
373+
var uberSheet = context.uberSheet;
374+
uberSheet.EnableKeyword("COLOR_GRADING_HDR_2D");
375+
uberSheet.properties.SetVector(ShaderIDs.Lut2D_Params, new Vector3(1f / lut.width, 1f / lut.height, lut.height - 1f));
376+
uberSheet.properties.SetTexture(ShaderIDs.Lut2D, lut);
377+
uberSheet.properties.SetFloat(ShaderIDs.PostExposure, RuntimeUtilities.Exp2(settings.postExposure.value));
378+
}
379+
305380
// LDR color pipeline is rendered to a 2D strip lut (works on every platform)
306-
void RenderLDRPipeline(PostProcessRenderContext context)
381+
void RenderLDRPipeline2D(PostProcessRenderContext context)
307382
{
308-
// For the same reasons as in RenderHDRPipeline, regen LUT on evey frame
383+
// For the same reasons as in RenderHDRPipeline3D, regen LUT on evey frame
309384
{
310-
CheckInternalLdrLut();
385+
CheckInternalStripLut();
311386

312387
// Lut setup
313388
var lutSheet = context.propertySheets.Get(context.resources.shaders.lut2DBaker);
@@ -342,18 +417,18 @@ void RenderLDRPipeline(PostProcessRenderContext context)
342417
lutSheet.properties.SetTexture(ShaderIDs.Curves, GetCurveTexture(false));
343418

344419
// Generate the lut
345-
context.command.BeginSample("LdrColorGradingLut");
420+
context.command.BeginSample("LdrColorGradingLut2D");
346421
var userLut = settings.ldrLut.value;
347422
if (userLut == null)
348423
context.command.BlitFullscreenTriangle(BuiltinRenderTextureType.None, m_InternalLdrLut, lutSheet, (int)Pass.LutGenLDRFromScratch);
349424
else
350425
context.command.BlitFullscreenTriangle(userLut, m_InternalLdrLut, lutSheet, (int)Pass.LutGenLDR);
351-
context.command.EndSample("LdrColorGradingLut");
426+
context.command.EndSample("LdrColorGradingLut2D");
352427
}
353428

354429
var lut = m_InternalLdrLut;
355430
var uberSheet = context.uberSheet;
356-
uberSheet.EnableKeyword("COLOR_GRADING_LDR");
431+
uberSheet.EnableKeyword("COLOR_GRADING_LDR_2D");
357432
uberSheet.properties.SetVector(ShaderIDs.Lut2D_Params, new Vector3(1f / lut.width, 1f / lut.height, lut.height - 1f));
358433
uberSheet.properties.SetTexture(ShaderIDs.Lut2D, lut);
359434
}
@@ -383,7 +458,7 @@ void CheckInternalLogLut()
383458
}
384459
}
385460

386-
void CheckInternalLdrLut()
461+
void CheckInternalStripLut()
387462
{
388463
// Check internal lut state, (re)create it if needed
389464
if (m_InternalLdrLut == null || !m_InternalLdrLut.IsCreated())
@@ -393,7 +468,7 @@ void CheckInternalLdrLut()
393468
var format = GetLutFormat();
394469
m_InternalLdrLut = new RenderTexture(k_Lut2DSize * k_Lut2DSize, k_Lut2DSize, 0, format, RenderTextureReadWrite.Linear)
395470
{
396-
name = "Color Grading Ldr Lut",
471+
name = "Color Grading Strip Lut",
397472
hideFlags = HideFlags.DontSave,
398473
filterMode = FilterMode.Bilinear,
399474
wrapMode = TextureWrapMode.Clamp,

PostProcessing/Runtime/Utils/ColorUtilities.cs

+14-22
Original file line numberDiff line numberDiff line change
@@ -84,34 +84,26 @@ public static Vector3 ColorToGain(Vector4 color)
8484

8585
// Alexa LogC converters (El 1000)
8686
// See http://www.vocas.nl/webfm_send/964
87+
const float logC_cut = 0.011361f;
88+
const float logC_a = 5.555556f;
89+
const float logC_b = 0.047996f;
90+
const float logC_c = 0.244161f;
91+
const float logC_d = 0.386036f;
92+
const float logC_e = 5.301883f;
93+
const float logC_f = 0.092819f;
94+
8795
public static float LogCToLinear(float x)
8896
{
89-
const float cut = 0.011361f;
90-
const float a = 5.555556f;
91-
const float b = 0.047996f;
92-
const float c = 0.244161f;
93-
const float d = 0.386036f;
94-
const float e = 5.301883f;
95-
const float f = 0.092819f;
96-
97-
return (x > e * cut + f)
98-
? (Mathf.Pow(10f, (x - d) / c) - b) / a
99-
: (x - f) / e;
97+
return x > logC_e * logC_cut + logC_f
98+
? (Mathf.Pow(10f, (x - logC_d) / logC_c) - logC_b) / logC_a
99+
: (x - logC_f) / logC_e;
100100
}
101101

102102
public static float LinearToLogC(float x)
103103
{
104-
const float cut = 0.011361f;
105-
const float a = 5.555556f;
106-
const float b = 0.047996f;
107-
const float c = 0.244161f;
108-
const float d = 0.386036f;
109-
const float e = 5.301883f;
110-
const float f = 0.092819f;
111-
112-
return (x > cut)
113-
? c * Mathf.Log10(a * x + b) + d
114-
: e * x + f;
104+
return x > logC_cut
105+
? logC_c * Mathf.Log10(logC_a * x + logC_b) + logC_d
106+
: logC_e * x + logC_f;
115107
}
116108

117109
public static uint ToHex(Color c)

0 commit comments

Comments
 (0)