1 million billboards at 60fps

Hello,

I was asked by an employer to be able to display 1 million billboards on screen at 60 frames per second. Assuming a best case scenario of 1 texture being reused by all billboards, is this a metric that is acheivable?

If not, do you have any suggestions on more effective solutions for displaying images at this volume on the globe that can be picked? Thank you for your thoughts.

As metrics goes, it sounds like a bad one as well as probably not an achievable one. A better metric would be “with what we’re trying to do, can it be done through the average computer of our average user”.

Basically, I would question why anyone needs to display 1 million billboards, as it stands on its own it makes absolutely no sense, neither from a systems perspective nor a human one. What I always suspect with these things is that you can always trim it down to “what we’re trying to do”, which usually ends up in some actual functional application; display some geospatial information of some context on a globe using a browser through an application of another context." And very often you can ask questions like;

  • What info do you want to display? (live data, changing data, full data / part data, selectable data, etc.)
  • When do you want to display it? (all the time, at acceptable intervals, at acceptable proximities, at acceptable cluster definitions, distances, etc. [see below])
  • What’s the geospatial context to displaying the info (distance to camera, proximity to other pieces of information, importance of selections or near-objects, etc.)

Very often a paradigm like “1 million billboards” have an answer that is a filtered, sorted, interfaced, systematized and optimized answer that’s a far better metric.

Cheers,

Alex

2 Likes

Hey Alexander,

Thank you for the detailed response. After reading your comment I went and spoke with the employer and hashed some requirements out that answered your questions.

It looks like 1 million billboards being displayed on the globe at any given time is not strictly required, billboards only need to exist for data points that are being viewed when zoomed in at a relatively low altitude.

The data points will be coming in as live data, with up to 2000 being received per second. It would be best to assume that 2000 billboards will need to be created per second, but I think this is something cesium can handle if you keep the total number of billboards on the viewer below 100k.

So it looks like I will be attempting a filtered solution, based on adding and removing billboards based on what area of the globe we are looking at. Do you think that sounds like a feasible approach?

Hiya,

Sure, sounds reasonable, but as with all these things it always comes down to the spec of the user’s machine, as it all runs in the browser. No GPU or special graphics card or similar will make Cesium seriously slow, etc. Memory consumption, blah, blah.

I would seriously consider not using the Cesium APIs per second, but do a quick calculation per incoming point as a distance to camera before pushing it through in a clustered entityCollection. Slap some display rules (also distance-based) on there to not show points too far away (you can calculate the width of the camera view to the distance between things as well) and only show points within 50% of that width in distance, for example. There’s many more ways of optimizing things, but it’s a good starting point.

Let us know how you go!

Cheers,

Alex

2 Likes

Hey Alex,

Thanks again for the tip. filtering based on the camera location seems to have really improved performance! Here is a working example. You’ll notice the billboards only begin rendering when you reach a certain height threshold:

However, I am running into an issue I am having a hard time solving. If you zoom in and pitch the camera to view the horizon, you will notice that if you look around, the rectangle seems to not encompass one of the four cardinal directions (I’ve noticed it happening when looking South East). Here’s an example showing what I mean, where you can see the cutoff line of the rectangle:

It is much worse if you keep looking left, but I wanted to be sure to include the cutoff line.

Fortunately I have found a way to calculate the horizon in different way that doesn’t have this problem, as you can see here:

However, this has the opposite problem that the horizon is too wide, and when I try placing billboards inside it, I hit performance problems because too many are being rendered.

Do you know of a way I can shrink the size of the rectangle in the second example so it covers a smaller distance? Say, by a factor of 1/2 as an arbitrary amount? I’ve tried using the subsection method in the rectangle API, Rectangle - Cesium Documentation but I could not figure out how to use it, or if I am using it for its intende purpose.

Thank you for any advice you can offer!

Actually after doing more testing, I found a simple solution to shrink the rectangle by using the center:

    const rect = Cesium.Rectangle.fromCartesianArray([
      entityW._position._value,
      entityS._position._value,
      entityE._position._value,
      entityN._position._value,
    ]);

    const rectCenter = Cesium.Cartographic.toCartesian(
      Cesium.Rectangle.center(rect)
    );

    const smallWest = new Cesium.Cartesian3(
      (rectCenter.x + entityW._position._value.x),
      (rectCenter.y + entityW._position._value.y),
      (rectCenter.z + entityW._position._value.z)
    );
    const smallSouth = new Cesium.Cartesian3(
      (rectCenter.x + entityS._position._value.x),
      (rectCenter.y + entityS._position._value.y),
      (rectCenter.z + entityS._position._value.z)
    );
    const smallEast = new Cesium.Cartesian3(
      (rectCenter.x + entityE._position._value.x),
      (rectCenter.y + entityE._position._value.y),
      (rectCenter.z + entityE._position._value.z)
    );
    const smallNorth = new Cesium.Cartesian3(
      (rectCenter.x + entityN._position._value.x),
      (rectCenter.y + entityN._position._value.y),
      (rectCenter.z + entityN._position._value.z)
    );

    const smallerRect = Cesium.Rectangle.fromCartesianArray([
      smallWest,
      smallSouth,
      smallEast,
      smallNorth,
    ]);

it would be nice to be able to make the amount the rectangle shrinks configurable, but that is beyond my math abilities

Thanks for the write-up, looks good. My math is not really good, either. For some of these issues instead of calculating things like a view area and find items within it, you can rely on other who do better math, like the distance rules for billboards and such (which are properties you can read) that look at distance to camera. Cluster is good for this where you alter the size of the clusters based on, say, distance between camera and the globe, which I’ve done with great results.

Cheers,

Alex

the distance rules for billboards and such (which are properties you can read) that look at distance to camera.

Can you expand on this a bit? I am not sure exactly what you are referring to. Perhaps the billboard API’s distanceDisplayCondition?

I just tried only rendering billboards that are from a certain distance from the camera here
https://sandcastle.cesium.com/#c=rVj/b9o4FP9XfOikmSszlGk3iXbTTRzTTtq66ZjWH0rVmcQkVkOMbIcWKv73e7ZjJwFKV25T18R+X/z5vPf87HRJJVpydsckeotydoeGTPFiTr7bOTxpRXY8FLmmPGdy0mqfTfIlWEV0ziQFK2dOVMRyRtxsqcKyjC+U4PG2VpKJKSNBXKpLahT/fPXmtNfrnaFud0SlThFdgseEGTEvFKjWPcVsWiTjVNx9kLCy+srkmEUiN460LBh4LqHkmuvVRQXETnCmCI1j/DDJEcrolGUDZN8RUuBzgGY0U6xjZjbwe+O5O2+Xv9Tb+Jd6G/0Cb5O820VzesuQKCSai+gWxVTTSQ4BVhrFQg+FkLEqK+czXWBjNhMS4YxpxEEAeeToHL3u9WxO+clJ263rfESQYZFIukib5TcM8zzCDuZnqlMiaR6LOW6jPxDuo5cIv+y30Qk6Nb9e9jvP1eyZh8FcxwMIaA5w9kAhWgy9Bq6wOw8hHkQxjX/A8PcHvvnRQQ81mp3aEjbImzLOEc2iIqOaoRepkHwt8hdI5BFDC9iaM1PcofKjDFJBRP6NwwOSOlpCij9xpWFDSBwcfXRuXCZnRR5pLnK0LcY2Hy4cNnMuEFe3bNVBS5oVTF0jMavYtX3dQB1Bkh8h1ynrEG1MIVo3Z97MVGkMcKmh14izNX1F5jTJuS5ihndlqphqSSON97QdshCKG5YNIFtl5fzgdrsdAPEZDnjO0amt1V470HQavzlCtemS4YGN5v95XIMarrp8yrNsKqiMB6hhBwtDLNgATVqEdO276jouN59EIm4E9MaMrsgiTyatTtM0EpmQg0DdjMjlx3++jcgd1+n7bJFS3COv21tmdzzW6eDN663plPEk1YN+rz69qZQ2VTibG8EW0sPBIqnZbsqXDRweilXBgATsxB/tBF6yOUTEa57VNEOu8iLL6oL/A7ZC656bcjuHzRZLsfiSe0A1Z+Um4jOEt+fc/ohqXQdQh6OSBPVvotEkKzdnlZdM5IndSGMteZ5Um822SC3+ZolkTOH6aiQYtUHjA79nMfaE++6lsQTVz1+htDm4gHtzkQsbG/wHohbETsil0NDcsHuwTjmWHUTzJGNljLvdcnqACug0aMkiLbzOwFw0aK6c4nB4CWEUt4bcTIp5qYs0XyAt/GhKFTP69hpjXce7B5rvPGde02TWxikSCjt8Qaa8TPF8WwY1O4Y+vr95grAMp49BfSTL0SPQqvOwYiLtWoaPMi/fLWPsMYQIV2ZLuIUt3t9ztRdfaOAHQLoVfwYqZKhQJjdfVzqliZBUIZ0yAZ0gRFnMF6db6KNOhXIL+EHwkRRK4X0BDe5+EjUc67aApgKuuHC+AuiqhAzk/hZk1Wnic7Al04XMEZw4Tk3hq2YMHX336F+HG0fYL40lXEJdTZdbxcDRbL54opyNCrn3kOHS5ZyQ+0q62pWuKul6V7quMzRKO+BrtJ2NqqGWTBUZHOr6Uei9DjI/VQG4G6sZ3aUcoGBzdy09k4zliU5Dlw7ugfbJW691xa9LznWN1ZbGakdjvaWxLjW4mT/1rS5EIxjuhCQxl0DI/7/gy7aN8vC59OfX2L+M/MtFdaa6y58E26r8gydi+l8I33sp6QpfectyFXLjWzW5sde+TlNh/JTC6CmFi8cUrqtTo6IxBCP7bfvUbd4vskM6sh6wcdbePp3cOmpOs+ySqccLzTvHFSRTNY8GjdxXt7K6zeqQzWq/zfqQzbrdPNFrhMai0OmRjMZHMBofwWj8LEYjenSKRkcQGh1BaPQsQhdCHp2iiyMYXRzB6OIwI98HbVd7VsMJu67TmLFl25wyeW/O2MDV2wb00FanNWnZGPv/CJ0rvcrYO2/7F58vwBIVMsPwPWaOJPMlrbrTIrplmkRK+Sydd+um5zFfIh6/3fPnNBRlVCmQzODDZMzXbNJ6d94F/R3TTMC9NE++uE8+o5aevvvkJgkh510Y7rfUQmRTKrc8/wc

but it actually seems like the rectangle evaluation is running more smoothly by about 10fps

Yeah, distanceDisplayCondition, pixelOffsetScaleByDistance, scaleByDistance, translucencyByDistance, but in a combination with cluster. I think show=false on an entity switches off its change detection, but I could be wrong about that. I’m about to write some plugins for entity clustering in the next week or so, so I’ll keep this thread in mind while doing so. I was thinking of a large cluster value based on height above the globe, and it might be really effective with a rectangle selector and filter.

Cheers,

Alex

1 Like