Struggling with Stable Arrow Patterns on Cesium Polylines (Pan, Zoom, Perspective Issues)

I am working on drawing arrow patterns on Polylines for route visualization on a map. To achieve this, I have implemented classes that satisfy the Cesium.MaterialProperty interface, where the arrow shapes are drawn procedurally using GLSL (Shader) code running entirely on the GPU.

Project Requirements:

1. Uniform Distribution: Arrow patterns must be drawn at equal intervals along the Polyline.

2. Pan Stability: The arrow patterns must remain fixed on the Polyline and should not “slide” or shift when the map is panned.

3. Perspective Integrity: In a 3D map environment, the shapes of the arrows should not distort or lose their proportions relative to the perspective when the camera angle changes.

4. Dynamic Scaling: The number of arrow patterns should scale dynamically during zoom-in and zoom-out operations.

I have explored several approaches and narrowed them down to two primary solutions. The fundamental difference between these two is the reference system used to place the patterns:

Solution 1: Screen-Space Approach

• Requirements Met: 1, 3, 4

• Technical Details: This method anchors the pattern directly to the screen pixels. It utilizes gl_FragCoord (screen coordinates) as the coordinate system. I capture the Polyline’s orientation on the screen and apply a mathematical rotation matrix to align the screen pixels with the line’s direction.

Solution 2: World-Space Approach

• Requirements Met: 1, 2, 4

• Technical Details: This method anchors the visual pattern to the geographic world. It uses materialInput.st (UV coordinates) as the coordinate system. On the CPU side, an algorithm runs every frame to calculate the camera’s distance and pitch (tilt). It derives the Meters Per Pixel (MPP) based on the camera’s current position and uses this to calculate exactly how many arrow patterns should be rendered on the Polyline for that specific frame.

Goal: I am looking for a solution that satisfies all four requirements simultaneously. I want to achieve this strictly through procedural shaders; I do not want to use external assets like Billboards, PNGs, or textures.

Hey @csmIntern, welcome to the forum!

One thing worth exploring: your two solutions might be complementary halves of a single approach. materialInput.st gives you pan-stable positioning, and GLSL’s built-in dFdx()/dFdy() derivative functions can tell you, per-fragment, how the UV-to-pixel ratio varies due to perspective. The ratio of fwidth(s) to fwidth(t) may give you the local aspect distortion factor you’d need to correct arrow shapes without a CPU-side approximation. It could be worth experimenting with using that ratio to scale one axis of your arrow’s local coordinates within each cell.

If that direction shows promise, your existing CPU-side camera/MPP logic could still drive a u_repeat uniform for dynamic scaling, while the derivative-based correction handles perspective on the GPU. Searching for “procedural anti-aliasing fwidth” should also surface the standard smoothstep + fwidth technique for cleaning up edges in procedural patterns.

Hopefully that helps! This isn’t a trivial problem to solve