From 424f4ae5730fb8ff2f2bef15e7be2dad394a283e Mon Sep 17 00:00:00 2001 From: Florian Boesch Date: Thu, 14 Mar 2013 15:19:52 +0000 Subject: [PATCH 01/12] added an option to use a gradient texture and turn of intensity to alpha --- deep-sea-gradient.png | Bin 0 -> 3403 bytes example.html | 4 +- readme.md | 8 ++++ skyline-gradient.png | Bin 0 -> 3452 bytes webgl-heatmap.coffee | 85 ++++++++++++++++++++++++++++++++---------- webgl-heatmap.js | 50 +++++++++++++++++++++++-- 6 files changed, 122 insertions(+), 25 deletions(-) create mode 100644 deep-sea-gradient.png create mode 100644 skyline-gradient.png diff --git a/deep-sea-gradient.png b/deep-sea-gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..dfb8f3fce597496af0f4ebf3f9c0d1d2117a46d5 GIT binary patch literal 3403 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 hZf69X window.onload = function(){ var canvas = document.getElementsByTagName('canvas')[0]; - var heatmap = createWebGLHeatmap({canvas: canvas}); + var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:true}); + //var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:true, gradientTexture:'deep-sea-gradient.png'}); + //var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:false, gradientTexture:'skyline-gradient.png'}); //var heatmap = createWebGLHeatmap({width: 500, height: 500}); // statically sized heatmap // just feeding some mouse coords diff --git a/readme.md b/readme.md index a80b9bc..3307ed2 100644 --- a/readme.md +++ b/readme.md @@ -28,6 +28,14 @@ catch(error){ } ``` +creation arguments + + * canvas: the canvas you wish to draw on + * width: explicit width + * height: explicit height + * intensityToAlpha: defaults to true + * gradientTexture: texture used instead of color calculation, can be path or an image + Add a data point. * x and y relative to the canvas in pixels diff --git a/skyline-gradient.png b/skyline-gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..9e046dadc7b8705034081ff66adc212fc43ac4e2 GIT binary patch literal 3452 zcmV-?4TJKDP)0o{~i8G{6YRf{vv)6AMh9XA-;ew@g{ zdiwpWmvR?u>l8sy0`G-{L;~?2a+~$sz6n6xOX91!JLdaw`B(vz3(n^w9$qH!=cJ(( z>!zDw7~{q4dcLVRy!F+`J%0P&J+vc%Vw@iqgq@-US?eAAEcZmZW0pJIqNY?xJp*qANK1W}~IrvbY6YqTbPko(ypE?JhqXXOv4si|+ z^&!svlsG(&078Z4JrIJ*PvscfuvHPRzCoNzA`=Ts62m6wO#ljfyZ{k#$NQG38`g?P zHGHZv0r3;e>0(SjuOdq8d%{X3*ATorAt5p`jAJVi`ISPm}sI%HY0$ieWKu zM&QLpC{iHYx|xc|u)UzCA*Mm($5@9g4twyKs^d!iZUPAFleOovxoVjV2Z%FSImo;) zQmM*f3NTb%(@s>m6_G5)DQo*Mv0P~#6BC5@behz^2kg|X+>%xANHRJ%v5N@iR-`%vc- z@Y0K5J>l(_@iN_%oGDuB0}Ng;LPhq6!I27D44-L^Z4oL@9c@~7l!0{8+p#xhC~re9 z(&J!vtK>wK;7eBgMqE@Nw4p0g5&ba-6YKADoatQa*-GIz>_gaf)4zOl=l&07Qq3nV*JvstX#h7Hk| zqLyFY5}!M12fl|e(6vt|H^X5wQpIOEPoFy9c$X(@g*b^}!ZfaE{d2S4Qib~+sS zw^g4OLUfBHHF?bJn{PExBvGvVEq9km!}NH^Bi%Cw-P9bJOL3~oM4rT#bef0Mfw;-7 zr}QVmp_SaCQZ)=-X?0pgxl=E-MrcG$eT57+I*3h*YQM{R#vr#zKMj(cc149!`FRFE zQyUOBGbfg}#`()wzOy%KrW}%E%gVW6&5r>s62mopO-h}fJaMD z-NcAOrt8wK$6b$L5LLwsP?2@GR)Yr~ z>>!k^059%c38plgY;RqeUQfxP4O41>mJ0G9us4{g7Y(}Pfb?}{V_-xX3qV-}wrON& zWX*d$yyBxwYes=MH7&ic@oJDvu}4EyscW>uoBIDSh=kCv@rDhA#4M#Ec2k+bjirctv;Zo<&rSjMCO zI)znLXJElliBj@1JCk|G5lv(>PoR?_3!hC6=rxW?i%VVpz)JrI_oPP?@4tC2N)3-uORcs*>5V9jY#m z^rls5WKpfas!n?AK+c1jf}6$`9Y2}KVFLT2H{~nL>UIx705u%t#gTPA!tt^*as4Br z)MJB`Hlv6}xcb*)*o6|-_@fKR+*-8F(U{W}rJkdd zSy^(an$#L~43|rkIaZ)PmFTM|wav!JKd4MyalX zlMI@A@BKw*S$y(SE*?!gU8^?eiqFe*jB6A_ucJYD9U@0-Om5Ul8-une@2pN`N>RGB z8`j+9a{x%KeCpNu@-Yy z?VjN60jvlfdean0yT)oIvj;P*UDa+bn*h@6qduk|N4uqFVC2*J(RqK<*PJg{3eAR; z0jWkqrsjhtO`YgeX9T7@%XwvRRWP*da@Uc@A99H?)< z0NBmIDljz$T<6r$8{9u9Wckkm&u`2E&7oq>AgbSNgwSY%C!%sZ-=zVOd?6fKt$v|9 z?@NchVv}H#{5Fi+Vx;I3M0M=WWcmZXQjJvr`$+8Q%{LFK%2h@jku_sTg$nJNo1c%) z#q*?$+tu$Hg1VFD7a_ZntNBDzQyxI zm{qnHbkb>kJK z66npPp*io_sLI}YaVKd~enpRh;M6s3M6-q*jEl<052eT0_>eY=!-(GGkV+(lmBCwv zV0qb?80(}XacZW;fH;7zR2vM|@G>yg8s3mTh6Jq_nmG^L=E1f4AG1_dD=8Y#ZWARM z^E~99hj1j4d_2^Cg*SKVZ9+bNVBv*|cBgBnKo(!8dcwQLJp1=3J&(r6FEcoX`Ff;| eCj0g@4F3i;vYelP3ls+c0000 + @width = data.width + @height = data.height + + @gl.texImage2D @target, 0, @channels, @channels, @type, data + return @ + linear: -> @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.LINEAR @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR @@ -368,7 +375,7 @@ class Heights @pointCount += 1 class WebGLHeatmap - constructor: ({@canvas, @width, @height}={}) -> + constructor: ({@canvas, @width, @height, intensityToAlpha, gradientTexture}={}) -> @canvas = document.createElement('canvas') unless @canvas try @gl = @canvas.getContext('experimental-webgl', depth:false, antialias:false) @@ -385,22 +392,30 @@ class WebGLHeatmap @gl.enableVertexAttribArray 0 @gl.blendFunc @gl.ONE, @gl.ONE - @shader = new Shader @gl, - vertex: vertexShaderBlit - fragment: fragmentShaderBlit + ''' - float linstep(float low, float high, float value){ - return clamp((value-low)/(high-low), 0.0, 1.0); - } - - float fade(float low, float high, float value){ - float mid = (low+high)*0.5; - float range = (high-low)*0.5; - float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0); - return smoothstep(0.0, 1.0, x); + if gradientTexture + textureGradient = @gradientTexture = new Texture(@gl, channels:'rgba').bind(0).setSize(2, 2).linear().clampToEdge() + if typeof gradientTexture == 'string' + image = new Image() + image.onload = -> + textureGradient.bind().upload(image) + image.src = gradientTexture + else + if gradientTexture.width > 0 and gradientTexture.height > 0 + textureGradient.upload(gradientTexture) + else + gradientTexture.onload = -> + textureGradient.upload(gradientTexture) + + getColorFun = ''' + uniform sampler2D gradientTexture; + vec3 getColor(float intensity){ + return texture2D(gradientTexture, vec2(intensity, 0.0)).rgb; } - void main(){ - float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r); - + ''' + else + textureGradient = null + getColorFun = ''' + vec3 getColor(float intensity){ vec3 blue = vec3(0.0, 0.0, 1.0); vec3 cyan = vec3(0.0, 1.0, 1.0); vec3 green = vec3(0.0, 1.0, 0.0); @@ -414,12 +429,40 @@ class WebGLHeatmap fade(0.5, 1.0, intensity)*yellow + smoothstep(0.75, 1.0, intensity)*red ); - - gl_FragColor = vec4(color*intensity, intensity); - //gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + return color; } ''' + intensityToAlpha ?= true + + if intensityToAlpha + output = 'vec4(color*intensity, intensity)' + else + output = 'vec4(color, 1.0)' + + @shader = new Shader @gl, + vertex: vertexShaderBlit + fragment: fragmentShaderBlit + """ + float linstep(float low, float high, float value){ + return clamp((value-low)/(high-low), 0.0, 1.0); + } + + float fade(float low, float high, float value){ + float mid = (low+high)*0.5; + float range = (high-low)*0.5; + float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0); + return smoothstep(0.0, 1.0, x); + } + + #{getColorFun} + + void main(){ + float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r); + vec3 color = getColor(intensity); + gl_FragColor = #{output}; + } + """ + @width ?= @canvas.offsetWidth or 2 @height ?= @canvas.offsetHeight or 2 @canvas.width = @width @@ -458,7 +501,9 @@ class WebGLHeatmap @gl.bindBuffer @gl.ARRAY_BUFFER, @quad @gl.vertexAttribPointer(0, 4, @gl.FLOAT, false, 0, 0) @heights.nodeFront.bind(0) - @shader.use().int('source', 0) + if @gradientTexture + @gradientTexture.bind(1) + @shader.use().int('source', 0).int('gradientTexture', 1) @gl.drawArrays @gl.TRIANGLES, 0, 6 update: -> diff --git a/webgl-heatmap.js b/webgl-heatmap.js index 180f1db..3971c82 100644 --- a/webgl-heatmap.js +++ b/webgl-heatmap.js @@ -208,6 +208,13 @@ return this; }; + Texture.prototype.upload = function(data) { + this.width = data.width; + this.height = data.height; + this.gl.texImage2D(this.target, 0, this.channels, this.channels, this.type, data); + return this; + }; + Texture.prototype.linear = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); @@ -432,8 +439,8 @@ WebGLHeatmap = (function() { function WebGLHeatmap(_arg) { - var quad, _ref, _ref1, _ref2; - _ref = _arg != null ? _arg : {}, this.canvas = _ref.canvas, this.width = _ref.width, this.height = _ref.height; + var getColorFun, gradientTexture, image, intensityToAlpha, output, quad, textureGradient, _ref, _ref1, _ref2; + _ref = _arg != null ? _arg : {}, this.canvas = _ref.canvas, this.width = _ref.width, this.height = _ref.height, intensityToAlpha = _ref.intensityToAlpha, gradientTexture = _ref.gradientTexture; if (!this.canvas) { this.canvas = document.createElement('canvas'); } @@ -459,9 +466,41 @@ } this.gl.enableVertexAttribArray(0); this.gl.blendFunc(this.gl.ONE, this.gl.ONE); + if (gradientTexture) { + textureGradient = this.gradientTexture = new Texture(this.gl, { + channels: 'rgba' + }).bind(0).setSize(2, 2).linear().clampToEdge(); + if (typeof gradientTexture === 'string') { + image = new Image(); + image.onload = function() { + return textureGradient.bind().upload(image); + }; + image.src = gradientTexture; + } else { + if (gradientTexture.width > 0 && gradientTexture.height > 0) { + textureGradient.upload(gradientTexture); + } else { + gradientTexture.onload = function() { + return textureGradient.upload(gradientTexture); + }; + } + } + getColorFun = 'uniform sampler2D gradientTexture;\nvec3 getColor(float intensity){\n return texture2D(gradientTexture, vec2(intensity, 0.0)).rgb;\n}'; + } else { + textureGradient = null; + getColorFun = 'vec3 getColor(float intensity){\n vec3 blue = vec3(0.0, 0.0, 1.0);\n vec3 cyan = vec3(0.0, 1.0, 1.0);\n vec3 green = vec3(0.0, 1.0, 0.0);\n vec3 yellow = vec3(1.0, 1.0, 0.0);\n vec3 red = vec3(1.0, 0.0, 0.0);\n\n vec3 color = (\n fade(-0.25, 0.25, intensity)*blue +\n fade(0.0, 0.5, intensity)*cyan +\n fade(0.25, 0.75, intensity)*green +\n fade(0.5, 1.0, intensity)*yellow +\n smoothstep(0.75, 1.0, intensity)*red\n );\n return color;\n}'; + } + if (intensityToAlpha == null) { + intensityToAlpha = true; + } + if (intensityToAlpha) { + output = 'vec4(color*intensity, intensity)'; + } else { + output = 'vec4(color, 1.0)'; + } this.shader = new Shader(this.gl, { vertex: vertexShaderBlit, - fragment: fragmentShaderBlit + 'float linstep(float low, float high, float value){\n return clamp((value-low)/(high-low), 0.0, 1.0);\n}\n\nfloat fade(float low, float high, float value){\n float mid = (low+high)*0.5;\n float range = (high-low)*0.5;\n float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\nvoid main(){\n float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r);\n\n vec3 blue = vec3(0.0, 0.0, 1.0);\n vec3 cyan = vec3(0.0, 1.0, 1.0);\n vec3 green = vec3(0.0, 1.0, 0.0);\n vec3 yellow = vec3(1.0, 1.0, 0.0);\n vec3 red = vec3(1.0, 0.0, 0.0);\n\n vec3 color = (\n fade(-0.25, 0.25, intensity)*blue +\n fade(0.0, 0.5, intensity)*cyan +\n fade(0.25, 0.75, intensity)*green +\n fade(0.5, 1.0, intensity)*yellow +\n smoothstep(0.75, 1.0, intensity)*red\n );\n\n gl_FragColor = vec4(color*intensity, intensity);\n //gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);\n}' + fragment: fragmentShaderBlit + ("float linstep(float low, float high, float value){\n return clamp((value-low)/(high-low), 0.0, 1.0);\n}\n\nfloat fade(float low, float high, float value){\n float mid = (low+high)*0.5;\n float range = (high-low)*0.5;\n float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\n\n" + getColorFun + "\n\nvoid main(){\n float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r);\n vec3 color = getColor(intensity);\n gl_FragColor = " + output + ";\n}") }); if ((_ref1 = this.width) == null) { this.width = this.canvas.offsetWidth || 2; @@ -498,7 +537,10 @@ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quad); this.gl.vertexAttribPointer(0, 4, this.gl.FLOAT, false, 0, 0); this.heights.nodeFront.bind(0); - this.shader.use().int('source', 0); + if (this.gradientTexture) { + this.gradientTexture.bind(1); + } + this.shader.use().int('source', 0).int('gradientTexture', 1); return this.gl.drawArrays(this.gl.TRIANGLES, 0, 6); }; From 743e5d032b41a3928f4e9200b2233c4c1f56fa68 Mon Sep 17 00:00:00 2001 From: Florian Boesch Date: Wed, 10 Apr 2013 11:36:06 +0000 Subject: [PATCH 02/12] fixed the precision specifiers --- webgl-heatmap.coffee | 6 ------ webgl-heatmap.js | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/webgl-heatmap.coffee b/webgl-heatmap.coffee index 1939b39..a6fa22b 100644 --- a/webgl-heatmap.coffee +++ b/webgl-heatmap.coffee @@ -188,9 +188,6 @@ vertexShaderBlit = ''' fragmentShaderBlit = ''' precision highp int; precision highp float; - precision highp vec2; - precision highp vec3; - precision highp vec4; uniform sampler2D source; varying vec2 texcoord; ''' @@ -215,9 +212,6 @@ class Heights fragment: ''' precision highp int; precision highp float; - precision highp vec2; - precision highp vec3; - precision highp vec4; varying vec2 off, dim; varying float vIntensity; void main(){ diff --git a/webgl-heatmap.js b/webgl-heatmap.js index 3971c82..90e5ebd 100644 --- a/webgl-heatmap.js +++ b/webgl-heatmap.js @@ -283,7 +283,7 @@ vertexShaderBlit = 'attribute vec4 position;\nvarying vec2 texcoord;\nvoid main(){\n texcoord = position.xy*0.5+0.5;\n gl_Position = position;\n}'; - fragmentShaderBlit = 'precision highp int;\nprecision highp float;\nprecision highp vec2;\nprecision highp vec3;\nprecision highp vec4;\nuniform sampler2D source;\nvarying vec2 texcoord;'; + fragmentShaderBlit = 'precision highp int;\nprecision highp float;\nuniform sampler2D source;\nvarying vec2 texcoord;'; Heights = (function() { @@ -295,7 +295,7 @@ this.height = height; this.shader = new Shader(this.gl, { vertex: 'attribute vec4 position, intensity;\nvarying vec2 off, dim;\nvarying float vIntensity;\nuniform vec2 viewport;\n\nvoid main(){\n dim = abs(position.zw);\n off = position.zw;\n vec2 pos = position.xy + position.zw;\n vIntensity = intensity.x;\n gl_Position = vec4((pos/viewport)*2.0-1.0, 0.0, 1.0);\n}', - fragment: 'precision highp int;\nprecision highp float;\nprecision highp vec2;\nprecision highp vec3;\nprecision highp vec4;\nvarying vec2 off, dim;\nvarying float vIntensity;\nvoid main(){\n float falloff = (1.0 - smoothstep(0.0, 1.0, length(off/dim)));\n float intensity = falloff*vIntensity;\n gl_FragColor = vec4(intensity);\n}' + fragment: 'precision highp int;\nprecision highp float;\nvarying vec2 off, dim;\nvarying float vIntensity;\nvoid main(){\n float falloff = (1.0 - smoothstep(0.0, 1.0, length(off/dim)));\n float intensity = falloff*vIntensity;\n gl_FragColor = vec4(intensity);\n}' }); this.clampShader = new Shader(this.gl, { vertex: vertexShaderBlit, From 37a0176c0bad58d536ecda2ffc90ede302ff6c28 Mon Sep 17 00:00:00 2001 From: Florian Boesch Date: Sat, 27 Apr 2013 06:50:53 +0000 Subject: [PATCH 03/12] added an alpha range parameter --- example.html | 2 ++ webgl-heatmap.coffee | 19 +++++++++++++++---- webgl-heatmap.js | 15 ++++++++------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/example.html b/example.html index 43fda9d..8ab1403 100644 --- a/example.html +++ b/example.html @@ -6,6 +6,8 @@ window.onload = function(){ var canvas = document.getElementsByTagName('canvas')[0]; var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:true}); + //var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:true, alphaRange: [1, 0]}); // inverse transparency + //var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:true, alphaRange: [0, 0.05]}); // steep transparency //var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:true, gradientTexture:'deep-sea-gradient.png'}); //var heatmap = createWebGLHeatmap({canvas: canvas, intensityToAlpha:false, gradientTexture:'skyline-gradient.png'}); //var heatmap = createWebGLHeatmap({width: 500, height: 500}); // statically sized heatmap diff --git a/webgl-heatmap.coffee b/webgl-heatmap.coffee index a6fa22b..2632da6 100644 --- a/webgl-heatmap.coffee +++ b/webgl-heatmap.coffee @@ -369,7 +369,7 @@ class Heights @pointCount += 1 class WebGLHeatmap - constructor: ({@canvas, @width, @height, intensityToAlpha, gradientTexture}={}) -> + constructor: ({@canvas, @width, @height, intensityToAlpha, gradientTexture, alphaRange}={}) -> @canvas = document.createElement('canvas') unless @canvas try @gl = @canvas.getContext('experimental-webgl', depth:false, antialias:false) @@ -430,9 +430,19 @@ class WebGLHeatmap intensityToAlpha ?= true if intensityToAlpha - output = 'vec4(color*intensity, intensity)' + [alphaStart, alphaEnd] = alphaRange ? [0,1] + output = """ + vec4 alphaFun(vec3 color, float intensity){ + float alpha = smoothstep(#{alphaStart.toFixed(8)}, #{alphaEnd.toFixed(8)}, intensity); + return vec4(color*alpha, alpha); + } + """ else - output = 'vec4(color, 1.0)' + output = ''' + vec4 alphaFun(vec3 color, float intensity){ + return vec4(color, 1.0); + } + ''' @shader = new Shader @gl, vertex: vertexShaderBlit @@ -449,11 +459,12 @@ class WebGLHeatmap } #{getColorFun} + #{output} void main(){ float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r); vec3 color = getColor(intensity); - gl_FragColor = #{output}; + gl_FragColor = alphaFun(color, intensity); } """ diff --git a/webgl-heatmap.js b/webgl-heatmap.js index 90e5ebd..037df14 100644 --- a/webgl-heatmap.js +++ b/webgl-heatmap.js @@ -439,8 +439,8 @@ WebGLHeatmap = (function() { function WebGLHeatmap(_arg) { - var getColorFun, gradientTexture, image, intensityToAlpha, output, quad, textureGradient, _ref, _ref1, _ref2; - _ref = _arg != null ? _arg : {}, this.canvas = _ref.canvas, this.width = _ref.width, this.height = _ref.height, intensityToAlpha = _ref.intensityToAlpha, gradientTexture = _ref.gradientTexture; + var alphaEnd, alphaRange, alphaStart, getColorFun, gradientTexture, image, intensityToAlpha, output, quad, textureGradient, _ref, _ref1, _ref2, _ref3; + _ref = _arg != null ? _arg : {}, this.canvas = _ref.canvas, this.width = _ref.width, this.height = _ref.height, intensityToAlpha = _ref.intensityToAlpha, gradientTexture = _ref.gradientTexture, alphaRange = _ref.alphaRange; if (!this.canvas) { this.canvas = document.createElement('canvas'); } @@ -494,18 +494,19 @@ intensityToAlpha = true; } if (intensityToAlpha) { - output = 'vec4(color*intensity, intensity)'; + _ref1 = alphaRange != null ? alphaRange : [0, 1], alphaStart = _ref1[0], alphaEnd = _ref1[1]; + output = "vec4 alphaFun(vec3 color, float intensity){\n float alpha = smoothstep(" + (alphaStart.toFixed(8)) + ", " + (alphaEnd.toFixed(8)) + ", intensity);\n return vec4(color*alpha, alpha);\n}"; } else { - output = 'vec4(color, 1.0)'; + output = 'vec4 alphaFun(vec3 color, float intensity){\n return vec4(color, 1.0);\n}'; } this.shader = new Shader(this.gl, { vertex: vertexShaderBlit, - fragment: fragmentShaderBlit + ("float linstep(float low, float high, float value){\n return clamp((value-low)/(high-low), 0.0, 1.0);\n}\n\nfloat fade(float low, float high, float value){\n float mid = (low+high)*0.5;\n float range = (high-low)*0.5;\n float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\n\n" + getColorFun + "\n\nvoid main(){\n float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r);\n vec3 color = getColor(intensity);\n gl_FragColor = " + output + ";\n}") + fragment: fragmentShaderBlit + ("float linstep(float low, float high, float value){\n return clamp((value-low)/(high-low), 0.0, 1.0);\n}\n\nfloat fade(float low, float high, float value){\n float mid = (low+high)*0.5;\n float range = (high-low)*0.5;\n float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\n\n" + getColorFun + "\n" + output + "\n\nvoid main(){\n float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r);\n vec3 color = getColor(intensity);\n gl_FragColor = alphaFun(color, intensity);\n}") }); - if ((_ref1 = this.width) == null) { + if ((_ref2 = this.width) == null) { this.width = this.canvas.offsetWidth || 2; } - if ((_ref2 = this.height) == null) { + if ((_ref3 = this.height) == null) { this.height = this.canvas.offsetHeight || 2; } this.canvas.width = this.width; From 85d4d2cd232094c2a9c629876fd4904cb125a42c Mon Sep 17 00:00:00 2001 From: thinker0 Date: Sat, 25 May 2013 00:42:58 +0900 Subject: [PATCH 04/12] check webkit/ms support --- example.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example.html b/example.html index 8ab1403..d3ef32c 100644 --- a/example.html +++ b/example.html @@ -39,7 +39,8 @@ heatmap.clear(); } - var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame; + var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; var update = function(){ heatmap.adjustSize(); // can be commented out for statically sized heatmaps, resize clears the map From a4c266f770e4d9e4a8b578b8087144f2ee291a96 Mon Sep 17 00:00:00 2001 From: Florian Boesch Date: Fri, 7 Jun 2013 14:21:59 +0000 Subject: [PATCH 05/12] added touch handling for mobiles and better precision handling --- example.html | 28 +++++++++++++++++++++++----- webgl-heatmap.coffee | 25 ++++++++++++++++++++----- webgl-heatmap.js | 12 +++++++++--- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/example.html b/example.html index 8ab1403..c67fc72 100644 --- a/example.html +++ b/example.html @@ -1,6 +1,7 @@ +