WebGL: Heatmap

Vertex Shader:

<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>

Fragment Shader:

<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>

Code Block

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)
        };
    }
    

Last updated