3D tile point cloud looks bad in new version

Recently I’ve upgraded the Cesium version in my website from 1.92.0 to latest (1.129.0) and I’ve noticed the point clouds look bad - very dark, with visible differences between tiles.

Here’s an example:

Previous version


New version

Here are the params I use when creating the 3D tiles:

{
    maximumScreenSpaceError: 4,
    pointCloudShading: {
      attenuation: true,
      backFaceCulling: true,
      normalShading: true,
      eyeDomeLighting: false,
    }
  }

I tried to change them around and add others, but I couldn’t find a configuration that fixes this issue.

The point clouds I show in the website are generated from an external system that I have no control over, so I can’t change the params they are generated with, and I need to support the existing ones. I do know that they are 3D Tiles Version 1.0 and that the tiles are PNTS.

I managed to read the config from the header of a tile and that’s what I got:

{
  "NORMAL": {
    "byteOffset": 0
  },
  "POINTS_LENGTH": 3882,
  "POSITION": {
    "byteOffset": 0
  },
  "RGB": {
    "byteOffset": 0
  },
  "extensions": {
    "3DTILES_draco_point_compression": {
      "byteLength": 34755,
      "byteOffset": 0,
      "properties": {
        "NORMAL": 1,
        "POSITION": 0,
        "RGB": 2
      }
    }
  }
}

Also, here’s the tileset.json file:

{
    "asset": {
        "version": "1.0"
    },
    "geometricError": 10000.0,
    "properties": {
        "Height": {
            "maximum": 74.391155375147719,
            "minimum": 33.076372730373201
        },
        "Latitude": {
            "maximum": 0.5665337080065066,
            "minimum": 0.56650932125666587
        },
        "Longitude": {
            "maximum": 0.61188800165443546,
            "minimum": 0.61186105843497185
        }
    },
    "root": {
        "boundingVolume": {
            "box": [
                9.289661465426974,
                -21.700411639009616,
                54.550000004788416,
                146.93283040338724,
                0.0,
                0.0,
                0.0,
                146.9328304022842,
                0.0,
                0.0,
                0.0,
                146.93283040814543
            ]
        },
        "children": [
            {
                "boundingVolume": {
                    "box": [
                        -64.176753736266676,
                        -95.166826840151742,
                        -18.916415199284273,
                        73.466415201693621,
                        0.0,
                        0.0,
                        0.0,
                        73.466415201142098,
                        0.0,
                        0.0,
                        0.0,
                        73.466415204072717
                    ]
                },
                "content": {
                    "uri": "Data/b0/tileset.json"
                },
                "geometricError": 36.733207601151399
            },
            {
                "boundingVolume": {
                    "box": [
                        82.756076667120567,
                        -95.166826840151742,
                        -18.916415199284287,
                        73.466415201693621,
                        0.0,
                        0.0,
                        0.0,
                        73.466415201142098,
                        0.0,
                        0.0,
                        0.0,
                        73.466415204072717
                    ]
                },
                "content": {
                    "uri": "Data/b1/tileset.json"
                },
                "geometricError": 36.733207601151399
            },
            {
                "boundingVolume": {
                    "box": [
                        -64.176753736266676,
                        51.766003562132482,
                        -18.916415199284273,
                        73.466415201693621,
                        0.0,
                        0.0,
                        0.0,
                        73.466415201142098,
                        0.0,
                        0.0,
                        0.0,
                        73.466415204072717
                    ]
                },
                "content": {
                    "uri": "Data/b2/tileset.json"
                },
                "geometricError": 36.733207601151399
            },
            {
                "boundingVolume": {
                    "box": [
                        82.756076667120567,
                        51.766003562132482,
                        -18.916415199284287,
                        73.466415201693621,
                        0.0,
                        0.0,
                        0.0,
                        73.466415201142098,
                        0.0,
                        0.0,
                        0.0,
                        73.466415204072717
                    ]
                },
                "content": {
                    "uri": "Data/b3/tileset.json"
                },
                "geometricError": 36.733207601151399
            }
        ],
        "content": {
            "boundingVolume": {
                "box": [
                    14.455268784296049,
                    -25.341447353324043,
                    -49.551336383013812,
                    64.283113301481919,
                    0.0,
                    0.0,
                    0.0,
                    56.803744370609621,
                    0.0,
                    0.0,
                    0.0,
                    19.720782108612788
                ]
            },
            "uri": "Data/a.pnts"
        },
        "geometricError": 73.466415202302798,
        "refine": "REPLACE",
        "transform": [
            0.58364998528952405,
            0.072613298422130865,
            -0.80875212739367608,
            0.0,
            -0.43897134058359788,
            0.86612372205684529,
            -0.23902690274660651,
            0.0,
            0.68312287097860458,
            0.49452705382372858,
            0.53738825460031392,
            0.0,
            4409617.4683041135,
            3094320.1357291178,
            3403576.0469450117,
            1.0
        ]
    }
}

Thanks in advance,
Shay

EDIT:
Here’s a sandcastle example.

Since I wrote this post until it got approved I was able to fix it using a custom shader to get rid of the lighting, which seems to cause the issue:

tileset.customShader = new Cesium.CustomShader({
  fragmentShaderText: `
    void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
      material.diffuse = fsInput.attributes.COLOR.rgb;
    }
  `,
  lightingModel: Cesium.LightingModel.UNLIT
});

But if anyone has a better idea, I’d love to hear about it :slight_smile:

I’m a bit surprised:

  1. For me, the sandcastle from the first post looks exactly like the screenshot in the second post (i.e. it is rendered properly, and not too dark)
  2. I didn’t find anything in the change logs that could dedicatedly explain the difference

The second point is to be taken with a grain of salt: The jump from version 1.92 to 1.129 is huge, and a lot of changes have happened in the meantime. Some of them might explain subtle visual differences. But that image that you posted is not something like a ~“subtle lighting change”. It looks really wrong.

Was that image taken with some special (maybe particularly old) hardware? We should try to rule out that this is some sort of regression. Even when the CustomShader (which essentially implements “unlit” rendering) solves the issue, this should not be necessary.

Hey Marco, thanks for the response!

Sorry for the confusion, I added the wrong sandcastle link.. I updated the original one.

I’m using a rather new 2025 Macbook Air BTW, although I tested the (correct) sandcastle on a Windows computer with the same results.

Yes, I can see the effect now. It strongly depends on the time of day (i.e. the location of the sun), as selected with the slider at the bottom. But it looks very wrong in nearly all configurations.

One … shallow question may now be: What is the difference between the sandcastle that you posted earlier, and the new, updated one? (The point is: The previous one did look correct. Knowing the difference might help us to figure out why the new one looks wrong…)

That was an Ion processed asset from the original LAS file that was used to generate the “bad” point cloud.
Unfortunately, since I need to support existing point clouds in my app, that won’t help me.

Is there a different, more appropriate, way to prevent lighting to affect the point cloud other than the custom shader I’ve used?

I still have to ask for some clarification:

  • These sandcastles have been using different data sets - is that correct?
  • Both data sets have been created with Cesium ion - is that correct?
  • The data sets have been created at different times
    • Specifically: The one that was working has been created a long time ago. The one that was too dark has been created only recently. Is that correct?

The point is: If all this is correct, the the difference in the appearance is not caused by CesiumJS or the different versions of CesiumJS. Instead, it would be caused by differences in how Cesium ion generates the data.

That would be important to know.


For now, this shader should be a reasonable solution, but some details still have to be investigated: The fact that the appearance ~“suddenly changed (with no apparent reason)” is not ideal, and we still have to find the actual reason for that. But even if the reason is some change in Cesium ion, then restoring the original behavior in clients like CesiumJS should be easier. (Roughly something like some tileset.useOldPointRendering=true; or something like that - a CustomShader is a huge hammer for a tiny nail here…)

I’ll try to give more context, hope it’ll be more clear:

  • The “bad” data set (the one that is shown in the sandcastle now) is a new Ion asset created from a zip file containing the PNTS files of the point cloud that were generated a long time ago, by a different system that is not Cesium Ion. They are the files I use in my app currently, and they are shown badly.
  • The “good” data set (the one that was shown in the sandcastle previously) is a new Ion asset created from the original LAS file that generated the “bad” PNTS files.

So in short, it’s not a Cesium Ion issue. I’ve only used it to simply show the issue in a sandcastle. I don’t use Ion in my app, not for generating and not for hosting data sets.

Thanks, I’ll try to confirm locally that the data that is currently used in the sandcastle indeed was rendered properly in CesiumJS 1.92 (I’ll have to check that old state locally for that), and try to figure out what could explain the differences here.

1 Like

I had another short look at this, but … that wasn’t entirely conclusive.

There are some differences when rendering the point cloud from the given Sandcastle with 1.92. Here is a comparison of that asset in 1.92 and 1.129, first in bright light (at noon), and then “at night”:

Cesium Point Cloud Brightness

During the day, there is no strong difference in the lighting.

But during the night, it looks much darker (with a stronger tint of ‘dark blue’) in 1.129 than it did in 1.92. I assume that this was what you observed.

Some of this could probably be explained with recent changes in the “environment based lighting” (although I’d have to do a bisect to see whether there is a specific change that made things darker).

What’s much more surprising for me right now is that the “time-of-day” does have such a strong effect at all, even when setting normalShading: false. I’d expect that to cause the normals to be ignored (and nothing else should affect the brightness of the points, FWIW). I’ll check if someone has an idea…

1 Like