Skip to content

Fix picking of labels by updating based on viewport instead of render…#457

Merged
lewey-foxglove merged 2 commits into
mainfrom
lewey/fg-13915-clipped-view-fix
Jun 17, 2026
Merged

Fix picking of labels by updating based on viewport instead of render…#457
lewey-foxglove merged 2 commits into
mainfrom
lewey/fg-13915-clipped-view-fix

Conversation

@lewey-foxglove

Copy link
Copy Markdown
Contributor

Changelog

None

Docs

None

Description

Labels with sizeAttenuation=false use screen-space offsets so they keep a consistent pixel size. The WebGL shader was deriving those offsets from the renderer size, but that is the full render target size rather than the active viewport. In clipped views, split views, or other custom viewport rendering, the displayed label and its picking material could therefore use the wrong dimensions and drift out of alignment.

This updates the WebGL label material to refresh its uniforms from renderer.getCurrentViewport() and the renderer DPR before each render. The visible label mesh also forwards its onBeforeRender call to the picking material so picking receives the same current viewport state. The WebGPU material now uses the same pre-projection viewport math with viewportSize and screenDPR, keeping the two render paths consistent even though WebGPU picking is not implemented yet.

Validation:

  • yarn test
  • yarn typecheck
  • yarn lint:ci
BeforeAfter
Screenshot 2026-06-16 at 1 58 26 PM

Picking bounds for non-size-attenuated billboard labels were computed from the full renderer size and could be offset or scaled incorrectly inside a clipped viewport.

Screenshot 2026-06-16 at 1 57 38 PM

Label rendering and picking use the active viewport dimensions and device pixel ratio, so screen-space label offsets match the clipped view being rendered.

Fixes: FG-13915

… target size. Also make the WebGPU path the same math, even though it doesn't handle picking yet.
@linear-code

linear-code Bot commented Jun 16, 2026

Copy link
Copy Markdown

FG-13915

@jtbandes jtbandes left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for working on this -- I think these changes are probably fine, although I can't say I recall how all the coordinate systems work to verify the math (is vertexPos really in NDC? iirc NDC is the final coordinate system that the GPU expects your gl_Position value to be in?)

One thing that would help quite a bit is updating the storybook for this repo -- since it seems like the primary change here is to properly support custom viewports, I think it would be cool to add a couple of sliders that tweak the renderer viewport in the storybook. Then we should be able to quickly move the viewport around and verify that the labels look correct.

uLabelSize: { value: [0, 0] },
uCanvasSize: { value: [0, 0] },
uPixelRatio: { value: 1 },
resolution: { value: new THREE.Vector2(0, 0) },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any reason not to follow the pattern of the other uniforms here with the u prefix and array type? Not really a big deal, but for consistency:

Suggested change
resolution: { value: new THREE.Vector2(0, 0) },
uResolution: { value: [0, 0] },

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So the app does actually call "pickingMaterial.uniforms.resolution.value.copy(pickResolution);" in packages/viz/src/panels/ThreeDeeRender/Picker.ts .

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Interesting, so why was it working before? or you're saying it wasn't?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In the app code there are numerous references to: X.resolution.value.copy(pickResolution);
I'd rather keep consistency with this so that we don't have versioning issues between these packages.

Comment thread src/Label.ts
Comment on lines +94 to +96
this.mesh.onBeforeRender = (renderer, scene, camera, geometry, _material, group) => {
this.pickingMaterial.onBeforeRender(renderer, scene, camera, geometry, this.mesh, group);
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

shouldn't this happen automatically when the picking material is used for rendering? Or what is it about the way we've implemented picking that makes this not get called automatically?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is legit as there are cases were the uCanvasSize wasn't being updated (I hit this), and the scale invariant text was suddenly stretching as the panel changed size.

Anyway working on your other suggestions...

@lewey-foxglove

Copy link
Copy Markdown
Contributor Author

@jtbandes , added picker system and visualization to the storybook, and looks like "resolution" is actually what is used in the app code.

Screenshot 2026-06-17 at 6 44 05 AM

Comment thread src/LabelPool.stories.tsx
lines.push(`viewOffset ${formatRect(pickResult.viewOffset)}`);
}
if (pickResult.pickViewportRect != undefined) {
lines.push(`pick viewport css ${formatRect(pickResult.pickViewportRect)}`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

seeing "CSS" info in the debug textarea stood out to me...maybe could use a better label?

uLabelSize: { value: [0, 0] },
uCanvasSize: { value: [0, 0] },
uPixelRatio: { value: 1 },
resolution: { value: new THREE.Vector2(0, 0) },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Interesting, so why was it working before? or you're saying it wasn't?

@lewey-foxglove
lewey-foxglove merged commit c700f36 into main Jun 17, 2026
2 checks passed
@lewey-foxglove
lewey-foxglove deleted the lewey/fg-13915-clipped-view-fix branch June 17, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants