|
|
@@ -0,0 +1,268 @@
|
|
|
+#unity/日常积累
|
|
|
+
|
|
|
+## SimpleUIHole.shader
|
|
|
+
|
|
|
+``` shell
|
|
|
+Shader "UI/SimpleUIHole"
|
|
|
+{
|
|
|
+ Properties
|
|
|
+ {
|
|
|
+ [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
|
|
+ _Color ("Dim Color", Color) = (0,0,0,0.7)
|
|
|
+
|
|
|
+ _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
|
|
|
+
|
|
|
+ [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
|
|
+
|
|
|
+ _Center ("Center", Vector) = (0,0,0,0)
|
|
|
+ _Radius ("Circle Radius", Float) = 200
|
|
|
+ _HalfSize ("Rect/Ellipse Half Size (x,y)", Vector) = (200,120,0,0)
|
|
|
+ _Shape ("Shape: 0 Circle, 1 Rect, 2 Ellipse", Float) = 0
|
|
|
+ _Softness ("Edge Softness", Range(0,50)) = 8
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+ {
|
|
|
+ Name "Default"
|
|
|
+ CGPROGRAM
|
|
|
+ #pragma vertex vert
|
|
|
+ #pragma fragment frag
|
|
|
+ #pragma target 2.0
|
|
|
+
|
|
|
+ #include "UnityCG.cginc"
|
|
|
+ #include "UnityUI.cginc"
|
|
|
+
|
|
|
+ #pragma multi_compile __ UNITY_UI_CLIP_RECT
|
|
|
+ #pragma multi_compile __ UNITY_UI_ALPHACLIP
|
|
|
+
|
|
|
+ struct appdata_t
|
|
|
+ {
|
|
|
+ float4 vertex : POSITION;
|
|
|
+ float4 color : COLOR;
|
|
|
+ float2 texcoord : TEXCOORD0;
|
|
|
+ UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
|
+ };
|
|
|
+
|
|
|
+ struct v2f
|
|
|
+ {
|
|
|
+ float4 vertex : SV_POSITION;
|
|
|
+ fixed4 color : COLOR;
|
|
|
+ float2 texcoord : TEXCOORD0;
|
|
|
+ float4 worldPosition : TEXCOORD1;
|
|
|
+ UNITY_VERTEX_OUTPUT_STEREO
|
|
|
+ };
|
|
|
+
|
|
|
+ sampler2D _MainTex;
|
|
|
+ fixed4 _Color;
|
|
|
+ fixed4 _TextureSampleAdd;
|
|
|
+ float4 _ClipRect;
|
|
|
+ float4 _MainTex_ST;
|
|
|
+
|
|
|
+ float2 _Center;
|
|
|
+ float _Radius;
|
|
|
+ float2 _HalfSize;
|
|
|
+ float _Shape; // 0 circle, 1 rect, 2 ellipse
|
|
|
+ float _Softness;
|
|
|
+
|
|
|
+ v2f vert(appdata_t v)
|
|
|
+ {
|
|
|
+ v2f OUT;
|
|
|
+ UNITY_SETUP_INSTANCE_ID(v);
|
|
|
+ UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
|
|
+ OUT.worldPosition = v.vertex;
|
|
|
+ OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
|
|
|
+ OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
|
|
|
+ OUT.color = v.color * _Color;
|
|
|
+ return OUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ float rectSDF(float2 p, float2 b)
|
|
|
+ {
|
|
|
+ float2 d = abs(p) - b;
|
|
|
+ return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
|
|
|
+ }
|
|
|
+
|
|
|
+ fixed4 frag(v2f IN) : SV_Target
|
|
|
+ {
|
|
|
+ half4 baseCol = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
|
|
|
+
|
|
|
+ #ifdef UNITY_UI_CLIP_RECT
|
|
|
+ baseCol.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #ifdef UNITY_UI_ALPHACLIP
|
|
|
+ clip (baseCol.a - 0.001);
|
|
|
+ #endif
|
|
|
+
|
|
|
+ float2 p = IN.worldPosition.xy - _Center.xy;
|
|
|
+
|
|
|
+ float distCircle = length(p) - _Radius;
|
|
|
+ float distRect = rectSDF(p, _HalfSize);
|
|
|
+ float dist = distCircle;
|
|
|
+ if (_Shape > 0.5) dist = distRect;
|
|
|
+ if (_Shape > 1.5)
|
|
|
+ {
|
|
|
+ float2 a = max(_HalfSize, float2(1e-5, 1e-5));
|
|
|
+ float norm = sqrt((p.x*p.x)/(a.x*a.x) + (p.y*p.y)/(a.y*a.y));
|
|
|
+ float distEllipse = (norm - 1.0) * max(a.x, a.y);
|
|
|
+ dist = distEllipse;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Smooth step from opaque (outside) to transparent (inside)
|
|
|
+ float edge = _Softness * 0.5;
|
|
|
+ float alphaMask = saturate(smoothstep(0.0, edge, dist));
|
|
|
+
|
|
|
+ fixed4 outCol = baseCol;
|
|
|
+ outCol.rgb = _Color.rgb;
|
|
|
+ outCol.a = _Color.a * alphaMask;
|
|
|
+
|
|
|
+ return outCol;
|
|
|
+ }
|
|
|
+ ENDCG
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 关键点一览
|
|
|
+
|
|
|
+- 渲染设置:
|
|
|
+
|
|
|
+- 透明队列、Alpha 混合、无深度写入、UI 专用 ZTest 与裁剪、可配合 UI Stencil。
|
|
|
+
|
|
|
+- 参考:
|
|
|
+
|
|
|
+``` shell
|
|
|
+ Cull Off
|
|
|
+ Lighting Off
|
|
|
+ ZWrite Off
|
|
|
+ ZTest [unity_GUIZTestMode]
|
|
|
+ Blend SrcAlpha OneMinusSrcAlpha
|
|
|
+ ColorMask [_ColorMask]
|
|
|
+```
|
|
|
+
|
|
|
+- 可调属性:
|
|
|
+
|
|
|
+- _Color: 遮罩颜色与不透明度(外部区域颜色取它,默认黑色 0.7 透明度)
|
|
|
+
|
|
|
+- _Center: 洞中心(与 UI 顶点同一坐标空间,通常是局部空间)
|
|
|
+
|
|
|
+- _Radius: 圆形半径
|
|
|
+
|
|
|
+- _HalfSize: 矩形/椭圆的一半尺寸 (x, y)
|
|
|
+
|
|
|
+- _Shape: 0 圆形、1 矩形、2 椭圆
|
|
|
+
|
|
|
+- _Softness: 边缘软化范围(越大过渡越柔)
|
|
|
+
|
|
|
+- 洞形状与软边原理:
|
|
|
+
|
|
|
+- 使用 SDF(有符号距离场)计算“点到形状边界”的距离:
|
|
|
+
|
|
|
+- 圆形:length(p) - _Radius
|
|
|
+
|
|
|
+- 矩形:rectSDF(p, _HalfSize)
|
|
|
+
|
|
|
+- 椭圆:归一化后 (norm - 1) * max(a.x, a.y)
|
|
|
+
|
|
|
+- 基于距离用 smoothstep 生成透明度过渡,使洞边缘柔和。
|
|
|
+
|
|
|
+- 参考:
|
|
|
+
|
|
|
+``` shell
|
|
|
+ float2 p = IN.worldPosition.xy - _Center.xy;
|
|
|
+
|
|
|
+ float distCircle = length(p) - _Radius;
|
|
|
+ float distRect = rectSDF(p, _HalfSize);
|
|
|
+ float dist = distCircle;
|
|
|
+ if (_Shape > 0.5) dist = distRect;
|
|
|
+ if (_Shape > 1.5)
|
|
|
+ {
|
|
|
+ float2 a = max(_HalfSize, float2(1e-5, 1e-5));
|
|
|
+ float norm = sqrt((p.x*p.x)/(a.x*a.x) + (p.y*p.y)/(a.y*a.y));
|
|
|
+ float distEllipse = (norm - 1.0) * max(a.x, a.y);
|
|
|
+ dist = distEllipse;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Smooth step from opaque (outside) to transparent (inside)
|
|
|
+ float edge = _Softness * 0.5;
|
|
|
+ float alphaMask = saturate(smoothstep(0.0, edge, dist));
|
|
|
+
|
|
|
+ fixed4 outCol = baseCol;
|
|
|
+ outCol.rgb = _Color.rgb;
|
|
|
+ outCol.a = _Color.a * alphaMask;
|
|
|
+```
|
|
|
+
|
|
|
+- 与 Unity UI 集成:
|
|
|
+
|
|
|
+- 支持 RectMask2D/Mask 的裁剪(UNITY_UI_CLIP_RECT)
|
|
|
+
|
|
|
+- 可启用 UI AlphaClip(UNITY_UI_ALPHACLIP)增强边缘裁剪
|
|
|
+
|
|
|
+- 支持 Stencil(可与其他 UI 蒙版组合)
|
|
|
+
|
|
|
+- 参考:
|
|
|
+
|
|
|
+``` shell
|
|
|
+ #pragma multi_compile __ UNITY_UI_CLIP_RECT
|
|
|
+ #pragma multi_compile __ UNITY_UI_ALPHACLIP
|
|
|
+```
|
|
|
+
|
|
|
+## 使用方式建议
|
|
|
+
|
|
|
+- 在 Canvas 下放一个全屏 Image,材质使用该 Shader。
|
|
|
+
|
|
|
+- 设置:
|
|
|
+
|
|
|
+- _Color 调整外部变暗程度与颜色(如黑 70% 透明)
|
|
|
+
|
|
|
+- _Center、_Radius 或 _HalfSize、_Shape 控制洞形状和位置
|
|
|
+
|
|
|
+- _Softness 控制边缘柔和
|
|
|
+
|
|
|
+- 若用 Screen Space - Overlay 的 Canvas,_Center 与尺寸通常是该 Image 的局部坐标;移动 Image 或改 RectTransform 会影响坐标基准。
|
|
|
+
|
|
|
+## 细节与注意
|
|
|
+
|
|
|
+- 最终输出颜色取 _Color.rgb,不是底图颜色;底图仅用于 UI 剪裁与 alpha 处理。这意味着该遮罩是“纯色暗化”,不是基于原图着色。
|
|
|
+
|
|
|
+- 椭圆计算避免除零:对极小半轴使用 1e-5 安全下限。
|
|
|
+
|
|
|
+- 边缘软化过大可能显得过于虚,推荐从 4–12 试调。
|
|
|
+
|
|
|
+## 一句话总结
|
|
|
+
|
|
|
+- 这是一个用于引导高亮的 UI 遮罩 Shader:给全屏加暗色层,在指定区域挖出圆/矩形/椭圆的透明洞,并提供可控的软边过渡,完美适配 Unity UI 的裁剪和 Stencil 流程。
|