494 lines
13 KiB
GLSL
494 lines
13 KiB
GLSL
#version 300 es
|
|
precision highp float;
|
|
#define MAX_ITERS 1000
|
|
#define TAU 6.283185307179586
|
|
|
|
#define TAU 6.283185307179586
|
|
#define CONST_E vec2(2.718281828459045,0.)
|
|
#define CONST_PI vec2(TAU*.5,0.)
|
|
#define CONST_TAU vec2(TAU,0.)
|
|
#define CONST_EMGAMMA vec2(0.5772156649015329,0.)
|
|
|
|
uniform highp vec2 uResolution;
|
|
uniform highp vec4 uRangeAxes;
|
|
uniform int uDrawContours;
|
|
uniform int uIterations;
|
|
uniform float uShadingIntensity;
|
|
uniform int uSwapBlackWhite;
|
|
uniform int uSmoothColor;
|
|
|
|
vec3 hsv2rgb(vec3 c) {
|
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
}
|
|
|
|
vec2 map(vec2 value, vec2 inMin, vec2 inMax, vec2 outMin, vec2 outMax) {
|
|
return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin);
|
|
}
|
|
|
|
float valuemap(float r) {
|
|
return r*inversesqrt(r*r+0.0625*uShadingIntensity)*.975+.025;
|
|
}
|
|
|
|
float saturationmap(float r) {
|
|
float rr = 1./r;
|
|
return rr*inversesqrt(rr*rr+0.0625*uShadingIntensity)*.975+.025;
|
|
}
|
|
|
|
////////////////////////////////
|
|
// Complex function library //
|
|
////////////////////////////////
|
|
|
|
vec2 cre(vec2 z) {
|
|
return vec2(z.x,0.);
|
|
}
|
|
vec2 cim(vec2 z) {
|
|
return vec2(z.y,0.);
|
|
}
|
|
vec2 carg(vec2 z) {
|
|
float angle = TAU*.5 - mod(TAU*.5 - atan(z.y,z.x), TAU);
|
|
return vec2(angle, 0.);
|
|
}
|
|
vec2 carg_o(float off, vec2 z) {
|
|
float angle = TAU*.5 - mod(TAU*.5 - atan(z.y,z.x) - off, TAU) - off;
|
|
return vec2(angle, 0.);
|
|
}
|
|
vec2 cabs(vec2 z) {
|
|
return vec2(sqrt(z.x*z.x + z.y*z.y),0.);
|
|
}
|
|
vec2 cnorm(vec2 z) {
|
|
return vec2(z.x*z.x + z.y*z.y,0.);
|
|
}
|
|
vec2 cconj(vec2 z) {
|
|
return vec2(z.x, -z.y);
|
|
}
|
|
vec2 cmul(vec2 z1, vec2 z2) {
|
|
return vec2(z1.x*z2.x - z1.y*z2.y, z1.x*z2.y + z1.y*z2.x);
|
|
}
|
|
vec2 cdiv(vec2 z1, vec2 z2) {
|
|
return vec2(z1.x*z2.x + z1.y*z2.y, -z1.x*z2.y + z1.y*z2.x)/(z2.x*z2.x + z2.y*z2.y);
|
|
}
|
|
vec2 crecip(vec2 z) {
|
|
return vec2(z.x, -z.y)/(z.x*z.x + z.y*z.y);
|
|
}
|
|
vec2 csquare(vec2 z) {
|
|
return vec2(z.x*z.x - z.y*z.y, 2.*z.x*z.y);
|
|
}
|
|
vec2 cexp(vec2 z) {
|
|
return exp(z.x)*vec2(cos(z.y), sin(z.y));
|
|
}
|
|
vec2 clog(vec2 z) {
|
|
return vec2(log(cabs(z).x), carg(z).x);
|
|
}
|
|
vec2 clog_o(float off, vec2 z) {
|
|
return vec2(log(cabs(z).x), carg_o(off, z).x);
|
|
}
|
|
vec2 clogbase(vec2 z, vec2 base) {
|
|
return cdiv(clog(z),clog(base));
|
|
}
|
|
vec2 clogbase_o(float off, vec2 z, vec2 base) {
|
|
return cdiv(clog_o(off, z),clog_o(off, base));
|
|
}
|
|
vec2 cpow(vec2 z1, vec2 z2) {
|
|
return cexp(cmul(z2, clog(z1)));
|
|
}
|
|
vec2 croot(vec2 z1, vec2 z2) {
|
|
return cexp(cmul(crecip(z2), clog(z1)));
|
|
}
|
|
vec2 csqrt(vec2 z) {
|
|
return cexp(0.5*clog(z));
|
|
}
|
|
vec2 csqrt_o(float off, vec2 z) {
|
|
return cexp(0.5*clog_o(off, z));
|
|
}
|
|
vec2 csin(vec2 z) {
|
|
vec2 e1 = cexp(vec2( z.y,-z.x));
|
|
vec2 e2 = cexp(vec2(-z.y, z.x));
|
|
vec2 w = .5*(e1-e2);
|
|
return vec2(-w.y,w.x);
|
|
}
|
|
vec2 ccos(vec2 z) {
|
|
vec2 e1 = cexp(vec2( z.y,-z.x));
|
|
vec2 e2 = cexp(vec2(-z.y, z.x));
|
|
return .5*(e1 + e2);
|
|
}
|
|
vec2 ctan(vec2 z) {
|
|
vec2 e1 = cexp(vec2( z.y,-z.x));
|
|
vec2 e2 = cexp(vec2(-z.y, z.x));
|
|
vec2 w = cdiv(e1 - e2, e1 + e2);
|
|
return vec2(-w.y,w.x);
|
|
}
|
|
vec2 csinh(vec2 z) {
|
|
vec2 e1 = cexp(z);
|
|
vec2 e2 = cexp(-z);
|
|
return .5*(e1 - e2);
|
|
}
|
|
vec2 ccosh(vec2 z) {
|
|
vec2 e1 = cexp(z);
|
|
vec2 e2 = cexp(-z);
|
|
return .5*(e1 + e2);
|
|
}
|
|
vec2 ctanh(vec2 z) {
|
|
vec2 e1 = cexp(z);
|
|
vec2 e2 = cexp(-z);
|
|
return cdiv(e1 - e2, e1 + e2);
|
|
}
|
|
vec2 casin(vec2 z) {
|
|
vec2 v = clog( csqrt(vec2(1.,0.)-csquare(z)) + vec2(-z.y,z.x) );
|
|
return vec2(v.y,-v.x);
|
|
}
|
|
vec2 cacos(vec2 z) {
|
|
vec2 v = clog( csqrt(vec2(1.,0.)-csquare(z)) + vec2(-z.y,z.x) );
|
|
return CONST_TAU*.25 + vec2(-v.y,v.x);
|
|
}
|
|
vec2 catan(vec2 z) {
|
|
vec2 v = clog(cdiv(vec2(1.,0.) - vec2(-z.y,z.x), vec2(1.,0.) + vec2(-z.y,z.x)));
|
|
return .5*vec2(-v.y,v.x);
|
|
}
|
|
vec2 casin_o(float off, vec2 z) {
|
|
vec2 v = clog(csqrt_o(off, vec2(1.,0.)-csquare(z)) + vec2(-z.y,z.x) );
|
|
return vec2(v.y,-v.x);
|
|
}
|
|
vec2 cacos_o(float off, vec2 z) {
|
|
vec2 v = clog(csqrt_o(off, vec2(1.,0.)-csquare(z)) + vec2(-z.y,z.x) );
|
|
return CONST_TAU*.25 + vec2(-v.y,v.x);
|
|
}
|
|
vec2 casinh(vec2 z) {
|
|
return clog(csqrt(vec2(1.,0.)+csquare(z)) + z);
|
|
}
|
|
vec2 cacosh(vec2 z) {
|
|
return clog(csqrt(csquare(z)-vec2(1.,0.)) + z);
|
|
}
|
|
vec2 catanh(vec2 z) {
|
|
return 0.5*clog(cdiv(vec2(1.,0.)+z, vec2(1.,0.)-z));
|
|
}
|
|
vec2 casinh_o(float off, vec2 z) {
|
|
return clog(csqrt_o(off, vec2(1.,0.)+csquare(z)) + z);
|
|
}
|
|
vec2 cacosh_o(float off, vec2 z) {
|
|
return clog(csqrt_o(off, csquare(z)-vec2(1.,0.)) + z);
|
|
}
|
|
|
|
// values for gamma function
|
|
const float gammaP0 = 676.5203681218851;
|
|
const float gammaP1 = -1259.1392167224028;
|
|
const float gammaP2 = 771.32342877765313;
|
|
const float gammaP3 = -176.61502916214059;
|
|
const float gammaP4 = 12.507343278686905;
|
|
const float gammaP5 = -0.13857109526572012;
|
|
const float gammaP6 = 9.9843695780195716e-6;
|
|
const float gammaP7 = 1.5056327351493116e-7;
|
|
|
|
vec2 gamma_inner(vec2 z) {
|
|
z -= vec2(1.,0.);
|
|
vec2 x = vec2(0.99999999999980993,0.);
|
|
x += cdiv(vec2(gammaP0,0.), z + vec2(1.,0.));
|
|
x += cdiv(vec2(gammaP1,0.), z + vec2(2.,0.));
|
|
x += cdiv(vec2(gammaP2,0.), z + vec2(3.,0.));
|
|
x += cdiv(vec2(gammaP3,0.), z + vec2(4.,0.));
|
|
x += cdiv(vec2(gammaP4,0.), z + vec2(5.,0.));
|
|
x += cdiv(vec2(gammaP5,0.), z + vec2(6.,0.));
|
|
x += cdiv(vec2(gammaP6,0.), z + vec2(7.,0.));
|
|
x += cdiv(vec2(gammaP7,0.), z + vec2(8.,0.));
|
|
vec2 t = z + vec2(7.5,0.);
|
|
return cmul(
|
|
sqrt(TAU) * cpow(t, z + vec2(0.5,0.)),
|
|
cmul(cexp(-t), x)
|
|
);
|
|
}
|
|
|
|
vec2 cgamma(vec2 z) {
|
|
if(z.x < 0.5) {
|
|
return cdiv(CONST_TAU*.5, cmul(csin(cmul(CONST_TAU*.5, z)), gamma_inner(vec2(1.,0.) - z)));
|
|
} else {
|
|
return gamma_inner(z);
|
|
}
|
|
}
|
|
|
|
|
|
vec2 cdigamma_pos(vec2 z) {
|
|
z -= vec2(.5,0);
|
|
float denom = dot(z, z);
|
|
vec2 iz = vec2(1.,-1.) * z / denom;
|
|
vec2 iz3 = vec2(z.x*z.x*z.x - 3.*z.x*z.y*z.y, z.y*z.y*z.y - 3.*z.y*z.x*z.x) / (denom*denom*denom);
|
|
return clog(z + 0.041666666666666664*iz + 0.006423611111111111*iz3);
|
|
}
|
|
|
|
vec2 cdigamma(vec2 z) {
|
|
if(z.x > 10. || abs(z.y) > 10.) {
|
|
return cdigamma_pos(z);
|
|
} else if(z.x > -20.) {
|
|
vec2 res = cdigamma_pos(z + vec2(1000.,0.));
|
|
for(int i = 0; i < 1001; i++) {
|
|
vec2 denom = vec2(i,0) + z;
|
|
res -= vec2(1,-1) * denom / dot(denom, denom);
|
|
}
|
|
return res;
|
|
} else {
|
|
return cdigamma_pos(vec2(1.,0.) - z) - cdiv(CONST_TAU*.5, ctan(TAU*.5 * z));
|
|
}
|
|
}
|
|
|
|
#define ERF_P 0.3275911
|
|
#define ERF_A1 0.254829592
|
|
#define ERF_A2 -0.284496736
|
|
#define ERF_A3 1.421413741
|
|
#define ERF_A4 -1.453152027
|
|
#define ERF_A5 1.061405429
|
|
|
|
float erf_gt0(float x) {
|
|
float denom = 1. + ERF_P*x;
|
|
float denom2 = denom*denom;
|
|
return 1. - (ERF_A1/denom + ERF_A2/denom2 + ERF_A3/(denom*denom2) + ERF_A4/(denom2*denom2) + ERF_A5/(denom2*denom2*denom))*exp(-x*x);
|
|
}
|
|
|
|
float erf(float x) {
|
|
if(x >= 0.) {
|
|
return erf_gt0(x);
|
|
} else {
|
|
return -erf_gt0(-x);
|
|
}
|
|
}
|
|
|
|
#define CERF_A 0.5
|
|
#define CERF_ITERS 20
|
|
vec2 cerf_near_re(vec2 z) {
|
|
float x = z.x;
|
|
float y = z.y;
|
|
float x2 = x*x;
|
|
float exp_neg_x2 = exp(-x2);
|
|
vec2 summation = vec2(0);
|
|
for(int i = 1; i < CERF_ITERS; i++) {
|
|
float n = float(i);
|
|
float a2n2 = CERF_A*CERF_A*n*n;
|
|
float mult = exp(-a2n2)/(4.*a2n2 + 4.*x2);
|
|
float cosh_2any = cosh(2.*CERF_A*n*y);
|
|
float sinh_2any = sinh(2.*CERF_A*n*y);
|
|
float cos_2xy = cos(2.*x*y);
|
|
float sin_2xy = sin(2.*x*y);
|
|
summation += mult*vec2(
|
|
x - x*cosh_2any*cos_2xy + CERF_A*n*sinh_2any*sin_2xy,
|
|
x*cosh_2any*sin_2xy + CERF_A*n*sinh_2any*cos_2xy
|
|
);
|
|
if(abs(y) > 8. && i > 7) {
|
|
break;
|
|
} else if(abs(y) > 4. && i > 9) {
|
|
break;
|
|
}
|
|
}
|
|
summation *= 16.*CERF_A*exp_neg_x2/TAU;
|
|
float coeff = CERF_A*exp_neg_x2/(TAU*.5*x);
|
|
summation += vec2(erf(x) + coeff*(1. - cos(2.*x*y)), coeff*sin(2.*x*y));
|
|
return summation;
|
|
}
|
|
|
|
#define CERF_TERMS 250
|
|
#define RECIP_SQRT_PI 0.5641895835477563
|
|
vec2 cerf_near_im(vec2 z) {
|
|
vec2 z2 = csquare(z);
|
|
vec2 coeff = 2. * RECIP_SQRT_PI * cmul(z, cexp(-z2));
|
|
vec2 term = vec2(float(CERF_TERMS*2 + 1), 0);
|
|
for(int i = 0; i < CERF_TERMS; i++) {
|
|
int j = CERF_TERMS - i;
|
|
if(j < 1) {
|
|
break;
|
|
}
|
|
if((j / 2) * 2 == j) {
|
|
term = vec2(float(2*j - 1), 0.) + cdiv(float(2*j)*z2, term);
|
|
} else {
|
|
term = vec2(float(2*j - 1), 0.) - cdiv(float(2*j)*z2, term);
|
|
}
|
|
}
|
|
return cdiv(coeff, term);
|
|
|
|
}
|
|
|
|
vec2 cerf(vec2 z) {
|
|
if(z.y == 0.) {
|
|
return vec2(erf(z.x), 0);
|
|
} else if(abs(z.x) < abs(z.y)) {
|
|
return cerf_near_im(z);
|
|
} else {
|
|
return cerf_near_re(z);
|
|
}
|
|
}
|
|
|
|
vec2 ceulerfn(vec2 z) {
|
|
if(cabs(z).x > 1.) {
|
|
return z*0./0.;
|
|
}
|
|
vec2 res = vec2(1,0);
|
|
vec2 w = z;
|
|
for(int i = 0; i < 600; i++) {
|
|
res = cmul(res, vec2(1,0) - w);
|
|
w = cmul(w, z);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
vec2 csignre(vec2 z) {
|
|
return vec2(sign(z.x), 0);
|
|
}
|
|
|
|
vec2 csignim(vec2 z) {
|
|
return vec2(sign(z.y), 0);
|
|
}
|
|
|
|
vec2 cunitcircle(vec2 z) {
|
|
return z/cabs(z).x;
|
|
}
|
|
|
|
#define LAMBERT_W_ITERS 30
|
|
|
|
vec2 clambertw(vec2 z) {
|
|
vec2 res;
|
|
if(z.x < -0.3678795) {
|
|
if(z.y == 0.) {
|
|
res = vec2(z.x, 1.);
|
|
} else {
|
|
res = vec2(z.x, z.y + 0.4*sign(z.y));
|
|
}
|
|
} else {
|
|
res = z;
|
|
}
|
|
res = clog(res + vec2(1,0));
|
|
for(int i = 0; i < LAMBERT_W_ITERS; i++) {
|
|
res = cdiv(csquare(res) + cmul(z, cexp(-res)), res + vec2(1,0));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// https://www.desmos.com/calculator/eh9vx5dil4
|
|
const float ti0 = 0.927295218002;
|
|
const float ti1 = -0.254590436003;
|
|
const float ti2 = -0.130819127994;
|
|
const float ti3 = 0.176304922654;
|
|
const float ti4 = -0.0454098453075;
|
|
const float ti5 = -0.0648283093849;
|
|
const float ti6 = 0.0695819521032;
|
|
const float ti7 = -0.00902813277778;
|
|
const float ti8 = -0.036994;
|
|
const float ti9 = 0.0321614;
|
|
const float ti10 = 0.00102442;
|
|
const float ti11 = -0.0221968;
|
|
const float ti12 = 0.0156038;
|
|
const float ti13 = 0.00369106;
|
|
const float ti14 = -0.0135653;
|
|
|
|
const float tiOffset = -0.487222405586;
|
|
|
|
vec2 cti_inner(vec2 z) {
|
|
z -= vec2(0.5,0);
|
|
vec2 z2 = cmul(z,z);
|
|
vec2 z3 = cmul(z2,z);
|
|
vec2 z4 = cmul(z2,z2);
|
|
vec2 z5 = cmul(z4,z);
|
|
vec2 z6 = cmul(z4,z2);
|
|
vec2 z7 = cmul(z4,z3);
|
|
vec2 z8 = cmul(z4,z4);
|
|
|
|
return ti0*z
|
|
+ ti1/2.*z2
|
|
+ ti2/3.*z3
|
|
+ ti3/4.*z4
|
|
+ ti4/5.*z5
|
|
+ ti5/6.*z6
|
|
+ ti6/7.*z7
|
|
+ ti7/8.*z8
|
|
+ ti8/9.*cmul(z8,z)
|
|
+ ti9/10.*cmul(z8,z2)
|
|
+ ti10/11.*cmul(z8,z3)
|
|
+ ti11/12.*cmul(z8,z4)
|
|
+ ti12/13.*cmul(z8,z5)
|
|
+ ti13/14.*cmul(z8,z6)
|
|
+ ti14/15.*cmul(z8,z7)
|
|
- vec2(tiOffset,0);
|
|
}
|
|
|
|
vec2 cti(vec2 z) {
|
|
float m = 1.;
|
|
if(z.x < 0.) {
|
|
m = -1.;
|
|
}
|
|
if(cnorm(z).x <= 1.) {
|
|
return m*cti_inner(m*z);
|
|
} else {
|
|
return m*(TAU*.25*clog(vec2(abs(z.x), z.y*m)) + cti_inner(m*crecip(z)));
|
|
}
|
|
}
|
|
|
|
// Weierstrass P function (℘)
|
|
vec2 cweierp(vec2 z, vec2 w1, vec2 w2) {
|
|
vec2 sum = cpow(z, vec2(-2,0));
|
|
for(int i = -25; i <= 25; i++) {
|
|
for(int j = -25; j <= 25; j++) {
|
|
if(i == 0 && j == 0) { continue; }
|
|
|
|
vec2 lambda = float(i)*w1 + float(j)*w2;
|
|
sum += cpow(z - lambda, vec2(-2,0)) - cpow(lambda, vec2(-2,0));
|
|
}
|
|
}
|
|
|
|
return sum/2.;
|
|
}
|
|
|
|
////////////////////
|
|
// Drawing code //
|
|
////////////////////
|
|
|
|
// will be implemented by the input expression
|
|
vec2 f(vec2 z, vec2 c, vec2 n);
|
|
vec2 z_init(vec2 z);
|
|
vec2 c_init(vec2 z);
|
|
|
|
out vec4 fragColor;
|
|
void main() {
|
|
float axis_size = 1./uResolution.x;
|
|
vec2 pt = map(
|
|
gl_FragCoord.xy,
|
|
vec2(0.), uResolution.xy,
|
|
uRangeAxes.xz, uRangeAxes.yw
|
|
);
|
|
vec2 z = z_init(pt);
|
|
vec2 c = c_init(pt);
|
|
for(int i = 0; i < MAX_ITERS; i++) {
|
|
if(i >= uIterations) {
|
|
break;
|
|
}
|
|
z = f(z, c, vec2(float(i),0.));
|
|
}
|
|
float rad = cabs(z).x;
|
|
if(rad == 0.) {
|
|
fragColor = vec4(vec3(uSwapBlackWhite),1.);
|
|
return;
|
|
} else if(isinf(rad)) {
|
|
fragColor = vec4(vec3(1-uSwapBlackWhite),1.);
|
|
return;
|
|
} else if(isnan(rad)) {
|
|
fragColor = vec4(.5,.5,.5,1.);
|
|
return;
|
|
}
|
|
float arg = carg(z).x;
|
|
float s = uSwapBlackWhite == 0 ? saturationmap(rad) : valuemap(rad);
|
|
float v = uSwapBlackWhite == 0 ? valuemap(rad) : saturationmap(rad);
|
|
if(uDrawContours == 1) {
|
|
if(abs(rad-1.) < 0.1) {
|
|
s *= 0.5;
|
|
v = clamp(v*1.5, 0., 1.);
|
|
} else if(rad > 0.1 && abs(fract(rad+0.5)-0.5) < 0.1) {
|
|
v *= 0.7;
|
|
}
|
|
}
|
|
vec3 col;
|
|
if(uSmoothColor == 1) {
|
|
float r = cos(arg)*.5 + .5;
|
|
float g = cos(arg - TAU/3.)*.5 + .5;
|
|
float b = cos(arg - 2.*TAU/3.)*.5 + .5;
|
|
vec3 hue = vec3(r,g,b);
|
|
col = hue*(1. - (1.-s) - (1.-v)) + (1.-s);
|
|
} else {
|
|
col = hsv2rgb(vec3(arg/TAU, s, v));
|
|
}
|
|
fragColor = vec4(col,1.);
|
|
}
|