Tonemapping.cs 9.1 KB

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