I am new to both shaders and cesium. I’m trying to create a repeating arrow pattern on a polyline geometry using a custom fragment shader.
When zooming in and out, I want the number of arrows to change dynamically (instead of scaling the arrows).
To achieve this i wrote a custom material and am using that on a primitive with a polyline geometry. However i couldn’t get it to work properly.
I would prefer to avoid eventListeners or JS based updates, as i plan to render many lines and want to avoid performance overhead.
Here is what I’ve tried:
I tried using derivatives of st to divide the line into segments. However i had issues with precision when zooming in. I also had issues with overlapping/double arrows on longer lines(i suspect it to be varying derivative magnitudes, since the line appears compressed/stretched looking from different angles, causing inconsistent rate of change).
I tried to use materialInput.positionToEyeEC to determine the number of arrows and divide the line into segments that way. But as far as i can tell polylines don’t have this information.
I tried the method using gl_FragCoord, used in this post. However the issue there still persists. So i adapted that code to use derivatives to calculate the line angle instead. This solved the flipping issue but re-introduced some of the derivative issues.
The last approach is my best attempt so far, but it stil has artifacts and broken arrows on longer lines as can be seen in the image below.
Does anyone know a better way to implement this or have any suggestions on how to fix these issues? I’m also open to non-shader-based solutions, as long as the performance overhead isn’t too high.
Welcome to the community forums and thanks for providing a sample sandcastle! I’ll start off by saying, I’m not 100% sure I understand what the end result you want looks like, but I’d like to help if you can bear with me.
It looks like the procedural arrow-based approach, among other potential issues, has a problem with aliasing. I’m wondering if a more robust (and, frankly, simpler) approach would be to use an image of an arrow as a texture and set the material to repeat.
My end goal is something like what had implemented originally (without the artifacts of course).
About your offered approach, were you thinking of updating the arrow count using a pre-render event listener? Something like in this Sandcastle example maybe?
I am a little concerned about using a pre-render event listener because of the performance impact. Since i am planning to use thousands of these lines. Here is an Sandcastle example with 2k random lines.
Also, when using PNGs how can i prevent a white box appearing before the PNG is loaded onto the line.
The background color would be kind of important. If it is possible i would appreciate hearing about a solution for that in your proposed approach.
were you thinking of updating the arrow count using a pre-render event listener?
I was thinking of using a camera-change event listener. It’s lighter weight, since it only runs when the camera changes and otherwise has no performance impact.
Also, rather than updating every primitive in a listener callback, as your linked example does, it would be less work to update every material. In your example, there would be 2k primitive updates to perform on every render callback, versus 2 material updates to perform on every camera callback. (Sandcastle example)
The drawback of using camera events is that they fire less frequently than render events, so the changes are a little coarser.
All that said, I think the main performance hit isn’t so much the post-render event listener as it is the 2k polylines that are each being treated as individual primitives. You’d probably get a fair bit more mileage by using a PolylineCollection (or multiple - one per material).
Also, when using PNGs how can i prevent a white box appearing before the PNG is loaded onto the line.
This is somewhat of a known issue that I would say has a good chance of being addressed in the near future (I’ve done some work towards it already this month). See issue #1640 and issue #10566.
The background color would be kind of important. If it is possible i would appreciate hearing about a solution for that in your proposed approach.
The only way I think this would work is if you have a black arrow with a white (not transparent) background. Then the Color field on the Material would turn the white background the color you want, while keeping the arrows black. But, again, kind of limiting.
If I have a chance later, I’ll take another look at your shader-based approach - unless you end up liking the image-based approach in the meantime.
This coarseness was the reason that pushed me away from it. I was thinking of using camera movement start/end event listeners to enable/disable the pre-render event listener. Is there a way to enable/disable event listeners besides setting/clearing a boolean and checking that?
Yes it would certainly help but would that not make the arrow count the same for all lines? Since i have lines at different heights this may not be viable for me unfortunately.
I tried it with a Polyline Collection and you were right! That seems to be the biggest performance hit. Using it nearly doubles the fps with 2-3k lines.
Thanks for pointing me to the GitHub issues around the white box! I will look into them.
While not perfect, the background thing definitely helps.
I also have experimented with using shaders to draw the arrows(giving me full control of the colors and eliminating the white box) while using the event listener to update the arrow count. This frees me from having to derive the coordinates, angles etc. . Now I am just dividing st into segments using the arrow count(still needs some work on the arrow count scaling) so i don’t have aliasing issues. I included a Sandcastle demo of this(didn’t include the improvements you mentioned in this demo, tested them locally though).
While looking at side by side lines(which are at the same height), i noticed this issue where depending on the zoom level, the lines kind of start to intersect/merge. Is there a solution for this?
While trying polylines with multiple points(giving it a cartesian3 array with more than 2 points), i encountered another issue. Having longer and shorter lines connected, the arrows get stretched/compressed as can be seen in the image below.
I am currently adding up their lengths and calculating the arrow count that way. Since they share the same material, i can’t think of a way to give them separate values. Because of that, the longer and shorter parts don’t scale well together. While creating separate lines with separate materials for each 2 points solves this issue, they are not connected leading to gaps and overlaps. Is there a solution for this?