diff --git a/Assets/Materials/MemoryForFade.mat b/Assets/Materials/MemoryForFade.mat index 0659b3043..fefccf1e9 100644 --- a/Assets/Materials/MemoryForFade.mat +++ b/Assets/Materials/MemoryForFade.mat @@ -8,7 +8,7 @@ Material: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: MemoryForFade - m_Shader: {fileID: 4800000, guid: 0803c241834332942abfcbd27c498b46, type: 3} + m_Shader: {fileID: 4800000, guid: 9f7b2ef66c2842c496327822f79da521, type: 3} m_Parent: {fileID: 0} m_ModifiedSerializedProperties: 0 m_ValidKeywords: diff --git a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs index b4653824a..e84970960 100644 --- a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs +++ b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs @@ -17,6 +17,23 @@ namespace AibisDream.FixSystem private const float DefaultFadeDuration = 1f; private const float SearchStartDelay = 1f; private const float ProjectorMoveDuration = 2f; + private const float MaxClarityDistortion = 0.58f; + private const float MaxFrequencyBlur = 0.6f; + private const float MinPixelDensity = 18f; + private const float MaxPixelDensity = 96f; + + private static readonly int EnablePixelateId = Shader.PropertyToID("_EnablePixelate"); + private static readonly int FrequencyTuningId = Shader.PropertyToID("_FrequencyTuning"); + private static readonly int ClarityTuningId = Shader.PropertyToID("_ClarityTuning"); + private static readonly int PixelateFadeId = Shader.PropertyToID("_PixelateFade"); + private static readonly int PixelatePixelDensityId = Shader.PropertyToID("_PixelatePixelDensity"); + private static readonly int EnableGaussianBlurId = Shader.PropertyToID("_EnableGaussianBlur"); + private static readonly int GaussianBlurFadeId = Shader.PropertyToID("_GaussianBlurFade"); + private static readonly int GaussianBlurOffsetId = Shader.PropertyToID("_GaussianBlurOffset"); + private static readonly int EnableUvDistortId = Shader.PropertyToID("_EnableUVDistort"); + private static readonly int UvDistortFadeId = Shader.PropertyToID("_UVDistortFade"); + private static readonly int EnableSaturationId = Shader.PropertyToID("_EnableSaturation"); + private static readonly int SaturationId = Shader.PropertyToID("_Saturation"); [SerializeField] private BubbleSlotGroup bubbleSlotGroup; public BubbleSlotGroup BubbleSlotGroup => bubbleSlotGroup; @@ -31,6 +48,7 @@ namespace AibisDream.FixSystem [SerializeField] private Volume memoryProcessVolume; [SerializeField] private MemorySlider frequencySlider; // 频率调节滑动条 [SerializeField] private MemorySlider claritySlider; // 清晰度调节滑动条 + [SerializeField] private bool useSpriteShaderTuning = true; [SerializeField] private CanvasGroup playBackground; @@ -45,6 +63,7 @@ namespace AibisDream.FixSystem [SerializeField] private MemoryPunchTapeSlot punchTapeSlot; [SerializeField] private Material fadeMaterial; + private Material runtimeMemoryMaterial; public void OpenMemoryView() { @@ -147,11 +166,18 @@ namespace AibisDream.FixSystem private void Start() { + PrepareRuntimeMemoryMaterial(); frequencySlider.OnFound += CheckAllFound; claritySlider.OnFound += CheckAllFound; Init(); } + private void OnDestroy() + { + if (runtimeMemoryMaterial != null) + Destroy(runtimeMemoryMaterial); + } + private void CheckAllFound() { if (frequencySlider.IsFound && claritySlider.IsFound) @@ -206,9 +232,19 @@ namespace AibisDream.FixSystem } if (frequencySlider != null) + { + frequencySlider.OnTuningChanged -= ApplyFrequencyTuning; frequencySlider.Init(memoryFrequencyVolume); + frequencySlider.SetVolumeOutputEnabled(!useSpriteShaderTuning); + frequencySlider.OnTuningChanged += ApplyFrequencyTuning; + } if (claritySlider != null) + { + claritySlider.OnTuningChanged -= ApplyClarityTuning; claritySlider.Init(memoryClarityVolume); + claritySlider.SetVolumeOutputEnabled(!useSpriteShaderTuning); + claritySlider.OnTuningChanged += ApplyClarityTuning; + } ResetPresentationState(); } @@ -239,6 +275,7 @@ namespace AibisDream.FixSystem { if (fadeMaterial != null) fadeMaterial.SetFloat("_DirectionalDistortionFade", FadeMaterialStartValue); + ResetSpriteTuning(); memoryFinishedSpriteRenderer.color = Color.black; memoryProcessVolume.weight = 0; _activePunchTape = null; @@ -377,10 +414,76 @@ namespace AibisDream.FixSystem claritySlider.SetInteractable(true); } + private void ApplyFrequencyTuning(float strength) + { + if (!useSpriteShaderTuning || fadeMaterial == null) + return; + + bool active = strength > 0.001f; + SetFloatIfExists(FrequencyTuningId, strength); + SetFloatIfExists(EnablePixelateId, active ? 1f : 0f); + SetFloatIfExists(PixelateFadeId, strength); + SetFloatIfExists(PixelatePixelDensityId, Mathf.Lerp(MaxPixelDensity, MinPixelDensity, strength)); + SetFloatIfExists(EnableGaussianBlurId, active ? 1f : 0f); + SetFloatIfExists(GaussianBlurFadeId, strength * MaxFrequencyBlur); + SetFloatIfExists(GaussianBlurOffsetId, Mathf.Lerp(0.05f, 0.35f, strength)); + } + + private void ApplyClarityTuning(float strength) + { + if (!useSpriteShaderTuning || fadeMaterial == null) + return; + + bool active = strength > 0.001f; + SetFloatIfExists(ClarityTuningId, strength); + SetFloatIfExists(EnableUvDistortId, active ? 1f : 0f); + SetFloatIfExists(UvDistortFadeId, strength * MaxClarityDistortion); + SetFloatIfExists(EnableSaturationId, active ? 1f : 0f); + SetFloatIfExists(SaturationId, Mathf.Lerp(1f, 0.25f, strength)); + } + + private void ResetSpriteTuning() + { + if (!useSpriteShaderTuning || fadeMaterial == null) + return; + + ApplyFrequencyTuning(0f); + ApplyClarityTuning(0f); + } + + private void SetFloatIfExists(int propertyId, float value) + { + if (fadeMaterial.HasProperty(propertyId)) + fadeMaterial.SetFloat(propertyId, value); + } + + private void PrepareRuntimeMemoryMaterial() + { + if (!useSpriteShaderTuning || memoryFinishedSpriteRenderer == null || fadeMaterial == null) + return; + + var shader = fadeMaterial.shader; + if (shader == null || shader.name != "AibisDream/MemoryTuningSprite") + shader = Shader.Find("AibisDream/MemoryTuningSprite"); + if (shader == null) + { + Debug.LogWarning("[MemoryProcess] 未找到 AibisDream/MemoryTuningSprite,调频将回退到当前材质表现。", this); + return; + } + + runtimeMemoryMaterial = new Material(fadeMaterial) + { + shader = shader, + name = $"{fadeMaterial.name}_RuntimeMemoryTuning" + }; + memoryFinishedSpriteRenderer.sharedMaterial = runtimeMemoryMaterial; + fadeMaterial = runtimeMemoryMaterial; + } + public bool IsProcessing() { return isProcessing; } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/FixSystem/Memory/MemorySlider.cs b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs index 961892632..039c4872f 100644 --- a/Assets/Scripts/FixSystem/Memory/MemorySlider.cs +++ b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs @@ -19,6 +19,7 @@ namespace AibisDream.FixSystem private Slider slider; private PlayableDirector playableDirector; private Volume _volume; + private bool driveVolume = true; /// 视觉 Lock/Unlock(Timeline);true = 收起未开,false = 已打开。 private bool isLocked; @@ -28,6 +29,7 @@ namespace AibisDream.FixSystem public event Action OnFound; public event Action OnLost; + public event Action OnTuningChanged; public void Init(Volume volume) { @@ -35,6 +37,7 @@ namespace AibisDream.FixSystem playableDirector = GetComponent(); _volume = volume; + driveVolume = true; IsFound = false; slider.onValueChanged.RemoveAllListeners(); slider.onValueChanged.AddListener(HandleValueChanged); @@ -49,6 +52,13 @@ namespace AibisDream.FixSystem /// 仅控制是否可拖动,与 / 无关。 public void SetInteractable(bool interactable) => slider.interactable = interactable; + public void SetVolumeOutputEnabled(bool enabled) + { + driveVolume = enabled; + if (!driveVolume && _volume != null) + _volume.weight = 0f; + } + /// /// 新一轮搜索前复位滑条与 IsFound(不播 Timeline、不改变视觉锁状态)。 /// 需先设置好 。 @@ -116,9 +126,11 @@ namespace AibisDream.FixSystem ); float t = Mathf.Clamp01(distance / maxDistance); - if (_volume != null) + if (_volume != null && driveVolume) _volume.weight = Mathf.Lerp(0, 1, t); + OnTuningChanged?.Invoke(t); + return t; } diff --git a/Assets/Shader/Shader/MemoryTuningSprite.shader b/Assets/Shader/Shader/MemoryTuningSprite.shader new file mode 100644 index 000000000..f56aa1ece --- /dev/null +++ b/Assets/Shader/Shader/MemoryTuningSprite.shader @@ -0,0 +1,171 @@ +Shader "AibisDream/MemoryTuningSprite" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + _Color ("Tint", Color) = (1,1,1,1) + _RendererColor ("Renderer Color", Color) = (1,1,1,1) + _ClarityTuning ("Clarity Tuning", Range(0,1)) = 0 + _FrequencyTuning ("Frequency Tuning", Range(0,1)) = 0 + _DirectionalDistortionFade ("Directional Distortion Fade", Range(-15,1)) = -15 + _PixelMin ("Pixel Min", Range(8,64)) = 18 + _PixelMax ("Pixel Max", Range(32,256)) = 128 + _BlurStep ("Blur Step", Range(0,0.01)) = 0.0025 + _DistortStrength ("Distort Strength", Range(0,0.05)) = 0.018 + _ChromaticStrength ("Chromatic Strength", Range(0,0.03)) = 0.008 + _FadeDistortStrength ("Fade Distort Strength", Range(0,0.2)) = 0.08 + } + + SubShader + { + Tags + { + "Queue" = "Transparent" + "RenderType" = "Transparent" + "RenderPipeline" = "UniversalPipeline" + "IgnoreProjector" = "True" + "CanUseSpriteAtlas" = "True" + } + + Cull Off + Lighting Off + ZWrite Off + Blend SrcAlpha OneMinusSrcAlpha + + Pass + { + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_instancing + + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" + + struct Attributes + { + float4 positionOS : POSITION; + float4 color : COLOR; + float2 uv : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct Varyings + { + float4 positionHCS : SV_POSITION; + half4 color : COLOR; + float2 uv : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO + }; + + TEXTURE2D(_MainTex); + SAMPLER(sampler_MainTex); + float4 _MainTex_TexelSize; + + CBUFFER_START(UnityPerMaterial) + half4 _Color; + half4 _RendererColor; + half _ClarityTuning; + half _FrequencyTuning; + half _DirectionalDistortionFade; + half _PixelMin; + half _PixelMax; + half _BlurStep; + half _DistortStrength; + half _ChromaticStrength; + half _FadeDistortStrength; + CBUFFER_END + + Varyings vert(Attributes input) + { + Varyings output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + + output.positionHCS = TransformObjectToHClip(input.positionOS.xyz); + output.uv = input.uv; + output.color = input.color * _Color * _RendererColor; + return output; + } + + half4 SampleSprite(float2 uv) + { + return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, saturate(uv)); + } + + float2 PixelSnap(float2 uv, half strength) + { + half pixels = lerp(_PixelMax, _PixelMin, strength); + float2 aspectPixels = float2(pixels, max(1.0, pixels * _MainTex_TexelSize.z / _MainTex_TexelSize.w)); + return (floor(uv * aspectPixels) + 0.5) / aspectPixels; + } + + float2 DistortUv(float2 uv, half strength) + { + float angle = radians(40.0); + float2 dir = float2(cos(angle), sin(angle)); + float2 side = float2(-sin(angle), cos(angle)); + float wave = sin(dot(uv - 0.5, dir) * 85.0 + _Time.y * 1.35); + return uv + side * wave * _DistortStrength * strength; + } + + half4 BlurSample(float2 uv, half strength) + { + float radial = distance(uv, 0.5) * 0.45; + float stepSize = _BlurStep * strength * (1.0 + radial); + float2 stepX = float2(stepSize, 0); + float2 stepY = float2(0, stepSize); + + half4 col = SampleSprite(uv) * 0.36; + col += SampleSprite(uv + stepX) * 0.12; + col += SampleSprite(uv - stepX) * 0.12; + col += SampleSprite(uv + stepY) * 0.12; + col += SampleSprite(uv - stepY) * 0.12; + col += SampleSprite(uv + stepX + stepY) * 0.05; + col += SampleSprite(uv - stepX - stepY) * 0.05; + col += SampleSprite(uv + stepX - stepY) * 0.03; + col += SampleSprite(uv - stepX + stepY) * 0.03; + return col; + } + + half3 ApplySaturation(half3 color, half saturation) + { + half luma = dot(color, half3(0.299, 0.587, 0.114)); + return lerp(half3(luma, luma, luma), color, saturation); + } + + half4 frag(Varyings input) : SV_Target + { + half clarity = saturate(_ClarityTuning); + half frequency = saturate(_FrequencyTuning); + + float2 uv = input.uv; + half fade = saturate((_DirectionalDistortionFade + 15.0) / 15.0); + if (fade > 0.001) + { + float fadeWave = sin((uv.y + uv.x * 0.25 + _Time.y * 0.4) * 38.0); + uv.x += fadeWave * _FadeDistortStrength * fade; + } + + uv = DistortUv(uv, clarity); + float2 pixelUv = PixelSnap(uv, frequency); + uv = lerp(uv, pixelUv, frequency); + + half4 baseCol = lerp(SampleSprite(uv), BlurSample(uv, frequency), frequency * 0.65); + + half chroma = _ChromaticStrength * clarity; + float radial = pow(distance(uv, 0.5), 3.0) * 3.0; + float2 chromaOffset = float2(chroma + radial * chroma, chroma * 0.35); + half4 r = SampleSprite(uv + chromaOffset); + half4 g = baseCol; + half4 b = SampleSprite(uv - chromaOffset.yx); + half4 chromaCol = half4(r.r, g.g, b.b, max(max(r.a, g.a), b.a)); + + half4 col = lerp(baseCol, chromaCol, clarity); + col.rgb = ApplySaturation(col.rgb, lerp(1.0, 0.28, clarity)); + col *= input.color; + return col; + } + ENDHLSL + } + } +} diff --git a/Assets/Shader/Shader/MemoryTuningSprite.shader.meta b/Assets/Shader/Shader/MemoryTuningSprite.shader.meta new file mode 100644 index 000000000..fe4948d7d --- /dev/null +++ b/Assets/Shader/Shader/MemoryTuningSprite.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 9f7b2ef66c2842c496327822f79da521 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shader/Shader/NewMemory.mat b/Assets/Shader/Shader/NewMemory.mat new file mode 100644 index 000000000..d9c8fdaf2 --- /dev/null +++ b/Assets/Shader/Shader/NewMemory.mat @@ -0,0 +1,49 @@ +%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: NewMemory + m_Shader: {fileID: 4800000, guid: e260cfa7296ee7642b167f1eb5be5023, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _AlphaTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MaskTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _NormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _EnableExternalAlpha: 0 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _Flip: {r: 1, g: 1, b: 1, a: 1} + - _RendererColor: {r: 1, g: 1, b: 1, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Shader/Shader/NewMemory.mat.meta b/Assets/Shader/Shader/NewMemory.mat.meta new file mode 100644 index 000000000..13cd4bb7e --- /dev/null +++ b/Assets/Shader/Shader/NewMemory.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7f4fa9a087a439b4d9c1939e6812b7e5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: