godot-plateformer/_shader/ripple2.gdshader

150 lines
4.9 KiB
Plaintext
Raw Normal View History

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) {
// debugR=通道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);
}