You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
7.1 KiB
272 lines
7.1 KiB
// |
|
// 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 |
|
} |
|
}
|
|
|