Bloom.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright (C) 2015, 2016 Keijiro Takahashi
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. //
  21. using UnityEngine;
  22. namespace Max820
  23. {
  24. [ExecuteInEditMode]
  25. [RequireComponent(typeof(Camera))]
  26. [AddComponentMenu("Max820 Image Effects/Bloom")]
  27. [ImageEffectAllowedInSceneView]
  28. public class Bloom : MonoBehaviour
  29. {
  30. #region Public Properties
  31. /// Prefilter threshold (gamma-encoded)
  32. /// Filters out pixels under this level of brightness.
  33. public float thresholdGamma {
  34. get { return Mathf.Max(_threshold, 0); }
  35. set { _threshold = value; }
  36. }
  37. /// Prefilter threshold (linearly-encoded)
  38. /// Filters out pixels under this level of brightness.
  39. public float thresholdLinear {
  40. get { return GammaToLinear(thresholdGamma); }
  41. set { _threshold = LinearToGamma(value); }
  42. }
  43. [SerializeField]
  44. [Tooltip("Filters out pixels under this level of brightness.")]
  45. float _threshold = 0.8f;
  46. /// Soft-knee coefficient
  47. /// Makes transition between under/over-threshold gradual.
  48. public float softKnee {
  49. get { return _softKnee; }
  50. set { _softKnee = value; }
  51. }
  52. [SerializeField, Range(0, 1)]
  53. [Tooltip("Makes transition between under/over-threshold gradual.")]
  54. float _softKnee = 0.5f;
  55. /// Bloom radius
  56. /// Changes extent of veiling effects in a screen
  57. /// resolution-independent fashion.
  58. public float radius {
  59. get { return _radius; }
  60. set { _radius = value; }
  61. }
  62. [SerializeField, Range(1, 7)]
  63. [Tooltip("Changes extent of veiling effects\n" +
  64. "in a screen resolution-independent fashion.")]
  65. float _radius = 2.5f;
  66. /// Bloom intensity
  67. /// Blend factor of the result image.
  68. public float intensity {
  69. get { return Mathf.Max(_intensity, 0); }
  70. set { _intensity = value; }
  71. }
  72. [SerializeField]
  73. [Tooltip("Blend factor of the result image.")]
  74. float _intensity = 0.8f;
  75. /// High quality mode
  76. /// Controls filter quality and buffer resolution.
  77. public bool highQuality {
  78. get { return _highQuality; }
  79. set { _highQuality = value; }
  80. }
  81. [SerializeField]
  82. [Tooltip("Controls filter quality and buffer resolution.")]
  83. bool _highQuality = true;
  84. /// Anti-flicker filter
  85. /// Reduces flashing noise with an additional filter.
  86. [SerializeField]
  87. [Tooltip("Reduces flashing noise with an additional filter.")]
  88. bool _antiFlicker = true;
  89. public bool antiFlicker {
  90. get { return _antiFlicker; }
  91. set { _antiFlicker = value; }
  92. }
  93. #endregion
  94. #region Private Members
  95. [SerializeField, HideInInspector]
  96. Shader _shader;
  97. Material _material;
  98. const int kMaxIterations = 16;
  99. RenderTexture[] _blurBuffer1 = new RenderTexture[kMaxIterations];
  100. RenderTexture[] _blurBuffer2 = new RenderTexture[kMaxIterations];
  101. float LinearToGamma(float x)
  102. {
  103. #if UNITY_5_3_OR_NEWER
  104. return Mathf.LinearToGammaSpace(x);
  105. #else
  106. if (x <= 0.0031308f)
  107. return 12.92f * x;
  108. else
  109. return 1.055f * Mathf.Pow(x, 1 / 2.4f) - 0.055f;
  110. #endif
  111. }
  112. float GammaToLinear(float x)
  113. {
  114. #if UNITY_5_3_OR_NEWER
  115. return Mathf.GammaToLinearSpace(x);
  116. #else
  117. if (x <= 0.04045f)
  118. return x / 12.92f;
  119. else
  120. return Mathf.Pow((x + 0.055f) / 1.055f, 2.4f);
  121. #endif
  122. }
  123. #endregion
  124. #region MonoBehaviour Functions
  125. void OnEnable()
  126. {
  127. var shader = _shader ? _shader : Shader.Find("Hidden/Max820/Bloom");
  128. _material = new Material(shader);
  129. _material.hideFlags = HideFlags.DontSave;
  130. }
  131. void OnDisable()
  132. {
  133. DestroyImmediate(_material);
  134. }
  135. void OnRenderImage(RenderTexture source, RenderTexture destination)
  136. {
  137. var useRGBM = Application.isMobilePlatform;
  138. // source texture size
  139. var tw = source.width;
  140. var th = source.height;
  141. // halve the texture size for the low quality mode
  142. if (!_highQuality)
  143. {
  144. tw /= 2;
  145. th /= 2;
  146. }
  147. // blur buffer format
  148. var rtFormat = useRGBM ?
  149. RenderTextureFormat.Default : RenderTextureFormat.DefaultHDR;
  150. // determine the iteration count
  151. var logh = Mathf.Log(th, 2) + _radius - 8;
  152. var logh_i = (int)logh;
  153. var iterations = Mathf.Clamp(logh_i, 1, kMaxIterations);
  154. // update the shader properties
  155. var lthresh = thresholdLinear;
  156. _material.SetFloat("_Threshold", lthresh);
  157. var knee = lthresh * _softKnee + 1e-5f;
  158. var curve = new Vector3(lthresh - knee, knee * 2, 0.25f / knee);
  159. _material.SetVector("_Curve", curve);
  160. var pfo = !_highQuality && _antiFlicker;
  161. _material.SetFloat("_PrefilterOffs", pfo ? -0.5f : 0.0f);
  162. _material.SetFloat("_SampleScale", 0.5f + logh - logh_i);
  163. _material.SetFloat("_Intensity", intensity);
  164. // prefilter pass
  165. var prefiltered = RenderTexture.GetTemporary(tw, th, 0, rtFormat);
  166. var pass = _antiFlicker ? 1 : 0;
  167. Graphics.Blit(source, prefiltered, _material, pass);
  168. // construct a mip pyramid
  169. var last = prefiltered;
  170. for (var level = 0; level < iterations; level++)
  171. {
  172. _blurBuffer1[level] = RenderTexture.GetTemporary(
  173. last.width / 2, last.height / 2, 0, rtFormat
  174. );
  175. pass = (level == 0) ? (_antiFlicker ? 3 : 2) : 4;
  176. Graphics.Blit(last, _blurBuffer1[level], _material, pass);
  177. last = _blurBuffer1[level];
  178. }
  179. // upsample and combine loop
  180. for (var level = iterations - 2; level >= 0; level--)
  181. {
  182. var basetex = _blurBuffer1[level];
  183. _material.SetTexture("_BaseTex", basetex);
  184. _blurBuffer2[level] = RenderTexture.GetTemporary(
  185. basetex.width, basetex.height, 0, rtFormat
  186. );
  187. pass = _highQuality ? 6 : 5;
  188. Graphics.Blit(last, _blurBuffer2[level], _material, pass);
  189. last = _blurBuffer2[level];
  190. }
  191. // finish process
  192. _material.SetTexture("_BaseTex", source);
  193. pass = _highQuality ? 8 : 7;
  194. Graphics.Blit(last, destination, _material, pass);
  195. // release the temporary buffers
  196. for (var i = 0; i < kMaxIterations; i++)
  197. {
  198. if (_blurBuffer1[i] != null)
  199. RenderTexture.ReleaseTemporary(_blurBuffer1[i]);
  200. if (_blurBuffer2[i] != null)
  201. RenderTexture.ReleaseTemporary(_blurBuffer2[i]);
  202. _blurBuffer1[i] = null;
  203. _blurBuffer2[i] = null;
  204. }
  205. RenderTexture.ReleaseTemporary(prefiltered);
  206. }
  207. #endregion
  208. }
  209. }