Constant COMMON
Source pub const COMMON: &str = r#"
fn src_dim() -> vec2<u32> {
return min(textureDimensions(src_tex), post.extent.xy);
}
fn load_src(p: vec2<i32>) -> vec4<f32> {
let dim = vec2<i32>(src_dim());
if (dim.x <= 0 || dim.y <= 0) { return vec4<f32>(0.0); }
let c = clamp(p, vec2<i32>(0), dim - vec2<i32>(1));
return textureLoad(src_tex, c, 0);
}
fn sample_src(x: f32, y: f32) -> vec4<f32> {
let dim = vec2<f32>(src_dim());
if (dim.x < 1.0 || dim.y < 1.0) { return vec4<f32>(0.0); }
let xc = clamp(x, 0.0, dim.x - 1.0);
let yc = clamp(y, 0.0, dim.y - 1.0);
let x0 = i32(floor(xc));
let y0 = i32(floor(yc));
let tx = xc - floor(xc);
let ty = yc - floor(yc);
let a = load_src(vec2<i32>(x0, y0));
let b = load_src(vec2<i32>(x0 + 1, y0));
let c = load_src(vec2<i32>(x0, y0 + 1));
let d = load_src(vec2<i32>(x0 + 1, y0 + 1));
return mix(mix(a, b, tx), mix(c, d, tx), ty);
}
fn store(p: vec2<u32>, c: vec4<f32>) {
textureStore(dst_tex, p, c);
}
fn luma(c: vec3<f32>) -> f32 {
return dot(c, vec3<f32>(0.2126, 0.7152, 0.0722));
}
fn luma_at(x: i32, y: i32) -> f32 {
return luma(load_src(vec2<i32>(x, y)).rgb);
}
fn sobel(x: i32, y: i32) -> f32 {
let gx = -luma_at(x - 1, y - 1) + luma_at(x + 1, y - 1) - 2.0 * luma_at(x - 1, y) + 2.0 * luma_at(x + 1, y) - luma_at(x - 1, y + 1) + luma_at(x + 1, y + 1);
let gy = -luma_at(x - 1, y - 1) - 2.0 * luma_at(x, y - 1) - luma_at(x + 1, y - 1) + luma_at(x - 1, y + 1) + 2.0 * luma_at(x, y + 1) + luma_at(x + 1, y + 1);
return sqrt(gx * gx + gy * gy);
}
fn normalize3(v: vec3<f32>) -> vec3<f32> {
let len = length(v);
if (len < 1e-8) { return vec3<f32>(0.0, 0.0, 1.0); }
return v / len;
}
fn safe_pow(a: f32, b: f32) -> f32 {
if (a <= 0.0) { return 0.0; }
return pow(a, b);
}
fn edge_step(edge0: f32, edge1: f32, x: f32) -> f32 {
let span = edge1 - edge0;
if (abs(span) < 1e-8) {
if (x < edge0) { return 0.0; }
return 1.0;
}
let t = clamp((x - edge0) / span, 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
fn grain_hash(x: u32, y: u32, seed: u32) -> f32 {
var h = x * 0x8da6b343u ^ y * 0xd8163841u ^ seed * 0xcb1ab31fu;
h = h ^ (h >> 15u);
h = h * 0x2c1b3c6du;
h = h ^ (h >> 12u);
return f32(h >> 8u) / 16777216.0;
}
fn mixed_seed() -> u32 {
return post.seed ^ (post.frame * 0x9e3779b9u);
}
fn hash21(x: f32, y: f32) -> f32 {
var qx = fract(x * 123.34);
var qy = fract(y * 456.21);
let d = qx * (qx + 45.32) + qy * (qy + 45.32);
qx = qx + d;
qy = qy + d;
return fract(qx * qy);
}
fn grain_hash_i(x: i32, y: i32, seed: u32) -> f32 {
return grain_hash(u32(x), u32(y), seed);
}
fn value_noise(x: f32, y: f32, seed: u32) -> f32 {
let x0 = floor(x);
let y0 = floor(y);
let tx = x - x0;
let ty = y - y0;
let sx = tx * tx * (3.0 - 2.0 * tx);
let sy = ty * ty * (3.0 - 2.0 * ty);
let xi = i32(x0);
let yi = i32(y0);
let a = grain_hash_i(xi, yi, seed);
let b = grain_hash_i(xi + 1, yi, seed);
let c = grain_hash_i(xi, yi + 1, seed);
let d = grain_hash_i(xi + 1, yi + 1, seed);
let ab = a + (b - a) * sx;
let cd = c + (d - c) * sx;
return ab + (cd - ab) * sy;
}
fn bayer(x: u32, y: u32) -> f32 {
var v = 0u;
var xx = x & 7u;
var yy = y & 7u;
var half = 4u;
var mul = 1u;
for (var i = 0u; i < 3u; i = i + 1u) {
let cx = xx >= half;
let cy = yy >= half;
if (xx >= half) { xx = xx - half; }
if (yy >= half) { yy = yy - half; }
var add = 0u;
if (cx && !cy) { add = 2u; }
else if (!cx && cy) { add = 3u; }
else if (cx && cy) { add = 1u; }
v = v + add * mul;
mul = mul * 4u;
half = half / 2u;
}
return (f32(v) + 0.5) / 64.0;
}
fn saturate3(c: vec3<f32>, amount: f32) -> vec3<f32> {
let l = luma(c);
return max(vec3<f32>(l) + (c - vec3<f32>(l)) * amount, vec3<f32>(0.0));
}
fn quantize(v: f32, levels: f32) -> f32 {
let t = clamp(v, 0.0, 1.0);
let q = min(floor(t * levels), levels - 1.0);
return q / (levels - 1.0);
}
fn keep_rgb(rgb: vec3<f32>, threshold: f32, knee: f32, knee_width: f32, soft: f32) -> vec3<f32> {
let l = max(rgb.r, max(rgb.g, rgb.b));
var hard = 0.0;
if (l > threshold) { hard = max(l - threshold, 0.0) / max(l, 1e-4); }
var knee_t = 0.0;
if (knee_width > 1e-6) { knee_t = clamp((l - knee) / knee_width, 0.0, 1.0); }
return rgb * (hard + knee_t * knee_t * soft);
}
fn aces_channel(x: f32) -> f32 {
let a = x * (x * 2.51 + 0.03);
let b = x * (x * 2.43 + 0.59) + 0.14;
if (abs(b) < 1e-8) { return 0.0; }
return clamp(a / b, 0.0, 1.0);
}
fn agx_contrast(x: f32) -> f32 {
let x2 = x * x;
let x4 = x2 * x2;
return 15.5 * x4 * x2 - 40.14 * x4 * x + 31.96 * x4 - 6.868 * x2 * x + 0.4298 * x2 + 0.1191 * x - 0.00232;
}
fn agx(c: vec3<f32>) -> vec3<f32> {
let r0 = vec3<f32>(0.842479062253094, 0.0784335999999992, 0.0792237451477643);
let r1 = vec3<f32>(0.0423282422610123, 0.878468636469772, 0.0791661274605434);
let r2 = vec3<f32>(0.0423756549057051, 0.0784336, 0.879142973793104);
let v = vec3<f32>(dot(r0, c), dot(r1, c), dot(r2, c));
let min_ev = -12.47393;
let max_ev = 4.026069;
let span = max_ev - min_ev;
let t0 = clamp((log2(max(v.r, 1e-10)) - min_ev) / span, 0.0, 1.0);
let t1 = clamp((log2(max(v.g, 1e-10)) - min_ev) / span, 0.0, 1.0);
let t2 = clamp((log2(max(v.b, 1e-10)) - min_ev) / span, 0.0, 1.0);
return clamp(vec3<f32>(agx_contrast(t0), agx_contrast(t1), agx_contrast(t2)), vec3<f32>(0.0), vec3<f32>(1.0));
}
fn neutral_map(c: vec3<f32>, start: f32, toe: f32, desat: f32, clamp_hi: bool) -> vec3<f32> {
let low = min(c.r, min(c.g, c.b));
var offset = toe;
if (low < 0.08) { offset = low - 6.25 * low * low; }
var v = c - vec3<f32>(offset);
let peak = max(v.r, max(v.g, v.b));
if (peak >= start && peak > 1e-8) {
let d = 1.0 - start;
let denom = peak + d - start;
var mapped = start;
if (abs(denom) >= 1e-8) { mapped = 1.0 - d * d / denom; }
v = v * (mapped / peak);
let g = 1.0 - 1.0 / (desat * (peak - mapped) + 1.0);
v = mix(v, vec3<f32>(mapped), g);
}
if (clamp_hi) { v = clamp(v, vec3<f32>(0.0), vec3<f32>(1.0)); }
return v;
}
fn band_shade(light: f32, bands: f32, softness: f32, threshold: f32, shadow: f32) -> f32 {
let bands_f = max(bands, 1.0);
let light_c = clamp(light, 0.0, 1.0);
let x = min(light_c * bands_f, bands_f - 1e-4);
var w = 0.0;
if (softness > 0.0) { w = edge_step(0.5 - softness * 0.5, 0.5 + softness * 0.5, fract(x)); }
let idx = floor(x) + w;
var q = light_c;
if (bands_f > 1.0) { q = clamp(idx / (bands_f - 1.0), 0.0, 1.0); }
var gate = 1.0;
if (softness <= 1e-5) {
if (light_c < threshold) { gate = 0.0; }
} else {
gate = edge_step(threshold - softness, threshold + softness, light_c);
}
return q * (shadow + (1.0 - shadow) * gate);
}
fn palette(i: u32) -> vec3<f32> {
if (i == 0u) { return vec3<f32>(0.0, 0.0, 0.0); }
if (i == 1u) { return vec3<f32>(0.0122865, 0.0241576, 0.0865005); }
if (i == 2u) { return vec3<f32>(0.208637, 0.0185002, 0.0865005); }
if (i == 3u) { return vec3<f32>(0.0, 0.242281, 0.0822827); }
if (i == 4u) { return vec3<f32>(0.40724, 0.0843762, 0.0368895); }
if (i == 5u) { return vec3<f32>(0.114435, 0.0953075, 0.0781874); }
if (i == 6u) { return vec3<f32>(0.539479, 0.545724, 0.571125); }
if (i == 7u) { return vec3<f32>(1.0, 0.879622, 0.806952); }
if (i == 8u) { return vec3<f32>(1.0, 0.0, 0.0742136); }
if (i == 9u) { return vec3<f32>(1.0, 0.366253, 0.0); }
if (i == 10u) { return vec3<f32>(1.0, 0.838799, 0.0202886); }
if (i == 11u) { return vec3<f32>(0.0, 0.775822, 0.0368895); }
if (i == 12u) { return vec3<f32>(0.0221739, 0.417885, 1.0); }
if (i == 13u) { return vec3<f32>(0.226966, 0.181164, 0.332452); }
if (i == 14u) { return vec3<f32>(1.0, 0.184475, 0.391572); }
return vec3<f32>(1.0, 0.603827, 0.401978);
}
fn nearest_palette(c: vec3<f32>) -> vec3<f32> {
var best = palette(0u);
var best_d = 1e30;
for (var i = 0u; i < 16u; i = i + 1u) {
let swatch = palette(i);
let d = dot(c - swatch, c - swatch);
if (d < best_d) { best_d = d; best = swatch; }
}
return best;
}
fn encode_channel(c: f32) -> f32 {
if (c <= 0.0031308) { return c * 12.92; }
return 1.055 * safe_pow(max(c, 0.0), 1.0 / 2.4) - 0.055;
}
fn gaussian(p: vec2<u32>, dirx: i32, diry: i32, radius: u32, spread: f32) -> vec4<f32> {
var acc = vec4<f32>(0.0);
var total = 0.0;
var s = spread;
if (s <= 1e-4) { s = 1.0; }
let r = i32(min(radius, 64u));
for (var k = -r; k <= r; k = k + 1) {
let w = exp(-f32(k * k) / s);
acc = acc + load_src(vec2<i32>(i32(p.x) + k * dirx, i32(p.y) + k * diry)) * w;
total = total + w;
}
return acc / max(total, 1e-8);
}
fn ring_radius(ring: u32) -> f32 {
if (ring == 0u) { return post.p1.x; }
if (ring == 1u) { return post.p1.y; }
if (ring == 2u) { return post.p1.z; }
return post.p1.w;
}
fn ring_gain(ring: u32) -> f32 {
if (ring == 0u) { return post.p2.x; }
if (ring == 1u) { return post.p2.y; }
if (ring == 2u) { return post.p2.z; }
return post.p2.w;
}
fn ring_channel(c: f32, threshold: f32, clamp_hi: f32) -> f32 {
var v = max(c - threshold, 0.0);
if (clamp_hi > 0.0) { v = min(v, clamp_hi); }
return v;
}
"#;