fragmentShader = {
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, `
#define MAX_SCHEME_LENGTH 16
precision highp float;
uniform float u_size;
uniform int u_n;
uniform vec3 u_colorScheme[MAX_SCHEME_LENGTH];
// Use this function because we cannot use dynamic value for array index in GLSL
vec3 getData(float x) {
for (int j=0; j<MAX_SCHEME_LENGTH; j++) {
if (j == int(floor(x))) return u_colorScheme[j];
}
return vec3(0.0, 0.0, 0.0);
}
// GLSL translation of basis() in d3
// https://github.com/d3/d3-interpolate/blob/master/src/basis.js
vec3 basis(float t1, vec3 v0, vec3 v1, vec3 v2, vec3 v3) {
float t2 = t1 * t1;
float t3 = t2 * t1;
return ((1.0 - 3.0 * t1 + 3.0 * t2 - t3) * v0
+ (4.0 - 6.0 * t2 + 3.0 * t3) * v1
+ (1.0 + 3.0 * t1 + 3.0 * t2 - 3.0 * t3) * v2
+ t3 * v3) / 6.0;
}
// GLSL translation of exported function in the URL below
// https://github.com/d3/d3-interpolate/blob/master/src/basis.js
vec3 getColor(float t) {
int n = u_n - 1;
float i = t <= 0.0 ? 0.0 : (t >= 1.0 ? float(n) - 1.0 : floor(t * float(n)));
float t2 = t <= 0.0 ? 0.0 : (t >= 1.0 ? 1.0 : t);
vec3 v1 = getData(i);
vec3 v2 = getData(i+1.0);
vec3 v0 = i > 0.0 ? getData(i-1.0) : 2.0 * v1 - v2;
vec3 v3 = i < float(n) - 1.0 ? getData(i+2.0) : 2.0 * v2 - v1;
return basis((t2 - i / float(n)) * float(n), v0, v1, v2, v3);
}
void main() {
// Fill pixels based on x coordinate
vec2 pos = gl_FragCoord.xy / u_size;
gl_FragColor = vec4(getColor(pos.x), 1.0);
}
`);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
throw new Error(gl.getShaderInfoLog(shader));
}