WebGL: Heatmap
Last updated
Last updated
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
vec2 clipSpace = a_texCoord * 2.0 - 1.0;
gl_Position = vec4(clipSpace, 0, 1);
v_texCoord = a_texCoord;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_framebuffer; uniform vec4 u_colorArr[100]; uniform float u_colorCount; uniform float u_opacity; uniform float u_offset[100];
float remap ( float minval, float maxval, float curval ) {
return ( curval - minval ) / ( maxval - minval );
}
void main() {
float alpha = texture2D(u_framebuffer, v_texCoord.xy).a;
if (alpha > 0.0 && alpha <= 1.0) {
vec4 color_;
if (alpha <= u_offset[0]) {
color_ = u_colorArr[0];
} else if (alpha <= u_offset[1]) {
color_ = mix( u_colorArr[0], u_colorArr[1], remap( u_offset[0], u_offset[1], alpha ) );
} else if (alpha <= u_offset[2]) {
color_ = mix( u_colorArr[1], u_colorArr[2], remap( u_offset[1], u_offset[2], alpha ) );
} else if (alpha <= u_offset[3]) {
color_ = mix( u_colorArr[2], u_colorArr[3], remap( u_offset[2], u_offset[3], alpha ) );
} else if (alpha <= u_offset[4]) {
color_ = mix( u_colorArr[3], u_colorArr[4], remap( u_offset[3], u_offset[4], alpha ) );
} else if (alpha <= u_offset[5]) {
color_ = mix( u_colorArr[4], u_colorArr[5], remap( u_offset[4], u_offset[5], alpha ) );
} else if (alpha <= u_offset[6]) {
color_ = mix( u_colorArr[5], u_colorArr[6], remap( u_offset[5], u_offset[6], alpha ) );
} else if (alpha <= u_offset[7]) {
color_ = mix( u_colorArr[6], u_colorArr[7], remap( u_offset[6], u_offset[7], alpha ) );
} else if (alpha <= u_offset[8]) {
color_ = mix( u_colorArr[7], u_colorArr[8], remap( u_offset[7], u_offset[8], alpha ) );
} else if (alpha <= u_offset[9]) {
color_ = mix( u_colorArr[8], u_colorArr[9], remap( u_offset[8], u_offset[9], alpha ) );
} else if (alpha <= u_offset[10]) {
color_ = mix( u_colorArr[9], u_colorArr[10], remap( u_offset[9], u_offset[10], alpha ) );
} else {
color_ = vec4(0.0, 0.0, 0.0, 0.0);
}
color_.a = color_.a - (1.0 - u_opacity);
if (color_.a < 0.0) {
color_.a = 0.0;
}
gl_FragColor = color_;
}
}
</script>
let size = 100.0;
let max = 100;
let blur = 1.0;
let zoom = 1.0;
let angle = 0.0;
let density = 2.0;
var Texture = webglRenderer.TextureObject({
width: webglRenderer.width * webglRenderer.pixelRatio,
height: webglRenderer.height * webglRenderer.pixelRatio,
border: 0,
format: 'RGBA',
type: 'UNSIGNED_BYTE',
warpS: 'CLAMP_TO_EDGE',
warpT: 'CLAMP_TO_EDGE',
magFilter: 'LINEAR',
minFilter: 'LINEAR',
mipMap: false
});
var renderTarget = webglRenderer.RenderTarget({
texture: Texture
})
var geome = webglRenderer.PointsGeometry();
geome.setAttr('a_intensity', {
value: new Float32Array([]),
size: 1
});
geome.setAttr('a_position', {
value: new Float32Array([]),
size: 2
});
var gradshaderRef = webglRenderer.createShaderEl({
fragmentShader: GradfragmentShader,
vertexShader: GradvertexShader,
uniforms: {
u_resolution: {
value: new Float32Array([webglRenderer.width * webglRenderer.pixelRatio, webglRenderer.height * webglRenderer.pixelRatio])
},
u_max: {
value: max.toFixed(2)
},
u_size: {
value: size.toFixed(2)
},
u_blur: {
value: blur.toFixed(2)
},
u_translate: {
value: new Float32Array([0, 0])
},
u_zoom: {
value: zoom.toFixed(2)
},
u_angle: {
value: angle.toFixed(2)
},
u_density: {
value: density.toFixed(2)
},
u_zoomCenter: {
value: new Float32Array([0, 0])
}
},
geometry: geome
,
renderTarget: renderTarget
});
let colorGrad = [{
color: [255, 255, 255, 0.0],
offset: 0
}, {
color: [212, 225, 255, 1.0],
offset: 0.2
}, {
color: [166, 255, 115, 1.0],
offset: 0.45
}, {
color: [255, 255, 0, 0.5],
offset: 0.75
}, {
color: [255, 0, 0, 1.0],
offset: 1.0
}];
let colorGradMap = gradientMapper(colorGrad);
var meshgeome = webglRenderer.MeshGeometry();
meshgeome.setAttr('a_texCoord', {
value: new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]),
size: 2
});
meshgeome.setDrawRange(0, 6);
var opacity = 1.0;
var colorShaderRef = webglRenderer.createShaderEl({
fragmentShader: ColorfragmentShader,
vertexShader: ColorvertexShader,
uniforms: {
u_colorArr: {
value: colorGradMap.value,
size: 4
},
u_colorCount: {
value: colorGradMap.length.toFixed(2)
},
u_offset: {
value: colorGradMap.offset,
size: 1
},
u_opacity: {
value: opacity.toFixed(2)
}
,
u_framebuffer: {
value: Texture
}
},
geometry: meshgeome
});
var positionArray = [];
var intensityArray = [];
var dataPush = true;
document.getElementById('canvas').addEventListener("mousemove", function (e) {
if (dataPush) {
positionArray[positionArray.length] = e.x;
positionArray[positionArray.length] = e.y;
intensityArray[intensityArray.length] = 20;
gradshaderRef.setAttributeData('a_position', new Float32Array(positionArray));
gradshaderRef.setAttributeData('a_intensity', new Float32Array(intensityArray));
geome.setDrawRange(0, positionArray.length / 2);
// shaderRef.execute();
dataPush = false;
setTimeout(idleFlag, 20);
}
});
function idleFlag () {
dataPush = true;
}
function gradientMapper (grad) {
const arr = [];
const gradLength = grad.length;
const offSetsArray = [];
grad.forEach(function (d) {
arr.push(d.color[0] / 255);
arr.push(d.color[1] / 255);
arr.push(d.color[2] / 255);
arr.push(d.color[3] === undefined ? 1.0 : d.color[3]);
offSetsArray.push(d.offset);
});
return {
value: new Float32Array(arr),
length: gradLength,
offset: new Float32Array(offSetsArray)
};
}