3D tile showing above the ground

I have made tiled 3D model from agismeta shape and export it as cesium 3d.zip
Now I am trying to visualize it but its appear above to the ground. Even after using cesium terrain it still above ground and when I use arcgis terrain data then it perfectly place on ground. But I dont wanna use Arcgis terrain provider use geodetic WGS 84 ellipsoid but Cesium use Cartesian xyz.
I am stucked into this from 2 days need help code is mentioned below:

const viewer = new Cesium.Viewer(“cesiumContainer”, { });

const customTilesetUrl = “http://localhost:8080/tileset.json”;
Cesium.Cesium3DTileset.fromUrl(customTilesetUrl).then((tileset) => {
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);
console.log(“Custom 3D Tileset loaded successfully!”);
}).catch((error) => {
console.error(“Failed to load custom 3D Tileset:”, error);
});

There are some details that may be relevant for analyzing this. Some relevant questions could be:

  • How did you create the tileset?
    • Specifically: Which tool was used for generating it?
  • Are there any transforms in the tileset?
    • Specifically: When you look at the tileset.json, do you see a transform property in the root tile? If so, which values does it contain?
  • Do you know the location that the tileset should have, in terms of longitude/latitude, and most importantly, the height?

You can set the tileset.modelMatrix to adjust for wrong transforms. For example, with

tileset.modelMatrix = Cesium.Matrix4.fromTranslation(
    new Cesium.Cartesian3(10, 100, 1000), new Cesium.Matrix4());

but… it is not clear whether this is the “best” solution here. And it is not clear what the modelMatrix has to be in order to place the tileset on the ground.

  1. I use agisoft metashape and make tiled 3d model then export it in cesium 3d.zip
    2)Yes, tileset.json has root and transform property
    3)Yes, I know the exact location of this tileset
    and modelMatrix did not work

if i add cesium world train than it look like this
const viewer = new Cesium.Viewer(“cesiumContainer”, {
terrain: Cesium.Terrain.fromWorldTerrain({
requestWaterMask: true,
requestVertexNormals: true,
}),
});

// Load the 3D Tileset from the local server
const tilesetUrl = “http://192.168.0.117:8080/tileset.json”;
Cesium.Cesium3DTileset.fromUrl(tilesetUrl)
.then((tileset) => {
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);
})
.catch((error) => {
console.error(“Error loading the tileset:”, error);
});

close to ground but above the ground

Can you provide the transform of the root from the tileset JSON, and the exact latitude/longitude/height that it is supposed to have?

this is the root of my tile set
“root”:{“boundingVolume”:{“box”:[-7.9871543221845229,39.968225686280448,65.906012527341275,206.07334351128929,0.0,0.0,0.0,206.07334351128929,0.0,0.0,0.0,206.07334351128935]},

and this is transform

“transform”:[0.83608746475470763,0.077899201067655882,-0.54303726000438168,0.0,-0.48953783601574752,0.55271892266965517,-0.67442901748954631,0.0,0.2476094876798236,0.82971893252830908,0.50025596909484127,0.0,1476431.7500245189,5240687.8038377818,3311506.67842936,1.0]}}

Right now, the transform contains a transform (of which I’m not sure where it’s coming from, and) that places the tileset a few meters above the ellipsoid surface (314 meters, specifically).

In order to change that, you can either modify that transform in the tileset JSON directly, to place the tileset at the surface. Or you can modify that at runtime.

You can modify it at runtime, by setting the tileset.modelMatrix.

and modelMatrix did not work

The matrix that I showed in the snippet did not fix the issue - it has to be set to the right value, which depends on your data.

To compute that, you can use the following:

// Take the cartesian position 
// This is just the last column of the transform matrix
// that is stored in the tileset JSON
const cartesian = new Cesium.Cartesian3(
  1476431.750024519, 5240687.803837782, 3311506.67842936);

// Compute the cartographic position 
// to obtain the height above the ellipsoid 
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);

// Compute the normal at the given cartesian position
const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(cartesian);

// Compute the translation that is required to place 
// the tileset at the ellipsoid surface
const translation = Cesium.Cartesian3.multiplyByScalar(
  normal, -cartographic.height, new Cesium.Cartesian3());

console.log(translation);

Then you can do

tileset.modelMatrix = Cesium.Matrix4.fromTranslation(
  translation, new Cesium.Matrix4());

and that should fix it.


Alternatively, you can compute the new transform for the tileset JSON:

const originalTransform = Cesium.Matrix4.fromArray([0.83608746475470763,0.077899201067655882,-0.54303726000438168,0.0,-0.48953783601574752,0.55271892266965517,-0.67442901748954631,0.0,0.2476094876798236,0.82971893252830908,0.50025596909484127,0.0,1476431.7500245189,5240687.8038377818,3311506.67842936,1.0]);
const translationMatrix = Cesium.Matrix4.fromTranslation(
  translation, new Cesium.Matrix4());
const newTransformMatrix = Cesium.Matrix4.multiply(translationMatrix, originalTransform, new Cesium.Matrix4());
const newTransform = Cesium.Matrix4.toArray(newTransformMatrix);

console.log(newTransform);

This will print
0.8360874647547076,0.07789920106765588,-0.5430372600043817,0,-0.4895378360157475,0.5527189226696552,-0.6744290174895463,0,0.2476094876798236,0.8297189325283091,0.5002559690948413,0,1476359.118534299,5240429.993773102,3311342.674474269,1

And when you put this into the transform of the root node of your tileset JSON, then it should appear at the right place.

Unless you are enabling Cesium World Terrain. Then you have to use a different height.

okay many thanks for this guide and for your precious time… Will try this