230 lines
4.9 KiB
WebGPU Shading Language
230 lines
4.9 KiB
WebGPU Shading Language
////////////////
|
|
// uniforms //
|
|
////////////////
|
|
|
|
struct Uniforms {
|
|
resolution: vec2u,
|
|
bounds_min: vec2f,
|
|
bounds_max: vec2f,
|
|
shading_intensity: f32,
|
|
}
|
|
|
|
@group(0) @binding(1) var<uniform> uniforms: Uniforms;
|
|
|
|
///////////////
|
|
// utility //
|
|
///////////////
|
|
|
|
fn remap(val: vec2f, a1: vec2f, b1: vec2f, a2: vec2f, b2: vec2f) -> vec2f {
|
|
return a2 + (b2 - a2) * ((val - a1) / (b1 - a1));
|
|
}
|
|
|
|
/////////////////
|
|
// constants //
|
|
/////////////////
|
|
|
|
const TAU = 6.283185307179586;
|
|
const E = 2.718281828459045;
|
|
|
|
const C_TAU = vec2f(TAU, 0.0);
|
|
const C_E = vec2f(E, 0.0);
|
|
const C_I = vec2f(0.0, 1.0);
|
|
|
|
/////////////////////////
|
|
// complex functions //
|
|
/////////////////////////
|
|
|
|
fn c_re(z: vec2f) -> vec2f {
|
|
return vec2(z.x, 0.0);
|
|
}
|
|
|
|
fn c_im(z: vec2f) -> vec2f {
|
|
return vec2(z.y, 0.0);
|
|
}
|
|
|
|
fn c_conj(z: vec2f) -> vec2f {
|
|
return z * vec2(1.0, -1.0);
|
|
}
|
|
|
|
fn c_abs_sq(z: vec2f) -> vec2f {
|
|
return vec2(dot(z, z), 0.0);
|
|
}
|
|
|
|
fn c_abs(z: vec2f) -> vec2f {
|
|
return vec2(sqrt(dot(z, z)), 0.0);
|
|
}
|
|
|
|
fn c_arg(z: vec2f) -> vec2f {
|
|
return vec2(atan2(z.y, z.x), 0.0);
|
|
}
|
|
|
|
fn c_add(u: vec2f, v: vec2f) -> vec2f {
|
|
return u + v;
|
|
}
|
|
|
|
fn c_sub(u: vec2f, v: vec2f) -> vec2f {
|
|
return u - v;
|
|
}
|
|
|
|
fn c_mul(u: vec2f, v: vec2f) -> vec2f {
|
|
return vec2(u.x*v.x - u.y*v.y, u.y*v.x + u.x*v.y);
|
|
}
|
|
|
|
fn c_div(u: vec2f, v: vec2f) -> vec2f {
|
|
return vec2(u.x*v.x + u.y*v.y, u.y*v.x - u.x*v.y) / dot(v, v);
|
|
}
|
|
|
|
fn c_pos(v: vec2f) -> vec2f {
|
|
return v;
|
|
}
|
|
|
|
fn c_neg(v: vec2f) -> vec2f {
|
|
return -v;
|
|
}
|
|
|
|
fn c_recip(v: vec2f) -> vec2f {
|
|
return vec2(v.x, -v.y) / dot(v, v);
|
|
}
|
|
|
|
fn c_exp(z: vec2f) -> vec2f {
|
|
return exp(z.x) * vec2(cos(z.y), sin(z.y));
|
|
}
|
|
|
|
fn c_log(z: vec2f) -> vec2f {
|
|
return vec2(0.5 * log(dot(z, z)), atan2(z.y, z.x));
|
|
}
|
|
|
|
fn c_pow(u: vec2f, v: vec2f) -> vec2f {
|
|
return c_exp(c_mul(c_log(u), v));
|
|
}
|
|
|
|
fn c_sqrt(z: vec2f) -> vec2f {
|
|
return c_pow(z, vec2(0.5, 0.0));
|
|
}
|
|
|
|
fn c_sin(z: vec2f) -> vec2f {
|
|
return vec2(sin(z.x)*cosh(z.y), cos(z.x)*sinh(z.y));
|
|
}
|
|
|
|
fn c_cos(z: vec2f) -> vec2f {
|
|
return vec2(cos(z.x)*cosh(z.y), -sin(z.x)*sinh(z.y));
|
|
}
|
|
|
|
fn c_tan(z: vec2f) -> vec2f {
|
|
return vec2(sin(2.0*z.x), sinh(2.0*z.y)) / (cos(2.0*z.x) + cosh(2.0*z.y));
|
|
}
|
|
|
|
fn c_sinh(z: vec2f) -> vec2f {
|
|
return vec2(sinh(z.x)*cos(z.y), cosh(z.x)*sin(z.y));
|
|
}
|
|
|
|
fn c_cosh(z: vec2f) -> vec2f {
|
|
return vec2(cosh(z.x)*cos(z.y), sinh(z.x)*sin(z.y));
|
|
}
|
|
|
|
fn c_tanh(z: vec2f) -> vec2f {
|
|
return vec2(sinh(2.0*z.x), sin(2.0*z.y)) / (cosh(2.0*z.x) + cos(2.0*z.y));
|
|
}
|
|
|
|
fn c_asin(z: vec2f) -> vec2f {
|
|
let u = c_sqrt(vec2f(1.0, 0.0) - c_mul(z, z));
|
|
let v = c_log(u + vec2(-z.y, z.x));
|
|
return vec2(v.y, -v.x);
|
|
}
|
|
|
|
fn c_acos(z: vec2f) -> vec2f {
|
|
let u = c_sqrt(vec2f(1.0, 0.0) - c_mul(z, z));
|
|
let v = c_log(u + vec2f(-z.y, z.x));
|
|
return vec2f(TAU*0.25 - v.y, v.x);
|
|
}
|
|
|
|
fn c_atan(z: vec2f) -> vec2f {
|
|
let u = vec2f(1.0, 0.0) - vec2f(-z.y, z.x);
|
|
let v = vec2f(1.0, 0.0) + vec2f(-z.y, z.x);
|
|
let w = c_log(c_div(u, v));
|
|
return 0.5 * vec2f(-w.y, w.x);
|
|
}
|
|
|
|
fn c_asinh(z: vec2f) -> vec2f {
|
|
let u = c_sqrt(vec2f(1.0, 0.0) + c_mul(z, z));
|
|
return c_log(u + z);
|
|
}
|
|
|
|
fn c_acosh(z: vec2f) -> vec2f {
|
|
let u = c_sqrt(vec2f(-1.0, 0.0) + c_mul(z, z));
|
|
return c_log(u + z);
|
|
}
|
|
|
|
fn c_atanh(z: vec2f) -> vec2f {
|
|
let u = vec2f(1.0, 0.0) + z;
|
|
let v = vec2f(1.0, 0.0) - z;
|
|
return 0.5 * c_log(c_div(u, v));
|
|
}
|
|
|
|
fn c_gamma(z: vec2f) -> vec2f {
|
|
let reflect = z.x < 0.5;
|
|
var zp = z;
|
|
if reflect {
|
|
zp = vec2(1.0, 0.0) - z;
|
|
}
|
|
var w = c_gamma_inner2(zp);
|
|
if reflect {
|
|
w = TAU * 0.5 * c_recip(c_mul(c_sin(TAU * 0.5 * z), w));
|
|
}
|
|
return w;
|
|
}
|
|
|
|
// Yang, ZH., Tian, JF. An accurate approximation formula for gamma function. J Inequal Appl 2018, 56 (2018).
|
|
// https://doi.org/10.1186/s13660-018-1646-6
|
|
fn c_gamma_inner(z: vec2f) -> vec2f {
|
|
let z2 = c_mul(z, z);
|
|
let z3 = c_mul(z2, z);
|
|
|
|
let a = c_sqrt(TAU * z);
|
|
let b = c_pow(1.0 / (E * E) * c_mul(z3, c_sinh(c_recip(z))), 0.5 * z);
|
|
let c = c_exp(7.0/324.0 * c_recip(c_mul(z3, 35.0 * z2 + 33.0)));
|
|
|
|
return c_mul(c_mul(a, b), c);
|
|
}
|
|
|
|
fn c_gamma_inner2(z: vec2f) -> vec2f {
|
|
let w = c_gamma_inner(z + vec2(3.0, 0.0));
|
|
return c_div(w, c_mul(c_mul(z, z + vec2(1.0, 0.0)), c_mul(z + vec2(2.0, 0.0), z + vec2(3.0, 0.0))));
|
|
}
|
|
|
|
/////////////////
|
|
// rendering //
|
|
/////////////////
|
|
|
|
fn hsv2rgb(c: vec3f) -> vec3f {
|
|
let p = abs(fract(c.xxx + vec3f(1.0, 2.0/3.0, 1.0/3.0)) * 6.0 - vec3f(3.0));
|
|
return c.z * mix(vec3f(1.0), clamp(p - vec3f(1.0), vec3f(0.0), vec3f(1.0)), c.y);
|
|
}
|
|
|
|
fn shademap(r: f32) -> f32 {
|
|
return r*inverseSqrt(r * r + 0.0625 * uniforms.shading_intensity);
|
|
}
|
|
|
|
fn colorfor(w: vec2f) -> vec3f {
|
|
let z = func_plot(w);
|
|
|
|
if z.x != z.x || z.y != z.y {
|
|
return vec3f(0.5, 0.5, 0.5);
|
|
}
|
|
|
|
let r = sqrt(z.x*z.x + z.y*z.y);
|
|
let arg = atan2(z.y, z.x);
|
|
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
|
|
return pow(hsv2rgb(hsv), vec3(2.0));
|
|
}
|
|
|
|
@fragment
|
|
fn main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
|
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
|
|
let z = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
|
|
|
let col = colorfor(z);
|
|
|
|
return vec4f(col, 1.0);
|
|
}
|