From 5d58cb3743542816b529b8aacd4e0a62a78d29c5 Mon Sep 17 00:00:00 2001 From: tianmo Date: Sat, 10 Oct 2026 20:12:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9D=90=E8=B4=A8=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scenes/Start.unity | 11 +- Assets/Shaders.meta | 8 + Assets/Shaders/Editor.meta | 8 + Assets/Shaders/Editor/UIMaskDrawers.cs | 101 +++++++ Assets/Shaders/Editor/UIMaskDrawers.cs.meta | 11 + Assets/Shaders/Editor/UIMaskShaderGUI.cs | 195 +++++++++++++ Assets/Shaders/Editor/UIMaskShaderGUI.cs.meta | 11 + Assets/Shaders/VFX.mat | 186 ++++++++++++ Assets/Shaders/VFX.mat.meta | 8 + Assets/Shaders/VFX.shader | 264 ++++++++++++++++++ Assets/Shaders/VFX.shader.meta | 9 + Assets/Textures/texture1.png.meta | 10 +- 12 files changed, 814 insertions(+), 8 deletions(-) create mode 100644 Assets/Shaders.meta create mode 100644 Assets/Shaders/Editor.meta create mode 100644 Assets/Shaders/Editor/UIMaskDrawers.cs create mode 100644 Assets/Shaders/Editor/UIMaskDrawers.cs.meta create mode 100644 Assets/Shaders/Editor/UIMaskShaderGUI.cs create mode 100644 Assets/Shaders/Editor/UIMaskShaderGUI.cs.meta create mode 100644 Assets/Shaders/VFX.mat create mode 100644 Assets/Shaders/VFX.mat.meta create mode 100644 Assets/Shaders/VFX.shader create mode 100644 Assets/Shaders/VFX.shader.meta diff --git a/Assets/Scenes/Start.unity b/Assets/Scenes/Start.unity index cc52692..786d6f6 100644 --- a/Assets/Scenes/Start.unity +++ b/Assets/Scenes/Start.unity @@ -354,7 +354,7 @@ Transform: m_GameObject: {fileID: 649210418} serializedVersion: 2 m_LocalRotation: {x: 0.5, y: 0, z: 0, w: 0.8660254} - m_LocalPosition: {x: 0, y: 26.980762, z: -15} + m_LocalPosition: {x: 0, y: 28.380762, z: -15} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: @@ -638,7 +638,7 @@ Transform: m_GameObject: {fileID: 961739749} serializedVersion: 2 m_LocalRotation: {x: 0.5, y: 0, z: 0, w: 0.8660254} - m_LocalPosition: {x: 0, y: 26.980762, z: -15} + m_LocalPosition: {x: 0, y: 28.380762, z: -15} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -1265,6 +1265,11 @@ PrefabInstance: propertyPath: m_Name value: PlayerRoot objectReference: {fileID: 0} + - target: {fileID: 6992472044908293245, guid: 905259bd26d574ba586c38b5e91d0b7f, + type: 3} + propertyPath: m_Materials.Array.data[0] + value: + objectReference: {fileID: 2100000, guid: fef3f43e5c75945209ac3bd77f1cad29, type: 2} - target: {fileID: 9034613337173532632, guid: 905259bd26d574ba586c38b5e91d0b7f, type: 3} propertyPath: m_LocalPosition.x @@ -1273,7 +1278,7 @@ PrefabInstance: - target: {fileID: 9034613337173532632, guid: 905259bd26d574ba586c38b5e91d0b7f, type: 3} propertyPath: m_LocalPosition.y - value: 0 + value: 1.4 objectReference: {fileID: 0} - target: {fileID: 9034613337173532632, guid: 905259bd26d574ba586c38b5e91d0b7f, type: 3} diff --git a/Assets/Shaders.meta b/Assets/Shaders.meta new file mode 100644 index 0000000..7b64121 --- /dev/null +++ b/Assets/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 82f4082f90a6047c5b477df20021b978 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/Editor.meta b/Assets/Shaders/Editor.meta new file mode 100644 index 0000000..57963ff --- /dev/null +++ b/Assets/Shaders/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8aeae0a7d94384a8b979cec567fe9d8b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/Editor/UIMaskDrawers.cs b/Assets/Shaders/Editor/UIMaskDrawers.cs new file mode 100644 index 0000000..b7805ae --- /dev/null +++ b/Assets/Shaders/Editor/UIMaskDrawers.cs @@ -0,0 +1,101 @@ +using UnityEditor; +using UnityEngine; + +// UIMask 材质面板的"下拉框 -> 隐藏属性"绘制器基类。 +// ShaderLab 的 Blend / Cull 只接受数字属性,没法用枚举直接切换, +// 所以由绘制器把下拉框的选项写进隐藏属性。 +// 每次绘制面板都会同步一次,所以从其它 shader(例如 URP/Lit 也有同名的 +// _SrcBlend / _DstBlend / _Cull)切过来的材质会被自动纠正。 +// 注意:attribute 的名字必须是子类的简单类名,Unity 按类名查找绘制器。 +public abstract class UIMaskEnumDrawer : MaterialPropertyDrawer +{ + protected abstract string[] Options { get; } + + // 把下拉框当前选项同步到材质上的隐藏属性 + protected abstract void Apply(MaterialEditor editor, int index); + + public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor) + { + EditorGUI.BeginChangeCheck(); + int index = Mathf.Clamp(Mathf.RoundToInt(prop.floatValue), 0, Options.Length - 1); + index = EditorGUI.Popup(position, label, index, Options); + if (EditorGUI.EndChangeCheck()) + { + prop.floatValue = index; + } + + Apply(editor, index); + } + + public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor) + { + return EditorGUIUtility.singleLineHeight; + } + + protected void SetValue(MaterialEditor editor, string propertyName, float value) + { + foreach (Object target in editor.targets) + { + var material = target as Material; + if (material == null || !material.HasProperty(propertyName)) + { + continue; + } + + if (!Mathf.Approximately(material.GetFloat(propertyName), value)) + { + material.SetFloat(propertyName, value); + } + } + } +} + +// 混合模式:加色 / 普通混合 +// 数值对应 UnityEngine.Rendering.BlendMode:SrcAlpha=5, One=1, OneMinusSrcAlpha=10 +public class UIMaskBlendMode : UIMaskEnumDrawer +{ + private static readonly string[] Items = { "Additive", "AlphaBlend" }; + + protected override string[] Options => Items; + + protected override void Apply(MaterialEditor editor, int index) + { + SetValue(editor, "_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha); + SetValue(editor, "_DstBlend", index == 0 + ? (float)UnityEngine.Rendering.BlendMode.One + : (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + } +} + +// 渲染面:只渲染前面 / 只渲染后面 / 双面 +// ShaderLab 的 Cull 取值:Off=0(双面)、Front=1(剔除正面,即只渲染后面)、Back=2(剔除背面,即只渲染前面) +public class UIMaskRenderFace : UIMaskEnumDrawer +{ + private const float CullOff = 0f; + private const float CullFront = 1f; + private const float CullBack = 2f; + + private static readonly string[] Items = { "前面", "背面", "双面" }; + + protected override string[] Options => Items; + + protected override void Apply(MaterialEditor editor, int index) + { + float cull = index == 0 ? CullBack : (index == 1 ? CullFront : CullOff); + SetValue(editor, "_Cull", cull); + } +} + +// 贴图通道:R / G / B / A(值直接被 shader 采样时使用,不需要写隐藏属性) +// 默认 A(index = 3) +public class UIMaskChannel : UIMaskEnumDrawer +{ + private static readonly string[] Items = { "R 通道", "G 通道", "B 通道", "A 通道" }; + + protected override string[] Options => Items; + + protected override void Apply(MaterialEditor editor, int index) + { + // 下拉框的值本身就是要采样的通道号,无需同步到别的属性 + } +} diff --git a/Assets/Shaders/Editor/UIMaskDrawers.cs.meta b/Assets/Shaders/Editor/UIMaskDrawers.cs.meta new file mode 100644 index 0000000..b939fd9 --- /dev/null +++ b/Assets/Shaders/Editor/UIMaskDrawers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de421d7ff241b44acba639824a11e4b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/Editor/UIMaskShaderGUI.cs b/Assets/Shaders/Editor/UIMaskShaderGUI.cs new file mode 100644 index 0000000..60e14a3 --- /dev/null +++ b/Assets/Shaders/Editor/UIMaskShaderGUI.cs @@ -0,0 +1,195 @@ +using UnityEditor; +using UnityEngine; + +// UIMask 的材质面板:分组折叠 + 中文标签。 +// shader 里用 CustomEditor "UIMaskShaderGUI" 挂上这个类。 +// 折叠状态存在 EditorPrefs(按组名),重编译/重启 Unity 后仍然保持。 +// 未在 Groups 里列出的属性(_SrcBlend/_DstBlend/_Cull、硬边溶解、Stencil 等)不绘制。 +public class UIMaskShaderGUI : ShaderGUI +{ + private const string FoldKeyPrefix = "UIMask.GUI."; + + private class Group + { + public string Label; + public string Toggle; // 组的启用开关(没有则为 null) + public string[] Props; // 组内属性,按显示顺序 + } + + private static readonly Group[] Groups = + { + new Group + { + Label = "基础", + Props = new[] { "_BlendMode", "_RenderFace", "_TintColor", "_GlowScale|_AlphaScale", "_MainTex" } + }, + new Group + { + Label = "UV 滚动", + Toggle = "_UseUvAni", + Props = new[] { "_SpeedU|_SpeedV" } + }, + new Group + { + Label = "UV 扰乱", + Toggle = "_UseUvDistortion", + Props = new[] { "_UVNoiseTex", "_Distortion", "_NoiseScroll" } + }, + new Group + { + Label = "遮罩 Mask", + Toggle = "_UseMask", + Props = new[] { "_UseMaskUvAni", "_Mask", "_MaskChannel", "_MaskSpeedU|_MaskSpeedV" } + }, + new Group + { + Label = "溶解", + Toggle = "_UseDissolve", + Props = new[] { "_DissolveTex", "_DissolveChannel", "_Dissolve", "_DissolveWidth" } + }, + new Group + { + Label = "UI 裁切", + Toggle = "_UseUIMaskClip", + Props = new[] { "_ClipRect" } + }, + new Group + { + Label = "顶点遮罩", + Toggle = "_UseVertexMask", + Props = new[] { "_VertexOffset" } + }, + }; + + public override void OnGUI(MaterialEditor editor, MaterialProperty[] props) + { + foreach (Group group in Groups) + { + DrawGroup(editor, props, group); + } + } + + private static void DrawGroup(MaterialEditor editor, MaterialProperty[] props, Group group) + { + MaterialProperty toggle = string.IsNullOrEmpty(group.Toggle) + ? null + : FindProperty(group.Toggle, props, false); + + string foldKey = FoldKeyPrefix + group.Label; + // 默认只展开"基础",其余收起(收起时右边的勾选状态仍能看出该功能有没有开) + bool expanded = EditorPrefs.GetBool(foldKey, group.Label == "基础"); + + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + EditorGUILayout.BeginHorizontal(); + bool newExpanded = EditorGUILayout.Foldout(expanded, group.Label, true, EditorStyles.foldoutHeader); + if (toggle != null) + { + GUILayout.FlexibleSpace(); + editor.ShaderProperty(toggle, GUIContent.none, 0); + } + EditorGUILayout.EndHorizontal(); + + if (newExpanded != expanded) + { + EditorPrefs.SetBool(foldKey, newExpanded); + } + + if (newExpanded) + { + EditorGUI.indentLevel++; + DrawProps(editor, props, group.Props); + EditorGUI.indentLevel--; + } + + EditorGUILayout.EndVertical(); + } + + // group.Props 里一项 = 一行;写 "A|B" 表示这两个单值属性并排在同一行 + private static void DrawProps(MaterialEditor editor, MaterialProperty[] props, string[] rows) + { + foreach (string row in rows) + { + int separator = row.IndexOf('|'); + if (separator < 0) + { + DrawOne(editor, props, row); + continue; + } + + MaterialProperty left = FindProperty(row.Substring(0, separator), props, false); + MaterialProperty right = FindProperty(row.Substring(separator + 1), props, false); + if (left != null && right != null && IsSingleValue(left) && IsSingleValue(right)) + { + DrawTwoPerRow(editor, left, right); + } + else + { + // 其中一边不存在或不是单值属性,退回各自一行,避免出错 + if (left != null) DrawOne(editor, props, left.name); + if (right != null) DrawOne(editor, props, right.name); + } + } + } + + private static void DrawOne(MaterialEditor editor, MaterialProperty[] props, string name) + { + MaterialProperty prop = FindProperty(name, props, false); + if (prop == null) + { + return; + } + + if (prop.type == MaterialProperty.PropType.Texture) + { + // Unity 原生贴图选择器(缩略图 + 平铺/偏移都由它画) + bool scaleOffset = (prop.flags & MaterialProperty.PropFlags.NoScaleOffset) == 0; + editor.TextureProperty(prop, prop.displayName, scaleOffset); + return; + } + + if (prop.type == MaterialProperty.PropType.Vector) + { + DrawVectorTwoLines(prop); + return; + } + + editor.ShaderProperty(prop, prop.displayName); + } + + private static bool IsSingleValue(MaterialProperty prop) + { + return prop.type == MaterialProperty.PropType.Float + || prop.type == MaterialProperty.PropType.Int; + } + + // 两个单值属性并排一行;半行放不下默认标签宽度,所以临时收窄标签 + private static void DrawTwoPerRow(MaterialEditor editor, MaterialProperty left, MaterialProperty right) + { + Rect row = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); + const float gap = 6f; + float half = (row.width - gap) * 0.5f; + + float oldLabelWidth = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = Mathf.Min(oldLabelWidth, half * 0.5f); + + editor.ShaderProperty(new Rect(row.x, row.y, half, row.height), left, new GUIContent(left.displayName)); + editor.ShaderProperty(new Rect(row.x + half + gap, row.y, half, row.height), right, new GUIContent(right.displayName)); + + EditorGUIUtility.labelWidth = oldLabelWidth; + } + + // 四分量向量:属性名独占一行,X/Y/Z/W 单独占下一行(默认是挤在同一行,名字会被切掉) + private static void DrawVectorTwoLines(MaterialProperty prop) + { + EditorGUILayout.LabelField(prop.displayName, EditorStyles.label); + + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = prop.hasMixedValue; + Vector4 value = EditorGUILayout.Vector4Field(GUIContent.none, prop.vectorValue); + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) + { + prop.vectorValue = value; + } + } +} diff --git a/Assets/Shaders/Editor/UIMaskShaderGUI.cs.meta b/Assets/Shaders/Editor/UIMaskShaderGUI.cs.meta new file mode 100644 index 0000000..e0196a9 --- /dev/null +++ b/Assets/Shaders/Editor/UIMaskShaderGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 621d15512511a4cbdaf5fc4e9f99dfa3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/VFX.mat b/Assets/Shaders/VFX.mat new file mode 100644 index 0000000..f1ab89d --- /dev/null +++ b/Assets/Shaders/VFX.mat @@ -0,0 +1,186 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: VFX + m_Shader: {fileID: 4800000, guid: 7ddbc50ff7d2d42939fd3e9816f2baf4, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: + - _USEUVTRAIL_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DissolveTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: 7ca288cbfc0684bbf876ce03dca16a9a, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _Mask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _RampTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _UVNoiseTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _AlphaScale: 1 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendMode: 1 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _ColorMask: 15 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _Dissolve: 0 + - _DissolveChannel: 3 + - _DissolveWidth: 0 + - _Distortion: 0 + - _DstBlend: 10 + - _DstBlendAlpha: 0 + - _EdgeGlow: 1 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _GlowScale: 1 + - _MaskChannel: 3 + - _MaskSpeedU: 0 + - _MaskSpeedV: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _RenderFace: 0 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SpeedU: 0 + - _SpeedV: 0 + - _SrcBlend: 5 + - _SrcBlendAlpha: 1 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _Surface: 0 + - _UseDissolve: 0 + - _UseHardEdge: 0 + - _UseMask: 0 + - _UseMaskUvAni: 0 + - _UseReceiveFog: 0 + - _UseUIMaskClip: 0 + - _UseUvAni: 0 + - _UseUvDistortion: 0 + - _UseUvTrail: 1 + - _UseVertexMask: 0 + - _VertexFlipX: 1 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 1, g: 1, b: 1, a: 1} + - _ClipRect: {r: -1, g: -1, b: 1, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EdgeColor: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _MainTexTiling: {r: 1, g: 1, b: 0, a: 0} + - _NoiseScroll: {r: 0, g: 0, b: 1, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + - _TintColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5} + - _VertexOffset: {r: 0, g: 0, b: 1, a: 1} + m_BuildTextureStacks: [] +--- !u!114 &4124507439711967337 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 7 diff --git a/Assets/Shaders/VFX.mat.meta b/Assets/Shaders/VFX.mat.meta new file mode 100644 index 0000000..0289c91 --- /dev/null +++ b/Assets/Shaders/VFX.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fef3f43e5c75945209ac3bd77f1cad29 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/VFX.shader b/Assets/Shaders/VFX.shader new file mode 100644 index 0000000..4fcc558 --- /dev/null +++ b/Assets/Shaders/VFX.shader @@ -0,0 +1,264 @@ +// UIMask:UI Mask 粒子/特效 shader —— 加色与普通混合共用一个 shader +// +// 混合模式在材质面板上是一个下拉框 "_BlendMode"(加色 Additive / 普通混合 AlphaBlend)。 +// 它由 Assets/Shaders/Editor/UIMaskBlendMode.cs([UIMaskBlendMode] 绘制器)映射到真正的 Blend 状态: +// 加色(Additive): _SrcBlend = 5 (SrcAlpha) _DstBlend = 1 (One) +// 普通混合(AlphaBlend):_SrcBlend = 5 (SrcAlpha) _DstBlend = 10 (OneMinusSrcAlpha) +// 渲染面同理:[UIMaskRenderFace] 下拉映射到隐藏属性 _Cull(双面=0,只渲染后面=1,只渲染前面=2)。 +// _SrcBlend / _DstBlend / _Cull 是隐藏属性,由下拉框自动写入,不要手动改。 +// Blend 是固定功能状态,不参与 shader 变体表,所以切换混合模式不产生额外变体。 +// 面向美术:程序/技术类参数一律用 [HideInInspector] 隐藏(混合模式内部值、Stencil 组、 +// 顶点 Mask 翻转),需要时由程序控制,材质面板只留艺术参数。 +Shader "VFX/VFX" { +Properties { + [UIMaskBlendMode] _BlendMode ("混合模式", Float) = 0 + [HideInInspector] _SrcBlend ("Src Blend", Float) = 5 + [HideInInspector] _DstBlend ("Dst Blend", Float) = 1 + [UIMaskRenderFace] _RenderFace ("渲染面", Float) = 2 + [HideInInspector] _Cull ("Cull", Float) = 0 + [HDR]_TintColor ("着色", Color) = (0.5,0.5,0.5,0.5) + _GlowScale("发光强度", float) = 1 + _AlphaScale("透明度", float) = 1 + _MainTex ("主贴图", 2D) = "white" {} + [Toggle] _UseUvAni("启用", Float) = 0 + _SpeedU("U 速度", Float) = 0 + _SpeedV("V 速度", Float) = 0 + [Toggle] _UseUvDistortion("启用", Float) = 0 + _UVNoiseTex("噪声贴图", 2D) = "black" {} + _Distortion ("扰动量", Float) = 0 + _NoiseScroll ("噪声滚动 (XY 速度 / ZW 强度)", Vector) = (0,0,1,1) + [Toggle] _UseMask("启用", Float) = 0 + [Toggle] _UseMaskUvAni("UV 滚动", Float) = 0 + _Mask("遮罩贴图", 2D) = "white" {} + [UIMaskChannel] _MaskChannel("通道", Float) = 3 + _MaskSpeedU("U 速度", Float) = 0 + _MaskSpeedV("V 速度", Float) = 0 + [Toggle] _UseDissolve("启用", Float) = 0 + _DissolveTex ("溶解贴图", 2D) = "white" {} + [UIMaskChannel] _DissolveChannel("通道", Float) = 3 + _Dissolve ("溶解进度", Range(0, 1.01)) = 0 + _DissolveWidth ("溶解宽度", Range(0, 1)) = 0 + [Toggle] _UseUIMaskClip("启用", Float) = 0 + _ClipRect("裁切矩形 (由 Mask / RectMask2D 控制)", Vector) = (-1, -1, 1, 1) + [Toggle] _UseVertexMask("启用", Float) = 0 + _VertexOffset ("方向与范围", Vector) = (0,0,1,1) + // 程序专用:顶点 Mask 的翻转,由程序设置 + [HideInInspector] _VertexFlipX ("程序专用", Float) = 1 + // required for UI.Mask —— 这几个由 Unity 的 Mask / RectMask2D 在运行时写入,不需要美术看到 + [HideInInspector] _StencilComp("Stencil Comparison", Float) = 8 + [HideInInspector] _Stencil("Stencil ID", Float) = 0 + [HideInInspector] _StencilOp("Stencil Operation", Float) = 0 + [HideInInspector] _StencilWriteMask("Stencil Write Mask", Float) = 255 + [HideInInspector] _StencilReadMask("Stencil Read Mask", Float) = 255 + [HideInInspector] _ColorMask("Color Mask", Float) = 15 +} + +CustomEditor "UIMaskShaderGUI" + +Category { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } + Blend [_SrcBlend][_DstBlend] + Cull [_Cull] Lighting Off ZWrite Off + + SubShader { + Stencil + { + Ref[_Stencil] + Comp[_StencilComp] + Pass[_StencilOp] + ReadMask[_StencilReadMask] + WriteMask[_StencilWriteMask] + } + ColorMask[_ColorMask] + + Pass { + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma target 2.0 + + // 全部使用 shader_feature(按材质用到的组合裁剪),不要改成 multi_compile + #pragma shader_feature _USEMASK_ON + #pragma shader_feature _USEMASKUVANI_ON + #pragma shader_feature _USEUVANI_ON + #pragma shader_feature _USEUVDISTORTION_ON + #pragma shader_feature _USEDISSOLVE_ON + #pragma shader_feature _USEUIMASKCLIP_ON + #pragma shader_feature _USEVERTEXMASK_ON + + // 不声明 multi_compile_fog:UNITY_FOG_* 宏在无雾关键字时展开为空, + // 原来的"_UseReceiveFog"开关勾了也不会有任何效果,故已移除。 + + #include "UnityCG.cginc" + #include "UnityUI.cginc" + + sampler2D _MainTex; + fixed4 _TintColor; + + #if _USEMASK_ON + sampler2D _Mask; + fixed4 _Mask_ST; + float _MaskChannel; + #if _USEMASKUVANI_ON + float _MaskSpeedU; + float _MaskSpeedV; + #endif + #endif + + #if _USEUVANI_ON + float _SpeedU; + float _SpeedV; + #endif + + #if _USEUVDISTORTION_ON + sampler2D _UVNoiseTex; + fixed4 _UVNoiseTex_ST; + fixed _Distortion; + fixed4 _NoiseScroll; + #endif + + #if _USEDISSOLVE_ON + sampler2D _DissolveTex; + fixed4 _DissolveTex_ST; + float _DissolveChannel; + fixed _Dissolve; + fixed _DissolveWidth; + #endif + + // 通道选择:0=R 1=G 2=B 3=A。用点乘取通道,避免动态分量索引(SM2.0 不支持) + inline float UIMaskSampleChannel(sampler2D tex, float2 uv, float channel) + { + float4 c = tex2D(tex, uv); + float4 w = 1.0 - saturate(abs(channel - float4(0.0, 1.0, 2.0, 3.0))); + return dot(c, w); + } + + #if _USEUIMASKCLIP_ON + fixed4 _ClipRect; + #endif + + #if _USEVERTEXMASK_ON + fixed4 _VertexOffset; + fixed _VertexFlipX; + #endif + + struct appdata_t { + float4 vertex : POSITION; + fixed4 color : COLOR; + fixed4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + fixed4 texcoord : TEXCOORD0; + + #if _USEMASK_ON + fixed2 texcoordMask : TEXCOORD2; + #endif + + #if _USEUVDISTORTION_ON + fixed2 texcoordNoise : TEXCOORD3; + #endif + + #if _USEDISSOLVE_ON + fixed2 texcoordDissolve: TEXCOORD4; + #endif + + #if _USEUIMASKCLIP_ON + fixed4 worldPosition : TEXCOORD5; + #endif + + #if _USEVERTEXMASK_ON + fixed4 vertex2 : TEXCOORD7; + #endif + + UNITY_VERTEX_OUTPUT_STEREO + }; + + fixed4 _MainTex_ST; + fixed _GlowScale; + fixed _AlphaScale; + + v2f vert (appdata_t v) + { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.vertex = UnityObjectToClipPos(v.vertex); + o.color = v.color * _TintColor; + o.texcoord.xy = TRANSFORM_TEX(v.texcoord,_MainTex); + o.texcoord.zw = v.texcoord.zw; + + #if _USEMASK_ON + o.texcoordMask = TRANSFORM_TEX(v.texcoord,_Mask); + #endif + + #if _USEUVDISTORTION_ON + o.texcoordNoise = TRANSFORM_TEX(v.texcoord,_UVNoiseTex); + #endif + + #if _USEDISSOLVE_ON + o.texcoordDissolve = TRANSFORM_TEX(v.texcoord,_DissolveTex); + #endif + + #if _USEUIMASKCLIP_ON + o.worldPosition = mul(unity_ObjectToWorld, v.vertex); + #endif + + #if _USEVERTEXMASK_ON + o.vertex2 = v.vertex; + #endif + + return o; + } + + fixed4 frag (v2f i) : SV_Target + { + #if _USEUVANI_ON + // 用 _Time.y(float)而不是 _Time.g(fixed):运行久了 fixed 精度不足会出现像素块/卡顿 + i.texcoord.xy += _Time.y * float2(_SpeedU, _SpeedV); + #endif + + #if _USEUVDISTORTION_ON + float2 noiseScrollXY = _NoiseScroll.xy; + i.texcoordNoise += fixed2(_Time.g * noiseScrollXY); + i.texcoord.xy += tex2D(_UVNoiseTex, i.texcoordNoise).xy*_Distortion*_NoiseScroll.zw; + #endif + + fixed4 col = 2.0f * i.color * tex2D(_MainTex, i.texcoord.xy); + col.rgb *= _GlowScale; + col.a = saturate(col.a) * _AlphaScale; + + #if _USEMASK_ON + #if _USEMASKUVANI_ON + i.texcoordMask += _Time.y * float2(_MaskSpeedU, _MaskSpeedV); + #endif + col.a = saturate(col.a * UIMaskSampleChannel(_Mask, i.texcoordMask, _MaskChannel)); + #endif + + #if _USEDISSOLVE_ON + fixed DissolveWithParticle =(_Dissolve*(1-i.texcoord.z)*(1+_DissolveWidth)-_DissolveWidth) ; + fixed dissolveMap = UIMaskSampleChannel(_DissolveTex, i.texcoordDissolve, _DissolveChannel); + fixed dissolveAlpha = saturate(smoothstep( DissolveWithParticle, (DissolveWithParticle+_DissolveWidth), dissolveMap)); + col.a *= dissolveAlpha; + #endif + + #if _USEUIMASKCLIP_ON + col.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect); + #endif + + #if _USEVERTEXMASK_ON + col.a *= smoothstep(1, 0, ((i.vertex2.x - _VertexOffset.x) * _VertexOffset.z + (i.vertex2.y - _VertexOffset.y) * _VertexOffset.w) * _VertexFlipX); + #endif + + return col; + } + + ENDCG + } + } +} +} diff --git a/Assets/Shaders/VFX.shader.meta b/Assets/Shaders/VFX.shader.meta new file mode 100644 index 0000000..13cdda3 --- /dev/null +++ b/Assets/Shaders/VFX.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7ddbc50ff7d2d42939fd3e9816f2baf4 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Textures/texture1.png.meta b/Assets/Textures/texture1.png.meta index c215e8a..aacd232 100644 --- a/Assets/Textures/texture1.png.meta +++ b/Assets/Textures/texture1.png.meta @@ -6,7 +6,7 @@ TextureImporter: serializedVersion: 13 mipmaps: mipMapMode: 0 - enableMipMap: 0 + enableMipMap: 1 sRGBTexture: 1 linearTexture: 0 fadeOut: 0 @@ -37,8 +37,8 @@ TextureImporter: filterMode: 1 aniso: 1 mipBias: 0 - wrapU: 1 - wrapV: 1 + wrapU: 0 + wrapV: 0 wrapW: 0 nPOTScale: 0 lightmap: 0 @@ -52,9 +52,9 @@ TextureImporter: spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 1 alphaUsage: 1 - alphaIsTransparency: 1 + alphaIsTransparency: 0 spriteTessellationDetail: -1 - textureType: 8 + textureType: 0 textureShape: 1 singleChannelComponent: 0 flipbookRows: 1