UnityMol  0.9.6-875
UnityMol viewer / In developement
Tonemapping.cs
Go to the documentation of this file.
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 
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 }
void OnRenderImage(RenderTexture source, RenderTexture destination)
Definition: Tonemapping.cs:130