diff --git a/examples/website/3d-heatmap/app.tsx b/examples/website/3d-heatmap/app.tsx index f4787c1204f..c40d56b7e2b 100644 --- a/examples/website/3d-heatmap/app.tsx +++ b/examples/website/3d-heatmap/app.tsx @@ -12,6 +12,7 @@ import {CSVLoader} from '@loaders.gl/csv'; import {load} from '@loaders.gl/core'; import type {Color, PickingInfo, MapViewState} from '@deck.gl/core'; +import type {Device} from '@luma.gl/core'; // Source data CSV const DATA_URL = @@ -78,13 +79,15 @@ export default function App({ mapStyle = MAP_STYLE, radius = 1000, upperPercentile = 100, - coverage = 1 + coverage = 1, + device }: { data?: DataPoint[] | null; mapStyle?: string; radius?: number; upperPercentile?: number; coverage?: number; + device?: Device; }) { const layers = [ new HexagonLayer({ @@ -115,6 +118,7 @@ export default function App({ return ( extends Colum getShaders() { const shaders = super.getShaders(); shaders.modules.push(hexagonUniforms); - return {...shaders, vs}; + return this.context.device.type === 'webgpu' ? {...shaders, source, vs} : {...shaders, vs}; } initializeState() { diff --git a/modules/aggregation-layers/src/hexagon-layer/hexagon-cell-layer.wgsl.ts b/modules/aggregation-layers/src/hexagon-layer/hexagon-cell-layer.wgsl.ts new file mode 100644 index 00000000000..384afa9d1bb --- /dev/null +++ b/modules/aggregation-layers/src/hexagon-layer/hexagon-cell-layer.wgsl.ts @@ -0,0 +1,120 @@ +// deck.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +export default /* wgsl */ `\ +const HEXBIN_DISTANCE: vec2 = vec2(1.7320508, 1.5); + +struct Attributes { + @builtin(instance_index) instanceIndex: u32, + @location(0) positions: vec3, + @location(1) normals: vec3, + @location(2) instancePositions: vec2, + @location(3) instanceColorValues: f32, + @location(4) instanceElevationValues: f32, +}; + +struct Varyings { + @builtin(position) position: vec4, + @location(0) color: vec4, + @location(1) pickingColor: vec3, +}; + +fn hexbinCentroid(binId: vec2, radius: f32) -> vec2 { + var adjustedBinId = binId; + adjustedBinId.x += fract(adjustedBinId.y * 0.5); + return adjustedBinId * HEXBIN_DISTANCE * radius; +} + +fn interpolate(value: f32, domain: vec2, range: vec2) -> f32 { + let ratio = clamp((value - domain.x) / (domain.y - domain.x), 0.0, 1.0); + return mix(range.x, range.y, ratio); +} + +fn sampleColorRange(value: f32, domain: vec2) -> vec4 { + let ratio = (value - domain.x) / (domain.y - domain.x); + return textureSampleLevel(colorRange, colorRangeSampler, vec2(ratio, 0.5), 0.0); +} + +@vertex +fn vertexMain(attributes: Attributes) -> Varyings { + var output: Varyings; + geometry.pickingColor = picking_getPickingColorFromIndex(attributes.instanceIndex); + output.pickingColor = geometry.pickingColor; + + if ( + attributes.instanceColorValues != attributes.instanceColorValues || + attributes.instanceColorValues < hexagon.colorDomain.z || + attributes.instanceColorValues > hexagon.colorDomain.w || + attributes.instanceElevationValues < hexagon.elevationDomain.z || + attributes.instanceElevationValues > hexagon.elevationDomain.w + ) { + output.position = vec4(0.0); + output.color = vec4(0.0); + return output; + } + + var commonPosition = + hexbinCentroid(attributes.instancePositions, column.radius) + + (hexagon.originCommon - project.commonOrigin.xy); + commonPosition += attributes.positions.xy * column.radius * column.coverage; + geometry.position = vec4(commonPosition, 0.0, 1.0); + geometry.normal = project_normal(attributes.normals); + + if (column.extruded > 0.5) { + var elevation = interpolate( + attributes.instanceElevationValues, + hexagon.elevationDomain.xy, + hexagon.elevationRange + ); + elevation = project_size_float(elevation); + geometry.position.z = (attributes.positions.z + 1.0) / 2.0 * elevation; + } + + output.position = project_common_position_to_clipspace(geometry.position); + var colorValue = sampleColorRange(attributes.instanceColorValues, hexagon.colorDomain.xy); + if (column.extruded > 0.5) { + colorValue = vec4( + lighting_getLightColor2( + colorValue.rgb, + project.cameraPosition, + geometry.position.xyz, + geometry.normal + ), + colorValue.a + ); + } + output.color = vec4(colorValue.rgb, colorValue.a * layer.opacity); + return output; +} + +@fragment +fn fragmentMain(varyings: Varyings) -> @location(0) vec4 { + if (picking.isActive > 0.5) { + if (!picking_isColorValid(varyings.pickingColor)) { + discard; + } + return vec4(varyings.pickingColor, 1.0); + } + + var color = varyings.color; + if (picking.isHighlightActive > 0.5) { + let highlightedObjectColor = picking_normalizeColor(picking.highlightedObjectColor); + if (picking_isColorZero(abs(varyings.pickingColor - highlightedObjectColor))) { + let highLightAlpha = picking.highlightColor.a; + let blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha); + if (blendedAlpha > 0.0) { + let highLightRatio = highLightAlpha / blendedAlpha; + color = vec4( + mix(color.rgb, picking.highlightColor.rgb, highLightRatio), + blendedAlpha + ); + } else { + color = vec4(color.rgb, 0.0); + } + } + } + + return deckgl_premultiplied_alpha(color); +} +`; diff --git a/modules/aggregation-layers/src/hexagon-layer/hexagon-layer-uniforms.ts b/modules/aggregation-layers/src/hexagon-layer/hexagon-layer-uniforms.ts index d0a86f6343a..3f1085dd8b2 100644 --- a/modules/aggregation-layers/src/hexagon-layer/hexagon-layer-uniforms.ts +++ b/modules/aggregation-layers/src/hexagon-layer/hexagon-layer-uniforms.ts @@ -5,6 +5,19 @@ import {Texture} from '@luma.gl/core'; import type {ShaderModule} from '@luma.gl/shadertools'; +const uniformBlockWGSL = /* wgsl */ `\ +struct HexagonUniforms { + colorDomain: vec4, + elevationDomain: vec4, + elevationRange: vec2, + originCommon: vec2, +}; + +@group(0) @binding(auto) var hexagon: HexagonUniforms; +@group(0) @binding(auto) var colorRange: texture_2d; +@group(0) @binding(auto) var colorRangeSampler: sampler; +`; + const uniformBlock = /* glsl */ `\ layout(std140) uniform hexagonUniforms { vec4 colorDomain; @@ -24,6 +37,7 @@ export type HexagonProps = { export const hexagonUniforms = { name: 'hexagon', + source: uniformBlockWGSL, vs: uniformBlock, uniformTypes: { colorDomain: 'vec4', diff --git a/website/src/examples/hexagon-layer.js b/website/src/examples/hexagon-layer.js index 43c6a9c5e9a..0f54124577f 100644 --- a/website/src/examples/hexagon-layer.js +++ b/website/src/examples/hexagon-layer.js @@ -13,6 +13,8 @@ import {makeExample} from '../components'; class HexagonDemo extends Component { static title = 'United Kingdom Road Safety'; + static hasDeviceTabs = true; + static data = { url: `${DATA_URI}/heatmap-data.txt`, worker: '/workers/heatmap-data-decoder.js' @@ -77,6 +79,8 @@ class HexagonDemo extends Component { return (