Last active
November 11, 2025 05:25
-
-
Save rumaniel/a8b929b8791f8759b19068ec00c36c32 to your computer and use it in GitHub Desktop.
To Applying Sprite Clipging
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using UnityEngine; | |
| using UnityEngine.EventSystems; | |
| [ExecuteInEditMode] | |
| public class SpritePanel : UIBehaviour | |
| { | |
| public enum Clipping | |
| { | |
| None, | |
| TextureMask, | |
| SoftClip | |
| }; | |
| [SerializeField] private Clipping _clipping; | |
| [SerializeField] private Vector4 _clipRange = new Vector4(0f, 0f, 300f, 200f); | |
| [SerializeField] private Vector2 _clipSoftness = new Vector2(0f, 0f); | |
| [SerializeField] private Texture2D _clipTexture = null; | |
| [SerializeField] private float _cutoff = -1f; | |
| [SerializeField] private float _cutoffSoftness = 1f; | |
| [SerializeField] private Material _material; | |
| [SerializeField] private Color _color = Color.white; | |
| private bool rebuildMaterial; | |
| public Clipping clipping | |
| { | |
| get => _clipping; | |
| set | |
| { | |
| if (_clipping != value) | |
| { | |
| _clipping = value; | |
| } | |
| } | |
| } | |
| public Vector4 clipRange | |
| { | |
| get => _clipRange; | |
| set | |
| { | |
| if (_clipRange != value) | |
| { | |
| _clipRange = value; | |
| } | |
| } | |
| } | |
| public Vector2 clipSoftness | |
| { | |
| get => _clipSoftness; | |
| set | |
| { | |
| if (_clipSoftness != value) | |
| { | |
| _clipSoftness = value; | |
| } | |
| } | |
| } | |
| public Texture2D clipTexture | |
| { | |
| get => _clipTexture; | |
| set | |
| { | |
| if (_clipTexture != value) | |
| { | |
| _clipTexture = value; | |
| } | |
| } | |
| } | |
| public float cutoff | |
| { | |
| get => _cutoff; | |
| set | |
| { | |
| if (_cutoff != value) | |
| { | |
| _cutoff = value; | |
| } | |
| } | |
| } | |
| public float cutoffSoftness | |
| { | |
| get => _cutoffSoftness; | |
| set | |
| { | |
| if (_cutoffSoftness != value) | |
| { | |
| _cutoffSoftness = value; | |
| } | |
| } | |
| } | |
| public Color color | |
| { | |
| get => _color; | |
| set | |
| { | |
| if (_color != value) | |
| { | |
| _color = value; | |
| } | |
| } | |
| } | |
| public Material material | |
| { | |
| get => _material; | |
| set | |
| { | |
| if (_material != value) | |
| { | |
| _material = value; | |
| rebuildMaterial = true; | |
| } | |
| } | |
| } | |
| public Material dynamicMaterial { get; protected set; } | |
| public SpriteGroup spriteGroup = null; | |
| protected override void Awake() | |
| { | |
| RebuildMaterial(); | |
| } | |
| protected override void OnEnable() | |
| { | |
| UpdateSpriteGroup(); | |
| } | |
| protected override void OnTransformParentChanged() | |
| { | |
| if (gameObject.activeSelf) | |
| UpdateSpriteGroup(); | |
| } | |
| private void UpdateSpriteGroup() | |
| { | |
| spriteGroup = gameObject.GetComponentInParent<SpriteGroup>(); | |
| } | |
| protected override void OnDestroy() | |
| { | |
| UnityUtil.DestroyObject(dynamicMaterial); | |
| dynamicMaterial = null; | |
| } | |
| private void RebuildMaterial() | |
| { | |
| UnityUtil.DestroyObject(dynamicMaterial); | |
| dynamicMaterial = new Material(material); | |
| dynamicMaterial.name = material.name; | |
| dynamicMaterial.hideFlags = HideFlags.DontSave | HideFlags.NotEditable; | |
| dynamicMaterial.CopyPropertiesFromMaterial(material); | |
| string[] keywords = material.shaderKeywords; | |
| for (int i = 0; i < keywords.Length; ++i) | |
| dynamicMaterial.EnableKeyword(keywords[i]); | |
| } | |
| protected virtual void LateUpdate() | |
| { | |
| if (material != null && rebuildMaterial) | |
| { | |
| RebuildMaterial(); | |
| rebuildMaterial = false; | |
| } | |
| UpdateDrawCall(); | |
| } | |
| public void UpdateDrawCall() | |
| { | |
| UpdateDrawCall(dynamicMaterial, 0); | |
| } | |
| public void UpdateDrawCall(Material mat, int depth) | |
| { | |
| if (mat == null) return; | |
| if (_clipping != Clipping.None) | |
| { | |
| Vector3 pos = transform.position; | |
| Vector3 scale = transform.lossyScale; | |
| Vector4 cr = _clipRange; | |
| cr.x *= scale.x; | |
| cr.y *= scale.y; | |
| cr.z *= 0.5f * scale.x; | |
| cr.w *= 0.5f * scale.y; | |
| if (_clipping == Clipping.SoftClip) | |
| { | |
| Vector2 sharpness = new Vector2(1000.0f, 1000.0f); | |
| if (_clipSoftness.x > 0f) sharpness.x = cr.z / _clipSoftness.x; | |
| if (_clipSoftness.y > 0f) sharpness.y = cr.w / _clipSoftness.y; | |
| mat.SetVector(SpriteShaderUtils.CLIP_ARGS_PROPERTY_ID[depth], new Vector4(sharpness.x, sharpness.y, 0f, 0f)); | |
| } | |
| else if (_clipping == Clipping.TextureMask) | |
| { | |
| float sharpness = 1f; | |
| if (_cutoffSoftness > 0f) sharpness = 1f / _cutoffSoftness; | |
| mat.SetTexture(SpriteShaderUtils.CLIP_TEX_PROPERTY_ID[depth], _clipTexture); | |
| mat.SetFloat(SpriteShaderUtils.CLIP_CUTOFF_PROPERTY_ID[depth], _cutoff); | |
| mat.SetFloat(SpriteShaderUtils.CLIP_CUTOFF_SHARPNESS_PROPERTY_ID[depth], sharpness); | |
| } | |
| mat.SetVector(SpriteShaderUtils.CLIP_PIVOT_PROPERTY_ID[depth], new Vector4(pos.x, pos.y, pos.z, 0f)); | |
| mat.SetMatrix(SpriteShaderUtils.CLIP_ROTATATION_PROPERTY_ID[depth], Matrix4x4.TRS(Vector3.zero, Quaternion.Inverse(transform.rotation), Vector3.one)); | |
| mat.SetVector(SpriteShaderUtils.CLIP_RANGE_PROPERTY_ID[depth], new Vector4(-cr.x / cr.z, -cr.y / cr.w, 1f / cr.z, 1f / cr.w)); | |
| var parent = transform.parent; | |
| if (parent == null) return; | |
| var panel = parent.GetComponentInParent<SpritePanel>(); | |
| if (panel != null) | |
| panel.UpdateDrawCall(mat, depth + 1); | |
| } | |
| if (depth == 0) | |
| { | |
| if (spriteGroup == null) | |
| mat.SetColor(SpriteShaderUtils.COLOR_PROPERTY_ID, _color); | |
| else | |
| mat.SetColor(SpriteShaderUtils.COLOR_PROPERTY_ID, _color * spriteGroup.color); | |
| } | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using UnityEngine; | |
| using UnityEditor; | |
| [CustomEditor(typeof(SpritePanel))] | |
| public class SpritePanelInspector : Editor | |
| { | |
| private SpritePanel panel; | |
| private void OnEnable() | |
| { | |
| panel = target as SpritePanel; | |
| } | |
| public override void OnInspectorGUI() | |
| { | |
| Material mat = (Material)EditorGUILayout.ObjectField("Material", panel.material, typeof(Material), false); | |
| if (panel.material != mat) | |
| { | |
| panel.material = mat; | |
| } | |
| panel.color = EditorGUILayout.ColorField("Color", panel.color); | |
| SpritePanel.Clipping clipping = (SpritePanel.Clipping)EditorGUILayout.EnumPopup("Clipping", panel.clipping); | |
| if (panel.clipping != clipping) | |
| { | |
| panel.clipping = clipping; | |
| } | |
| if (panel.clipping != SpritePanel.Clipping.None) | |
| { | |
| Vector4 range = panel.clipRange; | |
| GUILayout.BeginHorizontal(); | |
| GUILayout.Space(80f); | |
| Vector2 pos = EditorGUILayout.Vector2Field("Center", new Vector2(range.x, range.y), GUILayout.MinWidth(20f)); | |
| GUILayout.EndHorizontal(); | |
| GUILayout.BeginHorizontal(); | |
| GUILayout.Space(80f); | |
| Vector2 size = EditorGUILayout.Vector2Field("Size", new Vector2(range.z, range.w), GUILayout.MinWidth(20f)); | |
| GUILayout.EndHorizontal(); | |
| range.x = pos.x; | |
| range.y = pos.y; | |
| range.z = size.x; | |
| range.w = size.y; | |
| if (panel.clipRange != range) | |
| { | |
| panel.clipRange = range; | |
| } | |
| if (panel.clipping == SpritePanel.Clipping.SoftClip) | |
| { | |
| GUILayout.BeginHorizontal(); | |
| GUILayout.Space(80f); | |
| Vector2 soft = EditorGUILayout.Vector2Field("Softness", panel.clipSoftness, GUILayout.MinWidth(20f)); | |
| GUILayout.EndHorizontal(); | |
| if (panel.clipSoftness != soft) | |
| { | |
| panel.clipSoftness = soft; | |
| } | |
| } | |
| else if (panel.clipping == SpritePanel.Clipping.TextureMask) | |
| { | |
| GUILayout.BeginHorizontal(); | |
| GUILayout.Space(80f); | |
| float cutoff = EditorGUILayout.FloatField("Cutoff", panel.cutoff); | |
| cutoff = Mathf.Clamp(cutoff, -1f, 1f); | |
| GUILayout.EndHorizontal(); | |
| if (panel.cutoff != cutoff) | |
| { | |
| panel.cutoff = cutoff; | |
| } | |
| GUILayout.BeginHorizontal(); | |
| GUILayout.Space(80f); | |
| float soft = EditorGUILayout.FloatField("Cutoff Softness", panel.cutoffSoftness); | |
| soft = Mathf.Clamp(soft, 0.001f, 1f); | |
| GUILayout.EndHorizontal(); | |
| if (panel.cutoffSoftness != soft) | |
| { | |
| panel.cutoffSoftness = soft; | |
| } | |
| GUILayout.BeginHorizontal(); | |
| GUILayout.Space(80f); | |
| EditorGUIUtility.labelWidth = 0; | |
| EditorGUIUtility.fieldWidth = 15; | |
| Texture2D tex = (Texture2D)EditorGUILayout.ObjectField("Clip Texture", panel.clipTexture, | |
| typeof(Texture2D), false); | |
| GUILayout.EndHorizontal(); | |
| if (panel.clipTexture != tex) | |
| { | |
| panel.clipTexture = tex; | |
| } | |
| } | |
| if (GUILayout.Button("Fit")) | |
| { | |
| size = panel.GetComponent<RectTransform>().sizeDelta; | |
| panel.clipRange = new Vector4(0f, 0f, size.x, size.y); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using UnityEngine; | |
| public static class SpriteShaderUtils | |
| { | |
| public static int COLOR_PROPERTY_ID; | |
| public static int[] CLIP_PIVOT_PROPERTY_ID = new int[4]; | |
| public static int[] CLIP_RANGE_PROPERTY_ID = new int[4]; | |
| public static int[] CLIP_ROTATATION_PROPERTY_ID = new int[4]; | |
| public static int[] CLIP_ARGS_PROPERTY_ID = new int[4]; | |
| public static int[] CLIP_TEX_PROPERTY_ID = new int[4]; | |
| public static int[] CLIP_CUTOFF_PROPERTY_ID = new int[4]; | |
| public static int[] CLIP_CUTOFF_SHARPNESS_PROPERTY_ID = new int[4]; | |
| static SpriteShaderUtils() | |
| { | |
| GetShaderPropertyIDs(); | |
| } | |
| public static void GetShaderPropertyIDs() | |
| { | |
| COLOR_PROPERTY_ID = Shader.PropertyToID("_Color"); | |
| for (int i = 0; i < 4; ++i) | |
| { | |
| string postFix = ""; | |
| if (i > 0) postFix = i.ToString(); | |
| CLIP_PIVOT_PROPERTY_ID[i] = Shader.PropertyToID("_ClipPivot" + postFix); | |
| CLIP_RANGE_PROPERTY_ID[i] = Shader.PropertyToID("_ClipRange" + postFix); | |
| CLIP_ROTATATION_PROPERTY_ID[i] = Shader.PropertyToID("_ClipRotation" + postFix); | |
| CLIP_ARGS_PROPERTY_ID[i] = Shader.PropertyToID("_ClipArgs" + postFix); | |
| CLIP_TEX_PROPERTY_ID[i] = Shader.PropertyToID("_ClipTex" + postFix); | |
| CLIP_CUTOFF_PROPERTY_ID[i] = Shader.PropertyToID("_Cutoff" + postFix); | |
| CLIP_CUTOFF_SHARPNESS_PROPERTY_ID[i] = Shader.PropertyToID("_CutoffSharpness" + postFix); | |
| } | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Shader "UI/Soft Clip" | |
| { | |
| Properties | |
| { | |
| [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} | |
| _Color ("Tint", Color) = (1,1,1,1) | |
| _ClipPivot ("Clip Pivot", Vector) = (0,0,0,0) | |
| _ClipRange ("Clip Range", Vector) = (0,0,1,1) | |
| _ClipArgs ("Clip Arguments", Vector) = (1000,1000,0,1) | |
| _ColorMask("Color Mask", Float) = 15 | |
| } | |
| SubShader | |
| { | |
| Tags | |
| { | |
| "Queue"="Transparent" | |
| "IgnoreProjector"="True" | |
| "RenderType"="Transparent" | |
| "PreviewType"="Plane" | |
| "CanUseSpriteAtlas"="True" | |
| } | |
| Cull Off | |
| Lighting Off | |
| ZWrite Off | |
| ZTest[unity_GUIZTestMode] | |
| Blend SrcAlpha OneMinusSrcAlpha | |
| Pass | |
| { | |
| CGPROGRAM | |
| #pragma vertex vert | |
| #pragma fragment frag | |
| #pragma target 2.0 | |
| #include "UnityCG.cginc" | |
| #include "UnityUI.cginc" | |
| fixed4 _Color; | |
| fixed4 _TextureSampleAdd; | |
| float4 _ClipPivot; | |
| float4x4 _ClipRotation; | |
| float4 _ClipRange; | |
| float4 _ClipArgs; | |
| sampler2D _MainTex; | |
| struct appdata_ct | |
| { | |
| float4 vertex : POSITION; | |
| float4 color : COLOR; | |
| float2 texcoord : TEXCOORD0; | |
| UNITY_VERTEX_INPUT_INSTANCE_ID | |
| }; | |
| struct v2f_ct | |
| { | |
| float4 vertex : SV_POSITION; | |
| fixed4 color : COLOR; | |
| half2 texcoord : TEXCOORD0; | |
| float2 worldPos : TEXCOORD1; | |
| UNITY_VERTEX_OUTPUT_STEREO | |
| }; | |
| v2f_ct vert(appdata_ct IN) | |
| { | |
| v2f_ct OUT; | |
| UNITY_SETUP_INSTANCE_ID (IN); | |
| UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); | |
| OUT.vertex = UnityObjectToClipPos(IN.vertex); | |
| OUT.texcoord = IN.texcoord; | |
| OUT.color = IN.color * _Color; | |
| OUT.worldPos = _ClipPivot.xy - mul(unity_ObjectToWorld, IN.vertex).xy; | |
| OUT.worldPos = mul(_ClipRotation, float4(OUT.worldPos, 0, 0)).xy * _ClipRange.zw + _ClipRange.xy; | |
| return OUT; | |
| } | |
| fixed4 frag(v2f_ct IN) : SV_Target | |
| { | |
| // Softness factor | |
| fixed2 factor = (float2(1, 1) - abs(IN.worldPos)) * _ClipArgs.xy; | |
| fixed fade = clamp(min(factor.x, factor.y), 0, 1); | |
| half4 c = IN.color * (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd); | |
| c.a *= fade; | |
| c.rgb *= c.a; | |
| return c; | |
| } | |
| ENDCG | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment