diff --git a/modules/carto/src/layers/heatmap.ts b/modules/carto/src/layers/heatmap.ts index 68ac7d92334..35843bbf508 100644 --- a/modules/carto/src/layers/heatmap.ts +++ b/modules/carto/src/layers/heatmap.ts @@ -12,6 +12,7 @@ const glsl = (s: TemplateStringsArray) => `${s}`; const fs = glsl`\ uniform heatmapUniforms { + vec2 delta; float radiusPixels; vec2 colorDomain; vec3 color1; @@ -57,11 +58,15 @@ vec3 colorGradient(float value) { } const vec3 SHIFT = vec3(1.0, 256.0, 256.0 * 256.0); +vec4 pack(float value) { + return vec4(mod(vec3(value, floor(value / SHIFT.yz)), 256.0), 255.0) / 255.0; +} float unpack(vec3 color) { return 255.0 * dot(color, SHIFT); } vec4 heatmap_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { + bool firstPass = (heatmap.delta.y < 0.5); float accumulator = 0.0; // Randomize the lookup values to hide the fixed number of samples @@ -70,11 +75,10 @@ vec4 heatmap_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { // Gaussian normalization parameters const float sigma = SUPPORT / 3.0; const float a = -0.5 / (sigma * sigma); - const float w0 = 1.0 / (2.0 * 3.141592653589793 * sigma * sigma); // 2D normalization + const float w0 = 0.3989422804014327 / sigma; // 1D normalization for (float t = -SUPPORT; t <= SUPPORT; t++) { - for (float s = -SUPPORT; s <= SUPPORT; s++) { - vec2 percent = (vec2(s, t) + offset - 0.5) / SUPPORT; + vec2 percent = (t * heatmap.delta + offset - 0.5) / SUPPORT; vec2 delta = percent * heatmap.radiusPixels / texSize; vec4 offsetColor = texture(source, texCoord + delta); @@ -82,10 +86,13 @@ vec4 heatmap_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { float value = unpack(offsetColor.rgb); // Gaussian - float weight = w0 * exp(a * (s * s + t * t)); + float weight = w0 * exp(a * t * t); accumulator += value * weight; } + + if (firstPass) { + return pack(accumulator); } // Apply domain @@ -135,6 +142,7 @@ export type HeatmapProps = { }; export type HeatmapUniforms = { + delta?: [number, number]; radiusPixels?: number; colorDomain?: [number, number]; color1?: [number, number, number]; @@ -149,6 +157,7 @@ export type HeatmapUniforms = { export const heatmap: ShaderPass = { name: 'heatmap', uniformPropTypes: { + delta: {value: [0, 1]}, radiusPixels: {value: 20, min: 0, softMax: 100}, colorDomain: {value: [0, 1]}, color1: {value: [0, 0, 0]}, @@ -160,6 +169,7 @@ export const heatmap: ShaderPass = { opacity: {value: 1, min: 0, max: 1} }, uniformTypes: { + delta: 'vec2', radiusPixels: 'f32', colorDomain: 'vec2', color1: 'vec3', @@ -172,13 +182,15 @@ export const heatmap: ShaderPass = { }, getUniforms: opts => { const { + delta = [1, 0], colorRange = defaultColorRange, radiusPixels = 20, colorDomain = [0, 1], opacity = 1 - } = opts as HeatmapProps; + } = opts as HeatmapProps & {delta: [number, number]}; const [color1, color2, color3, color4, color5, color6] = colorRange; return { + delta, color1, color2, color3, @@ -192,5 +204,8 @@ export const heatmap: ShaderPass = { }, dependencies: [random], fs, - passes: [{sampler: true}] + passes: [ + {sampler: true, uniforms: {delta: [1, 0]}}, + {sampler: true, uniforms: {delta: [0, 1]}} + ] };