using UnityEngine; using System.Collections; [ExecuteInEditMode] [AddComponentMenu("Image Effects/Blur (island)")] public class BlurEffectIsland : MonoBehaviour { /// Blur iterations - larger number means more blur. public int iterations = 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.6f; // -------------------------------------------------------- // 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. private static string blurMatString = @"Shader ""BlurConeTap"" { SubShader { Pass { ZTest Always Cull Off ZWrite Off Fog { Mode Off } SetTexture [__RenderTex] {constantColor (0,0,0,0.25) combine texture * constant alpha} SetTexture [__RenderTex] {constantColor (0,0,0,0.25) combine texture * constant + previous} SetTexture [__RenderTex] {constantColor (0,0,0,0.25) combine texture * constant + previous} SetTexture [__RenderTex] {constantColor (0,0,0,0.25) combine texture * constant + previous} } } Fallback off }"; static Material m_Material = null; protected static Material material { get { if (m_Material == null) { m_Material = new Material(blurMatString); m_Material.hideFlags = HideFlags.HideAndDontSave; m_Material.shader.hideFlags = HideFlags.HideAndDontSave; } return m_Material; } } protected void OnDisable() { if (m_Material) { DestroyImmediate(m_Material.shader); DestroyImmediate(m_Material); } } // -------------------------------------------------------- public bool IsSupported() { // Disable if we don't support image effects if (!SystemInfo.supportsImageEffects) return false; // Disable if the shader can't run on the users graphics card if (!material.shader.isSupported) return false; return true; } protected void Start() { if (!IsSupported()) enabled = false; } // Performs one blur iteration. public void FourTapCone(RenderTexture source, RenderTexture dest, int iteration) { RenderTexture.active = dest; source.SetGlobalShaderProperty("__RenderTex"); float offsetX = (.5F + iteration * blurSpread) / (float)source.width; float offsetY = (.5F + iteration * blurSpread) / (float)source.height; GL.PushMatrix(); GL.LoadOrtho(); for (int i = 0; i < material.passCount; i++) { material.SetPass(i); Render4TapQuad(dest, offsetX, offsetY); } GL.PopMatrix(); } // Downsamples the texture to a quarter resolution. private void DownSample4x(RenderTexture source, RenderTexture dest) { RenderTexture.active = dest; source.SetGlobalShaderProperty("__RenderTex"); float offsetX = 1.0f / (float)source.width; float offsetY = 1.0f / (float)source.height; GL.PushMatrix(); GL.LoadOrtho(); for (int i = 0; i < material.passCount; i++) { material.SetPass(i); Render4TapQuad(dest, offsetX, offsetY); } GL.PopMatrix(); } // Called by the camera to apply the image effect void OnRenderImage(RenderTexture source, RenderTexture destination) { 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 bool oddEven = true; for (int i = 0; i < iterations; i++) { if (oddEven) FourTapCone(buffer, buffer2, i); else FourTapCone(buffer2, buffer, i); oddEven = !oddEven; } if (oddEven) ImageEffects.Blit(buffer, destination); else ImageEffects.Blit(buffer2, destination); RenderTexture.ReleaseTemporary(buffer); RenderTexture.ReleaseTemporary(buffer2); } private static void Render4TapQuad(RenderTexture dest, float offsetX, float offsetY) { GL.Begin(GL.QUADS); // Direct3D needs interesting texel offsets! Vector2 off = Vector2.zero; if (dest != null) off = dest.GetTexelOffset() * 0.75f; Set4TexCoords(off.x, off.y, offsetX, offsetY); GL.Vertex3(0, 0, .1f); Set4TexCoords(1.0f + off.x, off.y, offsetX, offsetY); GL.Vertex3(1, 0, .1f); Set4TexCoords(1.0f + off.x, 1.0f + off.y, offsetX, offsetY); GL.Vertex3(1, 1, .1f); Set4TexCoords(off.x, 1.0f + off.y, offsetX, offsetY); GL.Vertex3(0, 1, .1f); GL.End(); } private static void Set4TexCoords(float x, float y, float offsetX, float offsetY) { GL.MultiTexCoord2(0, x - offsetX, y - offsetY); GL.MultiTexCoord2(1, x + offsetX, y - offsetY); GL.MultiTexCoord2(2, x + offsetX, y + offsetY); GL.MultiTexCoord2(3, x - offsetX, y + offsetY); } }