123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- // Copyright (C) 2015, 2016 Keijiro Takahashi
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- using UnityEngine;
- namespace Max820
- {
- [ExecuteInEditMode]
- [RequireComponent(typeof(Camera))]
- [AddComponentMenu("Max820 Image Effects/Bloom")]
- [ImageEffectAllowedInSceneView]
- public class Bloom : MonoBehaviour
- {
- #region Public Properties
- /// Prefilter threshold (gamma-encoded)
- /// Filters out pixels under this level of brightness.
- public float thresholdGamma {
- get { return Mathf.Max(_threshold, 0); }
- set { _threshold = value; }
- }
- /// Prefilter threshold (linearly-encoded)
- /// Filters out pixels under this level of brightness.
- public float thresholdLinear {
- get { return GammaToLinear(thresholdGamma); }
- set { _threshold = LinearToGamma(value); }
- }
- [SerializeField]
- [Tooltip("Filters out pixels under this level of brightness.")]
- float _threshold = 0.8f;
- /// Soft-knee coefficient
- /// Makes transition between under/over-threshold gradual.
- public float softKnee {
- get { return _softKnee; }
- set { _softKnee = value; }
- }
- [SerializeField, Range(0, 1)]
- [Tooltip("Makes transition between under/over-threshold gradual.")]
- float _softKnee = 0.5f;
- /// Bloom radius
- /// Changes extent of veiling effects in a screen
- /// resolution-independent fashion.
- public float radius {
- get { return _radius; }
- set { _radius = value; }
- }
- [SerializeField, Range(1, 7)]
- [Tooltip("Changes extent of veiling effects\n" +
- "in a screen resolution-independent fashion.")]
- float _radius = 2.5f;
- /// Bloom intensity
- /// Blend factor of the result image.
- public float intensity {
- get { return Mathf.Max(_intensity, 0); }
- set { _intensity = value; }
- }
- [SerializeField]
- [Tooltip("Blend factor of the result image.")]
- float _intensity = 0.8f;
- /// High quality mode
- /// Controls filter quality and buffer resolution.
- public bool highQuality {
- get { return _highQuality; }
- set { _highQuality = value; }
- }
- [SerializeField]
- [Tooltip("Controls filter quality and buffer resolution.")]
- bool _highQuality = true;
- /// Anti-flicker filter
- /// Reduces flashing noise with an additional filter.
- [SerializeField]
- [Tooltip("Reduces flashing noise with an additional filter.")]
- bool _antiFlicker = true;
- public bool antiFlicker {
- get { return _antiFlicker; }
- set { _antiFlicker = value; }
- }
- #endregion
- #region Private Members
- [SerializeField, HideInInspector]
- Shader _shader;
- Material _material;
- const int kMaxIterations = 16;
- RenderTexture[] _blurBuffer1 = new RenderTexture[kMaxIterations];
- RenderTexture[] _blurBuffer2 = new RenderTexture[kMaxIterations];
- float LinearToGamma(float x)
- {
- #if UNITY_5_3_OR_NEWER
- return Mathf.LinearToGammaSpace(x);
- #else
- if (x <= 0.0031308f)
- return 12.92f * x;
- else
- return 1.055f * Mathf.Pow(x, 1 / 2.4f) - 0.055f;
- #endif
- }
- float GammaToLinear(float x)
- {
- #if UNITY_5_3_OR_NEWER
- return Mathf.GammaToLinearSpace(x);
- #else
- if (x <= 0.04045f)
- return x / 12.92f;
- else
- return Mathf.Pow((x + 0.055f) / 1.055f, 2.4f);
- #endif
- }
- #endregion
- #region MonoBehaviour Functions
- void OnEnable()
- {
- var shader = _shader ? _shader : Shader.Find("Hidden/Max820/Bloom");
- _material = new Material(shader);
- _material.hideFlags = HideFlags.DontSave;
- }
- void OnDisable()
- {
- DestroyImmediate(_material);
- }
- void OnRenderImage(RenderTexture source, RenderTexture destination)
- {
- var useRGBM = Application.isMobilePlatform;
- // source texture size
- var tw = source.width;
- var th = source.height;
- // halve the texture size for the low quality mode
- if (!_highQuality)
- {
- tw /= 2;
- th /= 2;
- }
- // blur buffer format
- var rtFormat = useRGBM ?
- RenderTextureFormat.Default : RenderTextureFormat.DefaultHDR;
- // determine the iteration count
- var logh = Mathf.Log(th, 2) + _radius - 8;
- var logh_i = (int)logh;
- var iterations = Mathf.Clamp(logh_i, 1, kMaxIterations);
- // update the shader properties
- var lthresh = thresholdLinear;
- _material.SetFloat("_Threshold", lthresh);
- var knee = lthresh * _softKnee + 1e-5f;
- var curve = new Vector3(lthresh - knee, knee * 2, 0.25f / knee);
- _material.SetVector("_Curve", curve);
- var pfo = !_highQuality && _antiFlicker;
- _material.SetFloat("_PrefilterOffs", pfo ? -0.5f : 0.0f);
- _material.SetFloat("_SampleScale", 0.5f + logh - logh_i);
- _material.SetFloat("_Intensity", intensity);
- // prefilter pass
- var prefiltered = RenderTexture.GetTemporary(tw, th, 0, rtFormat);
- var pass = _antiFlicker ? 1 : 0;
- Graphics.Blit(source, prefiltered, _material, pass);
- // construct a mip pyramid
- var last = prefiltered;
- for (var level = 0; level < iterations; level++)
- {
- _blurBuffer1[level] = RenderTexture.GetTemporary(
- last.width / 2, last.height / 2, 0, rtFormat
- );
- pass = (level == 0) ? (_antiFlicker ? 3 : 2) : 4;
- Graphics.Blit(last, _blurBuffer1[level], _material, pass);
- last = _blurBuffer1[level];
- }
- // upsample and combine loop
- for (var level = iterations - 2; level >= 0; level--)
- {
- var basetex = _blurBuffer1[level];
- _material.SetTexture("_BaseTex", basetex);
- _blurBuffer2[level] = RenderTexture.GetTemporary(
- basetex.width, basetex.height, 0, rtFormat
- );
- pass = _highQuality ? 6 : 5;
- Graphics.Blit(last, _blurBuffer2[level], _material, pass);
- last = _blurBuffer2[level];
- }
- // finish process
- _material.SetTexture("_BaseTex", source);
- pass = _highQuality ? 8 : 7;
- Graphics.Blit(last, destination, _material, pass);
- // release the temporary buffers
- for (var i = 0; i < kMaxIterations; i++)
- {
- if (_blurBuffer1[i] != null)
- RenderTexture.ReleaseTemporary(_blurBuffer1[i]);
- if (_blurBuffer2[i] != null)
- RenderTexture.ReleaseTemporary(_blurBuffer2[i]);
- _blurBuffer1[i] = null;
- _blurBuffer2[i] = null;
- }
- RenderTexture.ReleaseTemporary(prefiltered);
- }
- #endregion
- }
- }
|