fragmentShader = {
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, `
#extension GL_OES_standard_derivatives : enable
precision highp float;
const float pi = 3.14159265359;
const float width = ${canvas.width.toFixed(1)}; // TODO uniform?
const float height = ${canvas.height.toFixed(1)}; // TODO uniform?
const vec2 center = vec2(width, height) / 2.0;
uniform float t, contourSpacing, scale;
float value(float x, float y) {
return (${value});
}
vec3 cubehelix(vec3 c) {
float a = c.y * c.z * (1.0 - c.z);
float cosh = cos(c.x + pi / 2.0);
float sinh = sin(c.x + pi / 2.0);
return vec3(
(c.z + a * (1.78277 * sinh - 0.14861 * cosh)),
(c.z - a * (0.29227 * cosh + 0.90649 * sinh)),
(c.z + a * (1.97294 * cosh))
);
}
vec3 cubehelixDefault(float t) {
return cubehelix(vec3(mix(300.0 / 180.0 * pi, -240.0 / 180.0 * pi, t), 0.5, t));
}
vec3 cubehelixRainbow(float t) {
if (t < 0.0 || t > 1.0) t -= floor(t);
float ts = abs(t - 0.5);
return cubehelix(vec3((360.0 * t - 100.0) / 180.0 * pi, 1.5 - 1.5 * ts, 0.8 - 0.9 * ts));
}
float contourFunction (float f, float width, float feather) {
float w1 = width - feather * 0.5;
// Two slightly different options for the gradient magnitude. The first is a
// little faster, the second is a little more accurate. We use this to compute
// the required line width
//float d = fwidth(f);
float d = length(vec2(dFdx(f), dFdy(f)));
// Repeat the contour level every 1.0 units:
f = 0.5 - abs(mod(f, 1.0) - 0.5);
return smoothstep(d * w1, d * (w1 + feather), f);
}
void main(void) {
vec2 z = (gl_FragCoord.xy - center) * scale;
float f = value(z.x, z.y);
float contour = contourFunction(f / contourSpacing, 0.5, 0.75);
gl_FragColor = vec4(vec3(contour), 1.0);
}
`);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
throw new Error(gl.getShaderInfoLog(shader));
}