tree sway and shadow improvements
[WindSway-HDRP.git] / Library / PackageCache / com.unity.postprocessing@2.1.2 / PostProcessing / Runtime / Utils / HableCurve.cs
blob7528e46fde5fdf32e00111e8bf67d8db98f9d7bb
1 namespace UnityEngine.Rendering.PostProcessing
3 /// <summary>
4 /// A raw implementation of John Hable's artist-friendly tonemapping curve.
5 /// See http://filmicworlds.com/blog/filmic-tonemapping-with-piecewise-power-curves/
6 /// </summary>
7 public class HableCurve
9 class Segment
11 public float offsetX;
12 public float offsetY;
13 public float scaleX;
14 public float scaleY;
15 public float lnA;
16 public float B;
18 public float Eval(float x)
20 float x0 = (x - offsetX) * scaleX;
21 float y0 = 0f;
23 // log(0) is undefined but our function should evaluate to 0. There are better ways to handle this,
24 // but it's doing it the slow way here for clarity.
25 if (x0 > 0)
26 y0 = Mathf.Exp(lnA + B * Mathf.Log(x0));
28 return y0 * scaleY + offsetY;
32 struct DirectParams
34 internal float x0;
35 internal float y0;
36 internal float x1;
37 internal float y1;
38 internal float W;
40 internal float overshootX;
41 internal float overshootY;
43 internal float gamma;
46 /// <summary>
47 /// The curve's white point.
48 /// </summary>
49 public float whitePoint { get; private set; }
51 /// <summary>
52 /// The inverse of the curve's white point.
53 /// </summary>
54 public float inverseWhitePoint { get; private set; }
56 internal float x0 { get; private set; }
57 internal float x1 { get; private set; }
59 // Toe, mid, shoulder
60 readonly Segment[] m_Segments = new Segment[3];
62 /// <summary>
63 /// Creates a new curve.
64 /// </summary>
65 public HableCurve()
67 for (int i = 0; i < 3; i++)
68 m_Segments[i] = new Segment();
70 uniforms = new Uniforms(this);
73 /// <summary>
74 /// Evaluates a given point on the curve.
75 /// </summary>
76 /// <param name="x">The point within the curve to evaluate (on the horizontal axis)</param>
77 /// <returns>The value of the curve, at the point specified</returns>
78 public float Eval(float x)
80 float normX = x * inverseWhitePoint;
81 int index = (normX < x0) ? 0 : ((normX < x1) ? 1 : 2);
82 var segment = m_Segments[index];
83 float ret = segment.Eval(normX);
84 return ret;
87 /// <summary>
88 /// Initializes the curve with given settings.
89 /// </summary>
90 /// <param name="toeStrength">Affects the transition between the toe and the mid section of
91 /// the curve. A value of 0 means no toe, a value of 1 means a very hard transition</param>
92 /// <param name="toeLength">Affects how much of the dynamic range is in the toe. With a
93 /// small value, the toe will be very short and quickly transition into the linear section,
94 /// and with a longer value having a longer toe</param>
95 /// <param name="shoulderStrength">Affects the transition between the mid section and the
96 /// shoulder of the curve. A value of 0 means no shoulder, a value of 1 means a very hard
97 /// transition</param>
98 /// <param name="shoulderLength">Affects how many F-stops (EV) to add to the dynamic range
99 /// of the curve</param>
100 /// <param name="shoulderAngle">Affects how much overshoot to add to the shoulder</param>
101 /// <param name="gamma">Applies a gamma function to the curve</param>
102 public void Init(float toeStrength, float toeLength, float shoulderStrength, float shoulderLength, float shoulderAngle, float gamma)
104 var dstParams = new DirectParams();
106 // This is not actually the display gamma. It's just a UI space to avoid having to
107 // enter small numbers for the input.
108 const float kPerceptualGamma = 2.2f;
110 // Constraints
112 toeLength = Mathf.Pow(Mathf.Clamp01(toeLength), kPerceptualGamma);
113 toeStrength = Mathf.Clamp01(toeStrength);
114 shoulderAngle = Mathf.Clamp01(shoulderAngle);
115 shoulderStrength = Mathf.Clamp(shoulderStrength, 1e-5f, 1f - 1e-5f);
116 shoulderLength = Mathf.Max(0f, shoulderLength);
117 gamma = Mathf.Max(1e-5f, gamma);
120 // Apply base params
122 // Toe goes from 0 to 0.5
123 float x0 = toeLength * 0.5f;
124 float y0 = (1f - toeStrength) * x0; // Lerp from 0 to x0
126 float remainingY = 1f - y0;
128 float initialW = x0 + remainingY;
130 float y1_offset = (1f - shoulderStrength) * remainingY;
131 float x1 = x0 + y1_offset;
132 float y1 = y0 + y1_offset;
134 // Filmic shoulder strength is in F stops
135 float extraW = RuntimeUtilities.Exp2(shoulderLength) - 1f;
137 float W = initialW + extraW;
139 dstParams.x0 = x0;
140 dstParams.y0 = y0;
141 dstParams.x1 = x1;
142 dstParams.y1 = y1;
143 dstParams.W = W;
145 // Bake the linear to gamma space conversion
146 dstParams.gamma = gamma;
149 dstParams.overshootX = (dstParams.W * 2f) * shoulderAngle * shoulderLength;
150 dstParams.overshootY = 0.5f * shoulderAngle * shoulderLength;
152 InitSegments(dstParams);
155 void InitSegments(DirectParams srcParams)
157 var paramsCopy = srcParams;
159 whitePoint = srcParams.W;
160 inverseWhitePoint = 1f / srcParams.W;
162 // normalize params to 1.0 range
163 paramsCopy.W = 1f;
164 paramsCopy.x0 /= srcParams.W;
165 paramsCopy.x1 /= srcParams.W;
166 paramsCopy.overshootX = srcParams.overshootX / srcParams.W;
168 float toeM = 0f;
169 float shoulderM = 0f;
171 float m, b;
172 AsSlopeIntercept(out m, out b, paramsCopy.x0, paramsCopy.x1, paramsCopy.y0, paramsCopy.y1);
174 float g = srcParams.gamma;
176 // Base function of linear section plus gamma is
177 // y = (mx+b)^g
179 // which we can rewrite as
180 // y = exp(g*ln(m) + g*ln(x+b/m))
182 // and our evaluation function is (skipping the if parts):
184 float x0 = (x - offsetX) * scaleX;
185 y0 = exp(m_lnA + m_B*log(x0));
186 return y0*scaleY + m_offsetY;
189 var midSegment = m_Segments[1];
190 midSegment.offsetX = -(b / m);
191 midSegment.offsetY = 0f;
192 midSegment.scaleX = 1f;
193 midSegment.scaleY = 1f;
194 midSegment.lnA = g * Mathf.Log(m);
195 midSegment.B = g;
197 toeM = EvalDerivativeLinearGamma(m, b, g, paramsCopy.x0);
198 shoulderM = EvalDerivativeLinearGamma(m, b, g, paramsCopy.x1);
200 // apply gamma to endpoints
201 paramsCopy.y0 = Mathf.Max(1e-5f, Mathf.Pow(paramsCopy.y0, paramsCopy.gamma));
202 paramsCopy.y1 = Mathf.Max(1e-5f, Mathf.Pow(paramsCopy.y1, paramsCopy.gamma));
204 paramsCopy.overshootY = Mathf.Pow(1f + paramsCopy.overshootY, paramsCopy.gamma) - 1f;
207 this.x0 = paramsCopy.x0;
208 this.x1 = paramsCopy.x1;
210 // Toe section
212 var toeSegment = m_Segments[0];
213 toeSegment.offsetX = 0;
214 toeSegment.offsetY = 0f;
215 toeSegment.scaleX = 1f;
216 toeSegment.scaleY = 1f;
218 float lnA, B;
219 SolveAB(out lnA, out B, paramsCopy.x0, paramsCopy.y0, toeM);
220 toeSegment.lnA = lnA;
221 toeSegment.B = B;
224 // Shoulder section
226 // Use the simple version that is usually too flat
227 var shoulderSegment = m_Segments[2];
229 float x0 = (1f + paramsCopy.overshootX) - paramsCopy.x1;
230 float y0 = (1f + paramsCopy.overshootY) - paramsCopy.y1;
232 float lnA, B;
233 SolveAB(out lnA, out B, x0, y0, shoulderM);
235 shoulderSegment.offsetX = (1f + paramsCopy.overshootX);
236 shoulderSegment.offsetY = (1f + paramsCopy.overshootY);
238 shoulderSegment.scaleX = -1f;
239 shoulderSegment.scaleY = -1f;
240 shoulderSegment.lnA = lnA;
241 shoulderSegment.B = B;
244 // Normalize so that we hit 1.0 at our white point. We wouldn't have do this if we
245 // skipped the overshoot part.
247 // Evaluate shoulder at the end of the curve
248 float scale = m_Segments[2].Eval(1f);
249 float invScale = 1f / scale;
251 m_Segments[0].offsetY *= invScale;
252 m_Segments[0].scaleY *= invScale;
254 m_Segments[1].offsetY *= invScale;
255 m_Segments[1].scaleY *= invScale;
257 m_Segments[2].offsetY *= invScale;
258 m_Segments[2].scaleY *= invScale;
262 // Find a function of the form:
263 // f(x) = e^(lnA + Bln(x))
264 // where
265 // f(0) = 0; not really a constraint
266 // f(x0) = y0
267 // f'(x0) = m
268 void SolveAB(out float lnA, out float B, float x0, float y0, float m)
270 B = (m * x0) / y0;
271 lnA = Mathf.Log(y0) - B * Mathf.Log(x0);
274 // Convert to y=mx+b
275 void AsSlopeIntercept(out float m, out float b, float x0, float x1, float y0, float y1)
277 float dy = (y1 - y0);
278 float dx = (x1 - x0);
280 if (dx == 0)
281 m = 1f;
282 else
283 m = dy / dx;
285 b = y0 - x0 * m;
288 // f(x) = (mx+b)^g
289 // f'(x) = gm(mx+b)^(g-1)
290 float EvalDerivativeLinearGamma(float m, float b, float g, float x)
292 float ret = g * m * Mathf.Pow(m * x + b, g - 1f);
293 return ret;
296 /// <summary>
297 /// Utility class to retrieve curve values for shader evaluation.
298 /// </summary>
299 public class Uniforms
301 HableCurve parent;
303 internal Uniforms(HableCurve parent)
305 this.parent = parent;
308 /// <summary>
309 /// A pre-built <see cref="Vector4"/> holding: <c>(inverseWhitePoint, x0, x1, 0)</c>.
310 /// </summary>
311 public Vector4 curve
313 get { return new Vector4(parent.inverseWhitePoint, parent.x0, parent.x1, 0f); }
316 /// <summary>
317 /// A pre-built <see cref="Vector4"/> holding: <c>(toe.offsetX, toe.offsetY, toe.scaleX, toe.scaleY)</c>.
318 /// </summary>
319 public Vector4 toeSegmentA
323 var toe = parent.m_Segments[0];
324 return new Vector4(toe.offsetX, toe.offsetY, toe.scaleX, toe.scaleY);
328 /// <summary>
329 /// A pre-built <see cref="Vector4"/> holding: <c>(toe.lnA, toe.B, 0, 0)</c>.
330 /// </summary>
331 public Vector4 toeSegmentB
335 var toe = parent.m_Segments[0];
336 return new Vector4(toe.lnA, toe.B, 0f, 0f);
340 /// <summary>
341 /// A pre-built <see cref="Vector4"/> holding: <c>(mid.offsetX, mid.offsetY, mid.scaleX, mid.scaleY)</c>.
342 /// </summary>
343 public Vector4 midSegmentA
347 var mid = parent.m_Segments[1];
348 return new Vector4(mid.offsetX, mid.offsetY, mid.scaleX, mid.scaleY);
352 /// <summary>
353 /// A pre-built <see cref="Vector4"/> holding: <c>(mid.lnA, mid.B, 0, 0)</c>.
354 /// </summary>
355 public Vector4 midSegmentB
359 var mid = parent.m_Segments[1];
360 return new Vector4(mid.lnA, mid.B, 0f, 0f);
364 /// <summary>
365 /// A pre-built <see cref="Vector4"/> holding: <c>(toe.offsetX, toe.offsetY, toe.scaleX, toe.scaleY)</c>.
366 /// </summary>
367 public Vector4 shoSegmentA
371 var sho = parent.m_Segments[2];
372 return new Vector4(sho.offsetX, sho.offsetY, sho.scaleX, sho.scaleY);
376 /// <summary>
377 /// A pre-built <see cref="Vector4"/> holding: <c>(sho.lnA, sho.B, 0, 0)</c>.
378 /// </summary>
379 public Vector4 shoSegmentB
383 var sho = parent.m_Segments[2];
384 return new Vector4(sho.lnA, sho.B, 0f, 0f);
389 /// <summary>
390 /// The builtin <see cref="Uniforms"/> instance for this curve.
391 /// </summary>
392 public readonly Uniforms uniforms;