#ifndef HUOSHAN_MEMORY_INCLUDED #define HUOSHAN_MEMORY_INCLUDED #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; float4 color : COLOR; }; struct Varyings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; float4 color : COLOR; }; TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); CBUFFER_START(UnityPerMaterial) float4 _MainTex_ST; float4 _Color; float _Saturation; float _Brightness; float _Contrast; float _Vignette; float _Noise; float _SimGlitch; float _HorizontalTear; float _Impact; float _Damage; float _Overdrive; float _TearSeed; float4 _ColorTint; float4 _CalmCenter; float _CalmRadius; float _CalmFeather; float _EffectStrength; float _Opacity; float _AnalogGlitch; float _AnalogScale; float _AnalogChroma; CBUFFER_END float hash21(float2 p) { float3 p3 = frac(float3(p.xyx) * 0.1031); p3 += dot(p3, p3.yzx + 33.33); return frac((p3.x + p3.y) * p3.z); } Varyings vert(Attributes input) { Varyings output; output.positionHCS = TransformObjectToHClip(input.positionOS.xyz); output.uv = TRANSFORM_TEX(input.uv, _MainTex); output.color = input.color * _Color; return output; } half4 frag(Varyings input) : SV_Target { float2 centered = (input.uv - _CalmCenter.xy); float calmDistance = length(centered); float calmMask = 1.0 - smoothstep( _CalmRadius, _CalmRadius + max(0.001, _CalmFeather), calmDistance); calmMask *= step(0.001, _CalmRadius); float effect = saturate(_EffectStrength) * (1.0 - calmMask); // Small persistent simulation disturbance, independent of the impact path. float sim = saturate(_SimGlitch) * (1.0 - calmMask * 0.85); float impact = saturate(_Impact); float damage = saturate(_Damage); float overdrive = saturate(_Overdrive); float distortion = saturate(max(effect, damage * 0.72 + impact * 0.84 + overdrive)); distortion *= 1.0 - calmMask; // Each large word punches the memory closer before tearing it into horizontal slices. // 撕裂/噪点布局只跟 TearSeed、冲击走,不用 _Time 连续翻页(LOG3 平移白噪)。 float zoom = 1.0 + impact * 0.10 + overdrive * 0.035; float2 sampleUV = (input.uv - 0.5) / zoom + 0.5; float rowCount = lerp(22.0, 68.0, overdrive); float row = floor(input.uv.y * rowCount); float tearStep = floor(_TearSeed * 97.0 + overdrive * 31.0); float rowNoise = hash21(float2(row + _TearSeed * 43.0, tearStep)); float tearThreshold = lerp( 0.91, 0.36, saturate(damage * 0.55 + impact * 0.75 + overdrive)); float tearGate = step(tearThreshold, rowNoise); float tearDirection = hash21(float2(row * 1.71 + _TearSeed * 19.0, tearStep * 0.83)) * 2.0 - 1.0; float tearAmount = _HorizontalTear * 0.08 + damage * 0.026 + impact * 0.060 + overdrive * 0.082; sampleUV.x += tearDirection * tearGate * tearAmount * distortion; // 常驻失真是整幅画面的亚像素级投影漂移,不再让大块噪波在画面上流动。 float projectionTime = _Time.y; float2 projectionWeave = float2( sin(projectionTime * 0.73) + sin(projectionTime * 1.91 + 1.7) * 0.35, sin(projectionTime * 0.57 + 2.4) + sin(projectionTime * 1.37) * 0.25); sampleUV += projectionWeave * float2(0.00055, 0.00038) * sim; // 偶发的行同步偏差只移动一条很窄的带,并随 sim 平方衰减。 // 默认状态几乎不可察觉,剧情脉冲抬高 _SimGlitch 时才会被读到。 float simSlot = floor(projectionTime * 1.4 + _TearSeed * 7.0); float syncRow = hash21(float2(simSlot, 4.3)); float syncBand = 1.0 - smoothstep( 0.006, 0.018, abs(input.uv.y - syncRow)); float syncGate = step(0.82, hash21(float2(simSlot, 8.1))); float syncDirection = hash21(float2(simSlot, 12.7)) * 2.0 - 1.0; sampleUV.x += syncDirection * syncBand * syncGate * 0.0035 * sim * sim; sampleUV.y = (sampleUV.y - 0.5) * (1.0 + impact * 0.016 + overdrive * 0.025 + sim * 0.008) + 0.5; sampleUV = saturate(sampleUV); half4 original = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv); float chromaOffset = (damage * 0.0035 + impact * 0.009 + overdrive * 0.016 + sim * 0.00065) * lerp(0.30, 1.0, max(tearGate, sim * 0.35)); half red = SAMPLE_TEXTURE2D( _MainTex, sampler_MainTex, saturate(sampleUV + float2(chromaOffset, 0))).r; half4 displaced = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, sampleUV); half blue = SAMPLE_TEXTURE2D( _MainTex, sampler_MainTex, saturate(sampleUV - float2(chromaOffset, 0))).b; float3 memorySample = float3(red, displaced.g, blue); float luminance = dot(memorySample, float3(0.2126, 0.7152, 0.0722)); float3 graded = lerp(luminance.xxx, memorySample, _Saturation); graded = (graded - 0.5) * _Contrast + 0.5; graded *= _Brightness; graded *= _ColorTint.rgb; // At the hit, collapse the busy memory into a near-uniform ink field so the // white center word owns the frame. A little luminance remains as a ghost contour. float flatten = saturate(impact * 0.82 + overdrive * 0.68); float3 inkColor = float3(0.012, 0.035, 0.065); float3 inkMemory = inkColor + luminance.xxx * lerp(0.13, 0.04, overdrive); graded = lerp(graded, inkMemory, flatten); float vignetteDistance = length(input.uv - 0.5) * 1.4142; float vignette = 1.0 - smoothstep(0.38, 1.0, vignetteDistance) * _Vignette; // 暗角对常驻 sim 也生效,保留“略暗一点”的记忆感;不再叠颗粒噪波。 graded *= lerp(1.0, vignette, max(distortion, sim * 0.85)); // dropout 仅过载瞬间;常驻不加任何噪波颗粒。 float dropoutRow = floor(input.uv.y * lerp(9.0, 21.0, overdrive)); float dropoutNoise = hash21(float2( dropoutRow + _TearSeed * 61.0, tearStep)); float dropoutGate = step(lerp(1.05, 0.78, overdrive), dropoutNoise); float dropoutLevel = lerp( 0.18, 1.55, hash21(float2(dropoutRow * 3.17, tearStep))); graded *= lerp(1.0, dropoutLevel, dropoutGate * overdrive * 0.72); float3 finalColor = lerp(original.rgb, graded, max(distortion, sim * 0.62)); // 常驻略压亮度,配合暗角形成记忆发暗,而不是靠噪波。 finalColor *= lerp(1.0, 0.90, sim * 0.55); // ── 记忆放映层 ───────────────────────────────────────────── // 只使用低频、全局或规则的信号变化。避免低频随机色块造成“油污”观感。 float analog = saturate(_AnalogGlitch) * (1.0 - calmMask * 0.85); if (analog > 0.001) { float analogScale = max(0.25, _AnalogScale); float2 analogTexel = float2( max(fwidth(input.uv.x), 1e-7), max(fwidth(input.uv.y), 1e-7)); float2 analogPixel = input.uv / analogTexel; // 柔和扫描暗纹:只向下压亮度,且强度低于 1%,不会形成明亮条带。 float scanPeriodPx = 5.5 * analogScale; float scanWave = 0.5 + 0.5 * sin(analogPixel.y * 6.2831853 / scanPeriodPx); float scanDarkening = (1.0 - scanWave) * 0.035 * analog; finalColor *= 1.0 - scanDarkening; // 旧投影灯泡的曝光呼吸作用于整幅画面,不生成局部斑块。 float exposureBreath = sin(projectionTime * 1.13) * 0.55 + sin(projectionTime * 2.71 + 1.2) * 0.20; finalColor *= 1.0 + exposureBreath * 0.018 * analog; // 亚像素色差只在水平方向分离红蓝通道;默认量级约为十分之一像素。 float chromaPixels = 0.42 * analog * saturate(_AnalogChroma); float2 projectionChromaUV = float2(analogTexel.x * chromaPixels, 0.0); half projectionRed = SAMPLE_TEXTURE2D( _MainTex, sampler_MainTex, saturate(sampleUV + projectionChromaUV)).r; half projectionBlue = SAMPLE_TEXTURE2D( _MainTex, sampler_MainTex, saturate(sampleUV - projectionChromaUV)).b; float chromaBlend = analog * saturate(_AnalogChroma) * 0.28; finalColor.r = lerp(finalColor.r, projectionRed, chromaBlend); finalColor.b = lerp(finalColor.b, projectionBlue, chromaBlend); // 轻微压黑四角,建立“被放映出来”的取景边界。 float2 edge = abs(input.uv - 0.5) * 2.0; float projectorEdge = smoothstep(0.68, 1.08, max(edge.x, edge.y)); finalColor *= 1.0 - projectorEdge * 0.055 * analog; } return half4( finalColor * input.color.rgb, original.a * input.color.a * _Opacity); } #endif // HUOSHAN_MEMORY_INCLUDED