Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/developer-guide/webgpu.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ The table below covers the public layer exports from the layer packages. It is d
| `@deck.gl/layers` | `LineLayer` | ✅ | ✅ |
| `@deck.gl/layers` | `PointCloudLayer` | ✅ | ✅ |
| `@deck.gl/layers` | `ScatterplotLayer` | ✅ | ✅ |
| `@deck.gl/layers` | `ColumnLayer` | ✅ | |
| `@deck.gl/layers` | `GridCellLayer` | ✅ | |
| `@deck.gl/layers` | `ColumnLayer` | ✅ | |
| `@deck.gl/layers` | `GridCellLayer` | ✅ | |
| `@deck.gl/layers` | `PathLayer` | ✅ | ✅ |
| `@deck.gl/layers` | `PolygonLayer` | ✅ | ❌ |
| `@deck.gl/layers` | `GeoJsonLayer` | ✅ | ❌ |
Expand Down
22 changes: 22 additions & 0 deletions modules/layers/src/column-layer/column-layer-uniforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@

import type {ShaderModule} from '@luma.gl/shadertools';

const uniformBlockWGSL = /* wgsl */ `\
struct ColumnUniforms {
radius: f32,
angle: f32,
offset: vec2<f32>,
extruded: f32,
stroked: f32,
isStroke: f32,
coverage: f32,
elevationScale: f32,
edgeDistance: f32,
widthScale: f32,
widthMinPixels: f32,
widthMaxPixels: f32,
radiusUnits: i32,
widthUnits: i32,
};

@group(0) @binding(3) var<uniform> column: ColumnUniforms;
`;

const uniformBlock = `\
uniform columnUniforms {
float radius;
Expand Down Expand Up @@ -42,6 +63,7 @@ export type ColumnProps = {

export const columnUniforms = {
name: 'column',
source: uniformBlockWGSL,
vs: uniformBlock,
fs: uniformBlock,
uniformTypes: {
Expand Down
2 changes: 2 additions & 0 deletions modules/layers/src/column-layer/column-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {Model} from '@luma.gl/engine';
import ColumnGeometry from './column-geometry';

import {columnUniforms, ColumnProps} from './column-layer-uniforms';
import {getColumnLayerWGSL as source} from './column-layer.wgsl';
import vs from './column-layer-vertex.glsl';
import fs from './column-layer-fragment.glsl';

Expand Down Expand Up @@ -234,6 +235,7 @@ export default class ColumnLayer<DataT = any, ExtraPropsT extends {} = {}> exten
defines.FLAT_SHADING = 1;
}
return super.getShaders({
source: source(flatShading),
vs,
fs,
defines,
Expand Down
216 changes: 216 additions & 0 deletions modules/layers/src/column-layer/column-layer.wgsl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

const sharedSource = /* wgsl */ `\
struct Attributes {
@location(0) positions: vec3<f32>,
@location(1) normals: vec3<f32>,
@location(2) instancePositions: vec3<f32>,
@location(3) instancePositions64Low: vec3<f32>,
@location(4) instanceElevations: f32,
@location(5) instanceFillColors: vec4<f32>,
@location(6) instanceLineColors: vec4<f32>,
@location(7) instanceStrokeWidths: f32,
@location(8) instancePickingColors: vec3<f32>
};

fn getRotationMatrix(angle: f32) -> mat2x2<f32> {
let s = sin(angle);
let c = cos(angle);
return mat2x2<f32>(
vec2<f32>(c, s),
vec2<f32>(-s, c)
);
}

fn getOffset(
positions: vec3<f32>,
strokeOffsetRatio: f32,
dotRadius: f32,
rotationMatrix: mat2x2<f32>
) -> vec3<f32> {
var offset = (rotationMatrix * positions.xy * strokeOffsetRatio + column.offset) * dotRadius;
if (column.radiusUnits == UNIT_METERS) {
offset = project_size_vec2(offset);
} else if (column.radiusUnits == UNIT_PIXELS) {
offset = project_pixel_size_vec2(offset);
}
return vec3<f32>(offset, 0.0);
}
`;

const smoothSource = /* wgsl */ `\
${sharedSource}

struct Varyings {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a lot of repeated code, compared to the much more limited #define blocks in the GLSL

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead use string interpolation?

@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>
};

@vertex
fn vertexMain(attributes: Attributes) -> Varyings {
var varyings: Varyings;

geometry.worldPosition = attributes.instancePositions;
geometry.pickingColor = attributes.instancePickingColors;

let isStroke = column.isStroke > 0.5;
let baseColor = select(attributes.instanceFillColors, attributes.instanceLineColors, isStroke);
let rotationMatrix = getRotationMatrix(column.angle);

var elevation = 0.0;
var strokeOffsetRatio = 1.0;

if (column.extruded > 0.5) {
elevation =
attributes.instanceElevations * (attributes.positions.z + 1.0) / 2.0 * column.elevationScale;
} else if (column.stroked > 0.5) {
let widthPixels = clamp(
project_unit_size_to_pixel(attributes.instanceStrokeWidths * column.widthScale, column.widthUnits),
column.widthMinPixels,
column.widthMaxPixels
) / 2.0;
let halfOffset =
project_pixel_size_float(widthPixels) /
project_size_float(column.edgeDistance * column.coverage * column.radius);
if (isStroke) {
strokeOffsetRatio -= sign(attributes.positions.z) * halfOffset;
} else {
strokeOffsetRatio -= halfOffset;
}
}

let shouldRender = select(0.0, 1.0, baseColor.a > 0.0 && attributes.instanceElevations >= 0.0);
let dotRadius = column.radius * column.coverage * shouldRender;
let centroidPosition =
vec3<f32>(
attributes.instancePositions.xy,
attributes.instancePositions.z + elevation
);
let offset = getOffset(attributes.positions, strokeOffsetRatio, dotRadius, rotationMatrix);
let projected = project_position_to_clipspace_and_commonspace(
centroidPosition,
attributes.instancePositions64Low,
offset
);

geometry.position = projected.commonPosition;
geometry.normal = project_normal(vec3<f32>(rotationMatrix * attributes.normals.xy, attributes.normals.z));

let lightColor = lighting_getLightColor2(
baseColor.rgb,
project.cameraPosition,
geometry.position.xyz,
geometry.normal
);

varyings.position = projected.clipPosition;
varyings.color = vec4<f32>(
select(baseColor.rgb, lightColor, column.extruded > 0.5 && !isStroke),
baseColor.a * color.opacity
);

return varyings;
}

@fragment
fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
geometry.uv = vec2<f32>(0.0);
return deckgl_premultiplied_alpha(varyings.color);
}
`;

const flatSource = /* wgsl */ `\
${sharedSource}

struct Varyings {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) cameraPosition: vec3<f32>,
@location(2) positionCommonspace: vec4<f32>
};

@vertex
fn vertexMain(attributes: Attributes) -> Varyings {
var varyings: Varyings;

geometry.worldPosition = attributes.instancePositions;
geometry.pickingColor = attributes.instancePickingColors;

let isStroke = column.isStroke > 0.5;
let baseColor = select(attributes.instanceFillColors, attributes.instanceLineColors, isStroke);
let rotationMatrix = getRotationMatrix(column.angle);

var elevation = 0.0;
var strokeOffsetRatio = 1.0;

if (column.extruded > 0.5) {
elevation =
attributes.instanceElevations * (attributes.positions.z + 1.0) / 2.0 * column.elevationScale;
} else if (column.stroked > 0.5) {
let widthPixels = clamp(
project_unit_size_to_pixel(attributes.instanceStrokeWidths * column.widthScale, column.widthUnits),
column.widthMinPixels,
column.widthMaxPixels
) / 2.0;
let halfOffset =
project_pixel_size_float(widthPixels) /
project_size_float(column.edgeDistance * column.coverage * column.radius);
if (isStroke) {
strokeOffsetRatio -= sign(attributes.positions.z) * halfOffset;
} else {
strokeOffsetRatio -= halfOffset;
}
}

let shouldRender = select(0.0, 1.0, baseColor.a > 0.0 && attributes.instanceElevations >= 0.0);
let dotRadius = column.radius * column.coverage * shouldRender;
let centroidPosition =
vec3<f32>(
attributes.instancePositions.xy,
attributes.instancePositions.z + elevation
);
let offset = getOffset(attributes.positions, strokeOffsetRatio, dotRadius, rotationMatrix);
let projected = project_position_to_clipspace_and_commonspace(
centroidPosition,
attributes.instancePositions64Low,
offset
);

geometry.position = projected.commonPosition;
geometry.normal = project_normal(vec3<f32>(rotationMatrix * attributes.normals.xy, attributes.normals.z));

varyings.position = projected.clipPosition;
varyings.color = vec4<f32>(baseColor.rgb, baseColor.a * color.opacity);
varyings.cameraPosition = project.cameraPosition;
varyings.positionCommonspace = projected.commonPosition;

return varyings;
}

@fragment
fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
geometry.uv = vec2<f32>(0.0);

var fragColor = varyings.color;
if (column.extruded > 0.5 && column.isStroke < 0.5) {
let normal = normalize(cross(dpdx(varyings.positionCommonspace.xyz), dpdy(varyings.positionCommonspace.xyz)));
fragColor = vec4<f32>(
lighting_getLightColor2(
varyings.color.rgb,
varyings.cameraPosition,
varyings.positionCommonspace.xyz,
normal
),
varyings.color.a
);
}

return deckgl_premultiplied_alpha(fragColor);
}
`;

export function getColumnLayerWGSL(flatShading: boolean): string {
return flatShading ? flatSource : smoothSource;
}
Loading