shader_type canvas_item; // ✅ Godot 4:必须这样拿屏幕纹理(不能用 SCREEN_TEXTURE) uniform sampler2D screen_tex : hint_screen_texture, filter_linear_mipmap; // 可视化调试:R=通道,G=边缘脊,B=尾迹 uniform bool debug_show = false; // 由脚本每帧传入(屏幕UV 0~1) uniform vec2 obj_uv = vec2(0.5, 0.5); uniform vec2 obj_dir = vec2(1.0, 0.0); uniform float obj_speed = 1.0; // 可选噪声:建议给一张灰度噪声(Perlin/BlueNoise) // 没有也能跑,只是边缘会更规整 uniform sampler2D noise_tex : filter_linear, repeat_enable; // -------- 形状(开槽+水脊)-------- uniform float lane_half_width = 0.085; // 通道半宽(越大拨开越宽) uniform float edge_width = 0.040; // 水脊宽度(卷起那条边) uniform float trail_length = 0.55; // 尾迹长度 // -------- 位移强度(核心)-------- uniform float push_strength = 0.075; // 两侧推开(主要折射) uniform float drag_strength = 0.045; // 沿方向拖拽(带走水) uniform float suck_strength = 0.030; // 中心下陷(通道更像被挖开) uniform float curl_strength = 0.070; // 卷边强度(更像拨水卷起) // -------- 泡沫/细节 -------- uniform float foam_strength = 0.45; // 水脊亮边强度 uniform float noise_scale = 3.5; // 噪声缩放 uniform float noise_advect = 0.50; // 噪声随时间沿方向流动速度 uniform float chroma_shift = 0.60; // 色散强度(0=无) float sat(float x) { return clamp(x, 0.0, 1.0); } vec2 safe_norm(vec2 v) { float l = length(v); return (l > 1e-5) ? (v / l) : vec2(1.0, 0.0); } float n2(vec2 uv) { // 如果你没设置 noise_tex,这里通常会是 0(效果依旧能跑) return texture(noise_tex, uv).r; } // 用噪声梯度构造“卷动方向”(让边缘像被拨起来) vec2 curl_dir(vec2 uv) { float e = 0.002; float nL = n2(uv - vec2(e, 0.0)); float nR = n2(uv + vec2(e, 0.0)); float nD = n2(uv - vec2(0.0, e)); float nU = n2(uv + vec2(0.0, e)); vec2 g = vec2(nR - nL, nU - nD); return safe_norm(vec2(-g.y, g.x)); } void fragment() { vec2 uv = SCREEN_UV; vec2 dir = safe_norm(obj_dir); vec2 perp = vec2(-dir.y, dir.x); // 局部坐标(相对玩家) vec2 v = uv - obj_uv; float forward = dot(v, dir); // 沿前进方向(身后为正) float side = dot(v, perp); // 左右偏移 // 只在“身后”生效(像船头拨水/尾迹) float behind = step(0.0, forward); // 更像粘稠的尾迹衰减(指数衰减) float trail = exp(-forward / max(trail_length, 1e-4)) * behind; // 通道(中间被拨开的区域) float lane = 1.0 - smoothstep(lane_half_width, lane_half_width * 1.35, abs(side)); // 水脊(在通道边缘一圈) float edge = 1.0 - smoothstep(edge_width, 0.0, abs(abs(side) - lane_half_width)); // 噪声坐标:沿运动方向流动 vec2 flow_uv = uv * noise_scale + dir * (TIME * noise_advect); float nn = n2(flow_uv); float wobble = (nn * 2.0 - 1.0); // 速度缩放(慢就弱) float spd = sat(obj_speed); // 推开方向(左右分开) float sgn = sign(side + 1e-6); vec2 push = perp * sgn; // “抽走”方向(让中间更凹/更干净) vec2 suck = -push; // 卷边:用 curl_dir + push 混合,让边缘有翻卷感 vec2 cdir = curl_dir(flow_uv); vec2 curl = (cdir + push * 0.6) * (edge * trail); // 组装位移场(折射偏移) vec2 offset = vec2(0.0); // 1) 中央通道推开 offset += push * (lane * trail) * (push_strength * spd); // 2) 沿方向拖拽(把水带走) offset += dir * (lane * trail) * (drag_strength * spd); // 3) 中心下陷/抽走(更像被拨开挖出槽) offset += suck * (lane * trail) * (suck_strength * spd); // 4) 边缘卷动(水脊翻起) offset += curl * (curl_strength * spd); // 5) 边缘破碎(泡沫感) offset += push * wobble * (edge * trail) * (0.02 * spd); // 先准备输出颜色变量(避免 return) vec3 col; if (debug_show) { // debug:R=通道,G=边缘,B=尾迹 col = vec3(lane, edge, trail); } else { // ===== 折射采样(带一点色散)===== vec2 o = offset; if (chroma_shift > 0.001) { float cs = chroma_shift * 0.002; // 色散很容易过头,保持小 float r = texture(screen_tex, uv + o * (1.0 + cs)).r; float g = texture(screen_tex, uv + o).g; float b = texture(screen_tex, uv + o * (1.0 - cs)).b; col = vec3(r, g, b); } else { col = texture(screen_tex, uv + o).rgb; } // ===== 泡沫亮边 ===== float foam = edge * trail * spd; foam *= (0.55 + 0.45 * wobble); // 破碎感 foam = sat(foam); col += foam * foam_strength; } COLOR = vec4(col, 1.0); }