using UnityEngine; // Glow uses the alpha channel as a source of "extra brightness". // All builtin Unity shaders output baseTexture.alpha * color.alpha, plus // specularHighlight * specColor.alpha into that. // Usually you'd want either to make base textures to have zero alpha; or // set the color to have zero alpha (by default alpha is 0.5). [ExecuteInEditMode] [RequireComponent (typeof(Camera))] [AddComponentMenu("Image Effects/Glow")] public class GlowEffect : MonoBehaviour { /// The brightness of the glow. Values larger than one give extra "boost". public float glowIntensity = 1.5f; /// Blur iterations - larger number means more blur. public int blurIterations = 3; /// Blur spread for each iteration. Lower values /// give better looking blur, but require more iterations to /// get large blurs. Value is usually between 0.5 and 1.0. public float blurSpread = 0.7f; /// Tint glow with this color. Alpha adds additional glow everywhere. public Color glowTint = new Color(1,1,1,0); // -------------------------------------------------------- // The final composition shader: // adds (glow color * glow alpha * amount) to the original image. // In the combiner glow amount can be only in 0..1 range; we apply extra // amount during the blurring phase. public Shader compositeShader; Material m_CompositeMaterial = null; protected Material compositeMaterial { get { if (m_CompositeMaterial == null) { m_CompositeMaterial = new Material(compositeShader); m_CompositeMaterial.hideFlags = HideFlags.HideAndDontSave; } return m_CompositeMaterial; } } // -------------------------------------------------------- // The blur iteration shader. // Basically it just takes 4 texture samples and averages them. // By applying it repeatedly and spreading out sample locations // we get a Gaussian blur approximation. // The alpha value in _Color would normally be 0.25 (to average 4 samples), // however if we have glow amount larger than 1 then we increase this. public Shader blurShader; Material m_BlurMaterial = null; protected Material blurMaterial { get { if (m_BlurMaterial == null) { m_BlurMaterial = new Material(blurShader); m_BlurMaterial.hideFlags = HideFlags.HideAndDontSave; } return m_BlurMaterial; } } // -------------------------------------------------------- // The image downsample shaders for each brightness mode. // It is in external assets as it's quite complex and uses Cg. public Shader downsampleShader; Material m_DownsampleMaterial = null; protected Material downsampleMaterial { get { if (m_DownsampleMaterial == null) { m_DownsampleMaterial = new Material( downsampleShader ); m_DownsampleMaterial.hideFlags = HideFlags.HideAndDontSave; } return m_DownsampleMaterial; } } // -------------------------------------------------------- // finally, the actual code protected void OnDisable() { if( m_CompositeMaterial ) { DestroyImmediate( m_CompositeMaterial ); } if( m_BlurMaterial ) { DestroyImmediate( m_BlurMaterial ); } if( m_DownsampleMaterial ) DestroyImmediate( m_DownsampleMaterial ); } protected void Start() { // Disable if we don't support image effects if (!SystemInfo.supportsImageEffects) { enabled = false; return; } // Disable the effect if no downsample shader is setup if( downsampleShader == null ) { Debug.Log ("No downsample shader assigned! Disabling glow."); enabled = false; } // Disable if any of the shaders can't run on the users graphics card else { if( !blurMaterial.shader.isSupported ) enabled = false; if( !compositeMaterial.shader.isSupported ) enabled = false; if( !downsampleMaterial.shader.isSupported ) enabled = false; } } // Performs one blur iteration. public void FourTapCone (RenderTexture source, RenderTexture dest, int iteration) { float off = 0.5f + iteration*blurSpread; Graphics.BlitMultiTap (source, dest, blurMaterial, new Vector2( off, off), new Vector2(-off, off), new Vector2( off,-off), new Vector2(-off,-off) ); } // Downsamples the texture to a quarter resolution. private void DownSample4x (RenderTexture source, RenderTexture dest) { downsampleMaterial.color = new Color( glowTint.r, glowTint.g, glowTint.b, glowTint.a/4.0f ); Graphics.Blit (source, dest, downsampleMaterial); } // Called by the camera to apply the image effect void OnRenderImage (RenderTexture source, RenderTexture destination) { // Clamp parameters to sane values glowIntensity = Mathf.Clamp( glowIntensity, 0.0f, 10.0f ); blurIterations = Mathf.Clamp( blurIterations, 0, 30 ); blurSpread = Mathf.Clamp( blurSpread, 0.5f, 1.0f ); RenderTexture buffer = RenderTexture.GetTemporary(source.width/4, source.height/4, 0); RenderTexture buffer2 = RenderTexture.GetTemporary(source.width/4, source.height/4, 0); // Copy source to the 4x4 smaller texture. DownSample4x (source, buffer); // Blur the small texture float extraBlurBoost = Mathf.Clamp01( (glowIntensity - 1.0f) / 4.0f ); blurMaterial.color = new Color( 1F, 1F, 1F, 0.25f + extraBlurBoost ); bool oddEven = true; for(int i = 0; i < blurIterations; i++) { if( oddEven ) FourTapCone (buffer, buffer2, i); else FourTapCone (buffer2, buffer, i); oddEven = !oddEven; } Graphics.Blit(source,destination); if( oddEven ) BlitGlow(buffer, destination); else BlitGlow(buffer2, destination); RenderTexture.ReleaseTemporary(buffer); RenderTexture.ReleaseTemporary(buffer2); } public void BlitGlow( RenderTexture source, RenderTexture dest ) { compositeMaterial.color = new Color(1F, 1F, 1F, Mathf.Clamp01(glowIntensity)); Graphics.Blit (source, dest, compositeMaterial); } }