229 lines
8.8 KiB
HLSL
229 lines
8.8 KiB
HLSL
#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
|