Dynamic Point Size Update on Sustained Hover in Cesium3DTileset (with CustomShader)

Hello Cesium Community,

I’m working on an interactive visualization feature for a point cloud Cesium3DTileset and need some guidance on achieving a specific effect.

Goal: I want to increase the pointSize of a single, specific point within a Cesium3DTileset only when the mouse cursor hovers directly over that point for a sustained duration (e.g., 3-5 seconds). When the cursor moves away, the point should revert to its default size.

What I’ve tried and the challenges encountered:

  1. Mouse Picking:
  • I can get the screen position of the mouse cursor using movement.endPosition from a ScreenSpaceEventHandler.
  • Using viewer.scene.pick(movement.endPosition): This often returns a PickedObject whose primitive is the Cesium3DTileset itself, but I’m struggling to reliably get a Cesium3DTileFeature for the individual point under the cursor. For point clouds, I expect pick() to return a Cesium3DTileFeature if an individual point is hit.
  • Using viewer.scene.pickPosition(movement.endPosition): This gives me a Cartesian3 world position on the surface (which could be terrain or the tileset), but it doesn’t provide a direct link to the specific Cesium3DTileFeature or its properties, which I believe is crucial for targeting.
  1. Custom Shaders for Point Size:
  • I’ve implemented a CustomShader to modify pointSize in the vertexShader using vsOutput.pointSize.
  • I pass the currentMousePosition (obtained from pickPosition) as a u_positionMC uniform.
  • Issue: My current shader logic applies the size change to all points, rather than just the single, precisely hovered point. Here’s the relevant shader code I’m using:
const customShader = new Cesium.CustomShader({
    uniforms: {
        u_pointSize: {
            type: Cesium.UniformType.FLOAT,
            value: 10.0, // Example highlight size
        },
        u_defaultPointSize: { // Added default point size uniform
            type: Cesium.UniformType.FLOAT,
            value: 2.0, // Example default size
        },
        u_positionMC: {
            type: Cesium.UniformType.VEC3,
            value: currentMousePosition, // This is updated from JS
        },
        u_highlightRadius: { // Added highlight radius uniform
            type: Cesium.UniformType.FLOAT,
            value: 0.1, // Small radius to simulate 'equality'
        }
    },
    vertexShaderText: `
        void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) {
            // Transform the point's model coordinates to world coordinates
            vec3 pointWorldPosition = (czm_modelView * vec4(vsInput.attributes.positionMC, 1.0)).xyz;
            float dist = distance(pointWorldPosition, u_positionMC);

            if (dist < u_highlightRadius) {
                vsOutput.pointSize = u_pointSize; // Apply highlight size
            } else {
                vsOutput.pointSize = u_defaultPointSize; // Revert to default size
            }
        }
    `,
});

The Core Challenge:

My primary difficulty is how to effectively target only the specific point that the mouse has hovered over for a prolonged period.

  • Precise Point Targeting in Shader: How can I pass information to the CustomShader that allows it to uniquely identify and modify just the hovered point’s size (and potentially color), rather than all points within a general radius? Is there a way to leverage featureId or other per-feature properties within the shader for this purpose?

Any insights, alternative approaches, or examples demonstrating precise point targeting and sustained hover detection for Cesium3DTileset point clouds would be immensely helpful!

Thank you in advance for your time and expertise.

Best regards,
Sarah O.

1 Like

Hi @Sarah_Osay ,

Thanks for your post and for being part of the Cesium community.

For the first approach you outlined, some extra steps would be required. Here is a previous thread discussing the approach How do you pick point in a point cloud? - #4 by Alexander_Johannesen

For the second approach, as you suggested, of you assigned feature IDs to each point, they would be available thought the custom shader API, and you could use them to modify the appearance of the points. cesium/Documentation/CustomShaderGuide at main · CesiumGS/cesium · GitHub

Please let us know if these resources are a step in the right direction or if you have further questions about either approach.
Thanks,
Luke

Hello @Luke_McKinstry ,

Thank you for the warm welcome and for pointing me to those resources!

The link to the custom shader guide is very helpful and confirms that’s the right direction for manipulating the points once I can identify them.

My current challenge is actually one step before applying the shader. When I generate and load my Cesium3DTileset, the points don’t appear to have any feature IDs assigned. For example, when I inspect a point in the viewer, feature.featureId is undefined. This leads me to believe I’m missing a key step during the tiling process itself.

Could you offer some guidance on how to properly embed these per-point feature IDs into the .pnts files when creating the tileset? I’m trying to figure out if there’s a specific property name I need to use in my source data (e.g., in a LAS or LAZ file) or a particular configuration/flag I need to use with the point-cloud-tiler to ensure these IDs are generated correctly.

Any advice on that data preparation or tiling step would be greatly appreciated.

Thanks again for your help!

Sarah O.

Hi @Sarah_Osay ,

Thank you for your follow up question regarding passing metadata from a LAS or LAZ file into a 3D Tileset.

A thread was opened on the same topic here.

Is the information shared there helpful to you understanding how you setup metadata so that is passes through to the generated 3DTiles? If not please let us know and we would be happy to further assist.

Thanks,
Luke