// // Kino/Bloom v2 - Bloom filter for Unity // // 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. // // Modified by Jean Moreno for Cartoon FX Remaster Demo // - effect previews in SceneView // - disabled a code warning using UnityEngine; namespace Kino { [ExecuteInEditMode] [RequireComponent(typeof(Camera))] [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 #pragma warning disable 0649 [SerializeField, HideInInspector] Shader _shader; #pragma warning restore 0649 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/Kino/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 } }