diff --git a/Scripts/Effects/SoftMaskScript.cs b/Scripts/Effects/SoftMaskScript.cs new file mode 100644 index 0000000..30e8ef8 --- /dev/null +++ b/Scripts/Effects/SoftMaskScript.cs @@ -0,0 +1,164 @@ +using UnityEngine; +using UnityEngine.UI; + +using System.Collections; + +[ExecuteInEditMode] +[AddComponentMenu("UI/Effects/Extensions/SoftMaskScript")] +public class SoftMaskScript : MonoBehaviour +{ + Material mat; + Canvas canvas; + + [Tooltip("The area that is to be used as the container.")] + public RectTransform MaskArea; + RectTransform myRect; + + [Tooltip("Texture to be used to do the soft alpha")] + public Texture AlphaMask; + + [Tooltip("At what point to apply the alpha min range 0-1")] + [Range(0, 1)] + public float CutOff = 0; + + [Tooltip("Implement a hard blend based on the Cutoff")] + public bool HardBlend = false; + + Vector3[] worldCorners; + + Vector2 AlphaUV; + + Vector2 min; + Vector2 max = Vector2.one; + Vector2 p; + Vector2 siz; + + Rect maskRect; + Rect contentRect; + + Vector2 centre; + + bool isText = false; + + // Use this for initialization + void Start() + { + myRect = GetComponent(); + + if (!MaskArea) + { + MaskArea = myRect; + } + + if (GetComponent() != null) + { + mat = new Material(Shader.Find("UI Extensions/SoftMaskShader")); + GetComponent().material = mat; + } + + if (GetComponent()) + { + isText = true; + mat = new Material(Shader.Find("UI Extensions/SoftMaskShaderText")); + GetComponent().material = mat; + + GetCanvas(); + + // For some reason, having the mask control on the parent and disabled stops the mouse interacting + // with the texture layer that is not visible.. Not needed for the Image. + if (transform.parent.GetComponent() == null) + transform.parent.gameObject.AddComponent(); + + transform.parent.GetComponent().enabled = false; + } + } + + void GetCanvas() + { + Transform t = transform; + + int lvlLimit = 100; + int lvl = 0; + + while (canvas == null && lvl < lvlLimit) + { + canvas = t.gameObject.GetComponent(); + if (canvas == null) + t = GetParentTranform(t); + + lvl++; + } + } + + Transform GetParentTranform(Transform t) + { + return t.parent; + } + + void Update() + { + SetMask(); + } + + void SetMask() + { + // Get the two rectangle areas + maskRect = MaskArea.rect; + contentRect = myRect.rect; + + if (isText) // Need to do our calculations in world for Text + { + if (canvas.renderMode == RenderMode.ScreenSpaceOverlay && Application.isPlaying) + { + p = canvas.transform.InverseTransformPoint(MaskArea.transform.position); + siz = new Vector2(maskRect.width, maskRect.height); + } + else + { + worldCorners = new Vector3[4]; + MaskArea.GetWorldCorners(worldCorners); + siz = (worldCorners[2] - worldCorners[0]); + p = MaskArea.transform.position; + } + + min = p - (new Vector2(siz.x, siz.y) * .5f); + max = p + (new Vector2(siz.x, siz.y) * .5f); + } + else // Need to do our calculations in tex space for Image. + { + // Get the centre offset + centre = myRect.transform.InverseTransformPoint(MaskArea.transform.position); + + // Set the scale for mapping texcoords mask + AlphaUV = new Vector2(maskRect.width / contentRect.width, maskRect.height / contentRect.height); + + // set my min and max to the centre offest + min = centre; + max = min; + + siz = new Vector2(maskRect.width, maskRect.height) * .5f; + // Move them out to the min max extreams + min -= siz; + max += siz; + + // Now move these into texture space. 0 - 1 + min = new Vector2(min.x / contentRect.width, min.y / contentRect.height) + new Vector2(.5f, .5f); + max = new Vector2(max.x / contentRect.width, max.y / contentRect.height) + new Vector2(.5f, .5f); + + } + + mat.SetFloat("_HardBlend", HardBlend ? 1 : 0); + + // Pass the values to the shader + mat.SetVector("_Min", min); + mat.SetVector("_Max", max); + + mat.SetTexture("_AlphaMask", AlphaMask); + + if (!isText) // No mod needed for Text + mat.SetVector("_AlphaUV", AlphaUV); + + mat.SetFloat("_CutOff", CutOff); + } + +} \ No newline at end of file diff --git a/Scripts/Effects/SoftMaskScript.cs.meta b/Scripts/Effects/SoftMaskScript.cs.meta new file mode 100644 index 0000000..f9e28c2 --- /dev/null +++ b/Scripts/Effects/SoftMaskScript.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b8ee60af491978345bc197ed4e1316bc +timeCreated: 1448034177 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Shaders/SoftMaskShader.shader b/Shaders/SoftMaskShader.shader new file mode 100644 index 0000000..ad68915 --- /dev/null +++ b/Shaders/SoftMaskShader.shader @@ -0,0 +1,155 @@ +Shader "UI Extensions/SoftMaskShader" +{ + Properties + { + [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {} + _Color("Tint", Color) = (1,1,1,1) + + _StencilComp("Stencil Comparison", Float) = 8 + _Stencil("Stencil ID", Float) = 0 + _StencilOp("Stencil Operation", Float) = 0 + _StencilWriteMask("Stencil Write Mask", Float) = 255 + _StencilReadMask("Stencil Read Mask", Float) = 255 + + _ColorMask("Color Mask", Float) = 15 + + _Min("Min",Vector) = (0,0,0,0) + _Max("Max",Vector) = (1,1,0,0) + _AlphaMask("AlphaMask - Must be Wrapped",2D) = "white"{} + _AlphaUV("AlphaUV",Vector) = (0,0,0,0) + _CutOff("CutOff",Float) = 0 + [MaterialToggle] + _HardBlend("HardBlend",Float) = 0 + } + + SubShader + { + Tags + { + "Queue" = "Transparent" + "IgnoreProjector" = "True" + "RenderType" = "Transparent" + "PreviewType" = "Plane" + "CanUseSpriteAtlas" = "True" + } + + Stencil + { + Ref[_Stencil] + Comp[_StencilComp] + Pass[_StencilOp] + ReadMask[_StencilReadMask] + WriteMask[_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest[unity_GUIZTestMode] + Blend SrcAlpha OneMinusSrcAlpha + ColorMask[_ColorMask] + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct appdata_t + { + float4 vertex : POSITION; + float4 color : COLOR; + float2 texcoord : TEXCOORD0; + }; + + struct v2f + { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + half2 texcoord : TEXCOORD0; + float4 worldPosition : TEXCOORD1; + }; + + inline float UnityGet2DClipping (in float2 position, in float4 clipRect) + { + float2 inside = step(clipRect.xy, position.xy) * step(position.xy, clipRect.zw); + return inside.x * inside.y; + } + + fixed4 _Color; + fixed4 _TextureSampleAdd; + + bool _UseClipRect; + float4 _ClipRect; + + bool _UseAlphaClip; + + float4 _ProgressColor; + float _Value; + int _LeftToRight; + + v2f vert(appdata_t IN) + { + v2f OUT; + OUT.worldPosition = IN.vertex; + OUT.vertex = mul(UNITY_MATRIX_MVP, OUT.worldPosition); + + OUT.texcoord = IN.texcoord; + + #ifdef UNITY_HALF_TEXEL_OFFSET + OUT.vertex.xy += (_ScreenParams.zw - 1.0)*float2(-1,1); + #endif + + OUT.color = IN.color * _Color; + return OUT; + } + + sampler2D _MainTex; + sampler2D _AlphaMask; + + float2 _AlphaUV; + + float2 _Min; + float2 _Max; + + float _CutOff; + + bool _HardBlend = false; + + fixed4 frag(v2f IN) : SV_Target + { + half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; + + + // Do we want to clip the image to the Mask Rectangle? + if (IN.texcoord.x < _Min.x || IN.texcoord.x > _Max.x || IN.texcoord.y < _Min.y || IN.texcoord.y > _Max.y) // Yes we do + color.a = 0; + else // It's in the mask rectangle, so apply the alpha of the mask provided. + { + float a = tex2D(_AlphaMask, (IN.texcoord - _Min) / _AlphaUV).a; + + if (a <= _CutOff) + a = 0; + else + { + if(_HardBlend) + a = 1; + } + + color.a = a; + } + + if (_UseClipRect) + color *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); + + if (_UseAlphaClip) + clip(color.a - 0.001); + + return color; + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Shaders/SoftMaskShader.shader.meta b/Shaders/SoftMaskShader.shader.meta new file mode 100644 index 0000000..c12a269 --- /dev/null +++ b/Shaders/SoftMaskShader.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 947afae4d36f1274ea2e4098262ceef6 +timeCreated: 1444851202 +licenseType: Pro +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Shaders/SoftMaskShaderText.shader b/Shaders/SoftMaskShaderText.shader new file mode 100644 index 0000000..1522ef0 --- /dev/null +++ b/Shaders/SoftMaskShaderText.shader @@ -0,0 +1,156 @@ +Shader "UI Extensions/SoftMaskShaderText" +{ + Properties + { + [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {} + _Color("Tint", Color) = (1,1,1,1) + + _StencilComp("Stencil Comparison", Float) = 8 + _Stencil("Stencil ID", Float) = 0 + _StencilOp("Stencil Operation", Float) = 0 + _StencilWriteMask("Stencil Write Mask", Float) = 255 + _StencilReadMask("Stencil Read Mask", Float) = 255 + + _ColorMask("Color Mask", Float) = 15 + + _Min("Min",Vector) = (0,0,0,0) + _Max("Max",Vector) = (1,1,0,0) + _AlphaMask("AlphaMask - Must be Wrapped",2D) = "white"{} + _CutOff("CutOff",Float) = 0 + [MaterialToggle] + _HardBlend("HardBlend",Float) = 0 + } + + SubShader + { + Tags + { + "Queue" = "Transparent" + "IgnoreProjector" = "True" + "RenderType" = "Transparent" + "PreviewType" = "Plane" + "CanUseSpriteAtlas" = "True" + } + + Stencil + { + Ref[_Stencil] + Comp[_StencilComp] + Pass[_StencilOp] + ReadMask[_StencilReadMask] + WriteMask[_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest[unity_GUIZTestMode] + Blend SrcAlpha OneMinusSrcAlpha + ColorMask[_ColorMask] + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct appdata_t + { + float4 vertex : POSITION; + float4 color : COLOR; + float2 texcoord : TEXCOORD0; + }; + + struct v2f + { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + half2 texcoord : TEXCOORD0; + float4 worldPosition : TEXCOORD1; + float4 worldPosition2 : COLOR1; + }; + + inline float UnityGet2DClipping (in float2 position, in float4 clipRect) + { + float2 inside = step(clipRect.xy, position.xy) * step(position.xy, clipRect.zw); + return inside.x * inside.y; + } + + fixed4 _Color; + fixed4 _TextureSampleAdd; + + bool _UseClipRect; + float4 _ClipRect; + + bool _UseAlphaClip; + + float4 _ProgressColor; + float _Value; + int _LeftToRight; + + bool _HardBlend = false; + + v2f vert(appdata_t IN) + { + v2f OUT; + OUT.worldPosition = IN.vertex; + OUT.vertex = mul(UNITY_MATRIX_MVP, OUT.worldPosition); + OUT.worldPosition2 = mul(_Object2World, IN.vertex); + + OUT.texcoord = IN.texcoord; + + #ifdef UNITY_HALF_TEXEL_OFFSET + OUT.vertex.xy += (_ScreenParams.zw - 1.0)*float2(-1,1); + OUT.worldPosition2 += (_ScreenParams.zw - 1.0)*float2(-1, 1); + #endif + + OUT.color = IN.color * _Color; + return OUT; + } + + sampler2D _MainTex; + sampler2D _AlphaMask; + + float2 _Min; + float2 _Max; + + float2 _Mul; + + float _CutOff; + + fixed4 frag(v2f IN) : SV_Target + { + half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; + + + // Do we want to clip the image to the Mask Rectangle? + if (IN.worldPosition2.x <= _Min.x || IN.worldPosition2.x >= _Max.x || IN.worldPosition2.y <= _Min.y || IN.worldPosition2.y >= _Max.y) + color.a = 0; + else // It's in the mask rectangle, so apply the alpha of the mask provided. + { + float a = tex2D(_AlphaMask, (IN.worldPosition2.xy - _Max) / (_Max-_Min)).a; + if (a <= _CutOff) + a = 0; + else + { + if (_HardBlend) + a = 1; + } + + color.a = a * color.a; + } + + if (_UseClipRect) + color *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); + + if (_UseAlphaClip) + clip(color.a - 0.001); + + return color; + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Shaders/SoftMaskShaderText.shader.meta b/Shaders/SoftMaskShaderText.shader.meta new file mode 100644 index 0000000..d0c147e --- /dev/null +++ b/Shaders/SoftMaskShaderText.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: abe89b015c410b54db04a4842127ae9e +timeCreated: 1445628990 +licenseType: Pro +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: