`PathLayer` with `billboard:true` has broken `vPathPosition` interpolation
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 14.6k
- Forks
- 2.3k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 42
Description
This issue originated from a Slack discussion
I originally wanted to create a PR, but while trying to fix it, I was unsure how to implement my hack properly.
Description
The PathLayer with billboard: true has bad interpolation of vPathPosition.
Even splitting the line into smaller segments to avoid precision issues is not enough to fully mitigate this problem:
I noticed that it looked like perspective correction errors, so I added if (billboard) { gl_Position /= gl_Position.w; } to the vs:#main-end - which worked:
I assume the perspective division leads to precision issues when looking along the line.
The problem only seems to occur when pitching the map, and only when looking along the line.
However, I'm not sure if my proposed change is correct, because deck.gl explicitly does its own nextPositionScreen.w multiplication after lineJoin (and my hack just undoes that).
I wonder if there’s a better way, like multiplying vPathPosition by W? However, deck.gl calculates vPathPosition and geometry.uv as part of lineJoin, so I'm not sure where to do this modification cleanly.
I could also imagine that the depth values see the same interpolation problem, so my proposed solution of keeping W=1.0 might be better after all.
I have some trouble wrapping my head around the existing transformation code in deck.gl, so I'd appreciate if someone else could look into it. Specifically, the existing code was written by @Pessimistress.
Expected Behavior
A line which is colored if abs(vPathPosition.x) < 0.5 should remain colored near the center, even when the camera moves.
However, in practice, the colored portion of the line keeps shifting around.
Repro Steps
The video above was made with these settings:
getWidth: 50.0,
widthUnits: 'pixels',
widthScale: 1.0,
billboard: true,
I also had this hack to visualize the problem:
if (vPathPosition.y / vPathLength < 0.01) {
discard; // Cut off 1% of line segment, so it's clear where each segment starts
}
gl_FragColor = vec4(vec3(1.0-abs(vPathPosition.x)), 1.0); // Center of line should be white, rest black
A full test-case is this (based on the PathLayer codepen that is reachable through the docs):
/*
* https://deck.gl/docs/api-reference/layers/path-layer
*/
const {DeckGL, PathLayer} = deck;
class DebugPathLayer extends PathLayer {
getShaders() {
return {
...super.getShaders(),
inject: {
'vs:#main-end': `if (billboard) { gl_Position /= gl_Position.w; }`, // Hack to undo perspective (Disable this line to see the bug)
'fs:#main-end': `gl_FragColor = vec4(vec3(1.0-abs(vPathPosition.x)), 1.0);` // Force a white center, black edge
}
}
}
}
const layer = new DebugPathLayer({
id: 'PathLayer',
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-lines.json',
/* props from PathLayer class */
// billboard: false,
// capRounded: false,
getColor: d => {
const hex = d.color;
// convert to RGB
return hex.match(/[0-9a-f]{2}/g).map(x => parseInt(x, 16));
},
billboard: true,
getPath: d => d.path,
getWidth: d => 50,
// jointRounded: false,
// miterLimit: 4,
// widthMaxPixels: Number.MAX_SAFE_INTEGER,
widthMinPixels: 2,
widthScale: 50,
// widthUnits: 'meters',
/* props inherited from Layer class */
// autoHighlight: false,
// coordinateOrigin: [0, 0, 0],
// coordinateSystem: COORDINATE_SYSTEM.LNGLAT,
// highlightColor: [0, 0, 128, 128],
// modelMatrix: null,
// opacity: 1,
parameters: {
depthMask: false
},
pickable: true,
// visible: true,
// wrapLongitude: false,
});
new DeckGL({
mapStyle: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
initialViewState: {
longitude: -122.4,
latitude: 37.74,
zoom: 11,
maxZoom: 20,
pitch: 30,
bearing: 0
},
controller: true,
getTooltip: ({object}) => object && object.name,
layers: [layer]
});
Environment
- Framework Version: deck.gl 8.4.20
- Browser Version: Browser independent (but Chrome 92.0.4515.131 used for tests)
- OS: Probably OS independent (but macOS Big Sur 11.5.1 used for tests)
Logs
None
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with modules/layers/src/path-layer/path-layer-vertex.glsl.js, especially the lineJoin logic around lines 150–154 and the nextPositionScreen.w multiplication near line 215. Reproduce the issue using the supplied PathLayer example with billboard enabled and the vPathPosition fragment-color injection. Done means vPathPosition interpolation remains stable while pitching the camera and viewing along the line, without relying on the proposed hack.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- computer-graphics, data-visualization
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100