1 using System;
2 using
UnityEngine;
3
4 namespace
UnityStandardAssets.ImageEffects
5 {
6     
[ExecuteInEditMode]
7     
[RequireComponent(typeof (Camera))]
8     
[AddComponentMenu("Image Effects/Color Adjustments/Tonemapping")]
9     
public class Tonemapping : PostEffectsBase
10     {
11         
public enum TonemapperType
12         {
13             SimpleReinhard,
14             UserCurve,
15             Hable,
16             Photographic,
17             OptimizedHejiDawson,
18             AdaptiveReinhard,
19             AdaptiveReinhardAutoWhite,
20         };
21
22         
public enum AdaptiveTexSize
23         {
24             Square16 =
16,
25             Square32 =
32,
26             Square64 =
64,
27             Square128 =
128,
28             Square256 =
256,
29             Square512 =
512,
30             Square1024 =
1024,
31         };
32
33         
public TonemapperType type = TonemapperType.Photographic;
34         
public AdaptiveTexSize adaptiveTextureSize = AdaptiveTexSize.Square256;
35
36         
// CURVE parameter
37         
public AnimationCurve remapCurve;
38         
private Texture2D curveTex = null;
39
40         
// UNCHARTED parameter
41         
public float exposureAdjustment = 1.5f;
42
43         
// REINHARD parameter
44         
public float middleGrey = 0.4f;
45         
public float white = 2.0f;
46         
public float adaptionSpeed = 1.5f;
47
48         
// usual & internal stuff
49         
public Shader tonemapper = null;
50         
public bool validRenderTextureFormat = true;
51         
private Material tonemapMaterial = null;
52         
private RenderTexture rt = null;
53         
private RenderTextureFormat rtFormat = RenderTextureFormat.ARGBHalf;
54
55
56         
public override bool CheckResources()
57         {
58             CheckSupport(
false, true);
59
60             tonemapMaterial = CheckShaderAndCreateMaterial(tonemapper, tonemapMaterial);
61             
if (!curveTex && type == TonemapperType.UserCurve)
62             {
63                 curveTex =
new Texture2D(256, 1, TextureFormat.ARGB32, false, true);
64                 curveTex.filterMode = FilterMode.Bilinear;
65                 curveTex.wrapMode = TextureWrapMode.Clamp;
66                 curveTex.hideFlags = HideFlags.DontSave;
67             }
68
69             
if (!isSupported)
70                 ReportAutoDisable();
71             
return isSupported;
72         }
73
74
75         
public float UpdateCurve()
76         {
77             
float range = 1.0f;
78             
if (remapCurve.keys.Length < 1)
79                 remapCurve =
new AnimationCurve(new Keyframe(0, 0), new Keyframe(2, 1));
80             
if (remapCurve != null)
81             {
82                 
if (remapCurve.length > 0)
83                     range = remapCurve[remapCurve.length -
1].time;
84                 
for (float i = 0.0f; i <= 1.0f; i += 1.0f/255.0f)
85                 {
86                     
float c = remapCurve.Evaluate(i*1.0f*range);
87                     curveTex.SetPixel((
int) Mathf.Floor(i*255.0f), 0, new Color(c, c, c));
88                 }
89                 curveTex.Apply();
90             }
91             
return 1.0f/range;
92         }
93
94
95         
private void OnDisable()
96         {
97             
if (rt)
98             {
99                 DestroyImmediate(rt);
100                 rt =
null;
101             }
102             
if (tonemapMaterial)
103             {
104                 DestroyImmediate(tonemapMaterial);
105                 tonemapMaterial =
null;
106             }
107             
if (curveTex)
108             {
109                 DestroyImmediate(curveTex);
110                 curveTex =
null;
111             }
112         }
113
114
115         
private bool CreateInternalRenderTexture()
116         {
117             
if (rt)
118             {
119                 
return false;
120             }
121             rtFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGHalf) ? RenderTextureFormat.RGHalf : RenderTextureFormat.ARGBHalf;
122             rt =
new RenderTexture(1, 1, 0, rtFormat);
123             rt.hideFlags = HideFlags.DontSave;
124             
return true;
125         }
126
127
128         
// attribute indicates that the image filter chain will continue in LDR
129         
[ImageEffectTransformsToLDR]
130         
private void OnRenderImage(RenderTexture source, RenderTexture destination)
131         {
132             
if (CheckResources() == false)
133             {
134                 Graphics.Blit(source, destination);
135                 
return;
136             }
137
138 #
if UNITY_EDITOR
139             validRenderTextureFormat =
true;
140             
if (source.format != RenderTextureFormat.ARGBHalf)
141             {
142                 validRenderTextureFormat =
false;
143             }
144 #endif
145
146             
// clamp some values to not go out of a valid range
147
148             exposureAdjustment = exposureAdjustment <
0.001f ? 0.001f : exposureAdjustment;
149
150             
// SimpleReinhard tonemappers (local, non adaptive)
151
152             
if (type == TonemapperType.UserCurve)
153             {
154                 
float rangeScale = UpdateCurve();
155                 tonemapMaterial.SetFloat(
"_RangeScale", rangeScale);
156                 tonemapMaterial.SetTexture(
"_Curve", curveTex);
157                 Graphics.Blit(source, destination, tonemapMaterial,
4);
158                 
return;
159             }
160
161             
if (type == TonemapperType.SimpleReinhard)
162             {
163                 tonemapMaterial.SetFloat(
"_ExposureAdjustment", exposureAdjustment);
164                 Graphics.Blit(source, destination, tonemapMaterial,
6);
165                 
return;
166             }
167
168             
if (type == TonemapperType.Hable)
169             {
170                 tonemapMaterial.SetFloat(
"_ExposureAdjustment", exposureAdjustment);
171                 Graphics.Blit(source, destination, tonemapMaterial,
5);
172                 
return;
173             }
174
175             
if (type == TonemapperType.Photographic)
176             {
177                 tonemapMaterial.SetFloat(
"_ExposureAdjustment", exposureAdjustment);
178                 Graphics.Blit(source, destination, tonemapMaterial,
8);
179                 
return;
180             }
181
182             
if (type == TonemapperType.OptimizedHejiDawson)
183             {
184                 tonemapMaterial.SetFloat(
"_ExposureAdjustment", 0.5f*exposureAdjustment);
185                 Graphics.Blit(source, destination, tonemapMaterial,
7);
186                 
return;
187             }
188
189             
// still here?
190             
// => adaptive tone mapping:
191             
// builds an average log luminance, tonemaps according to
192             
// middle grey and white values (user controlled)
193
194             
// AdaptiveReinhardAutoWhite will calculate white value automagically
195
196             
bool freshlyBrewedInternalRt = CreateInternalRenderTexture(); // this retrieves rtFormat, so should happen before rt allocations
197
198             RenderTexture rtSquared = RenderTexture.GetTemporary((
int) adaptiveTextureSize, (int) adaptiveTextureSize, 0, rtFormat);
199             Graphics.Blit(source, rtSquared);
200
201             
int downsample = (int) Mathf.Log(rtSquared.width*1.0f, 2);
202
203             
int div = 2;
204             
var rts = new RenderTexture[downsample];
205             
for (int i = 0; i < downsample; i++)
206             {
207                 rts[i] = RenderTexture.GetTemporary(rtSquared.width/div, rtSquared.width/div,
0, rtFormat);
208                 div *=
2;
209             }
210
211             
// downsample pyramid
212
213             
var lumRt = rts[downsample - 1];
214             Graphics.Blit(rtSquared, rts[
0], tonemapMaterial, 1);
215             
if (type == TonemapperType.AdaptiveReinhardAutoWhite)
216             {
217                 
for (int i = 0; i < downsample - 1; i++)
218                 {
219                     Graphics.Blit(rts[i], rts[i +
1], tonemapMaterial, 9);
220                     lumRt = rts[i +
1];
221                 }
222             }
223             
else if (type == TonemapperType.AdaptiveReinhard)
224             {
225                 
for (int i = 0; i < downsample - 1; i++)
226                 {
227                     Graphics.Blit(rts[i], rts[i +
1]);
228                     lumRt = rts[i +
1];
229                 }
230             }
231
232             
// we have the needed values, let's apply adaptive tonemapping
233
234             adaptionSpeed = adaptionSpeed <
0.001f ? 0.001f : adaptionSpeed;
235             tonemapMaterial.SetFloat(
"_AdaptionSpeed", adaptionSpeed);
236
237             rt.MarkRestoreExpected();
// keeping luminance values between frames, RT restore expected
238
239 #
if UNITY_EDITOR
240             
if (Application.isPlaying && !freshlyBrewedInternalRt)
241                 Graphics.Blit(lumRt, rt, tonemapMaterial,
2);
242             
else
243                 Graphics.Blit(lumRt, rt, tonemapMaterial,
3);
244 #
else
245             Graphics.Blit (lumRt, rt, tonemapMaterial, freshlyBrewedInternalRt ?
3 : 2);
246 #endif
247
248             middleGrey = middleGrey <
0.001f ? 0.001f : middleGrey;
249             tonemapMaterial.SetVector(
"_HdrParams", new Vector4(middleGrey, middleGrey, middleGrey, white*white));
250             tonemapMaterial.SetTexture(
"_SmallTex", rt);
251             
if (type == TonemapperType.AdaptiveReinhard)
252             {
253                 Graphics.Blit(source, destination, tonemapMaterial,
0);
254             }
255             
else if (type == TonemapperType.AdaptiveReinhardAutoWhite)
256             {
257                 Graphics.Blit(source, destination, tonemapMaterial,
10);
258             }
259             
else
260             {
261                 Debug.LogError(
"No valid adaptive tonemapper type found!");
262                 Graphics.Blit(source, destination);
// at least we get the TransformToLDR effect
263             }
264
265             
// cleanup for adaptive
266
267             
for (int i = 0; i < downsample; i++)
268             {
269                 RenderTexture.ReleaseTemporary(rts[i]);
270             }
271             RenderTexture.ReleaseTemporary(rtSquared);
272         }
273     }
274 }