273 lines
7.1 KiB
C#
273 lines
7.1 KiB
C#
//
|
|
// 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
|
|
}
|
|
}
|