How to get the height of terrain in the VertexShader of CustomShader?

My idea is roughly like this: if there is a model with a completely flat bottom, but the terrain is not flat, I want to use CustomShader to adjust the position of vertices, so that the bottom of the model is close to the surface of the terrain.

1 Like

Hi Peter @ptrgags, Do you have any thoughts on that?



Interesting question! I think this will be a bit tricky, as CustomShader is related to glTF and 3D Tiles rendering, while terrain is a different system in CesiumJS. However, I have some ideas that might help you find a workaround.

First, While the CustomShader doesn’t have direct access to the terrain, note that you can pass in texture uniforms either from an image or a procedurally generated texture (see the Custom Shader Models Sandcastle for examples of both). So one thing that might help is to pass in terrain heights as a texture.

As for acquiring terrain heights, CesiumJS does provide a method for sampling terrain heights, see the Terrain Sandcastle.

The tricky thing will be how to turn terrain heights into vertex offsets, as the terrain and your model’s vertices are in very different coordinate systems:

  • Sampling the terrain produces a grid of Cartographic coordinates (i.e. (lon, lat, height)). Note that the heights are relative to the WGS84 ellipsoid and measured in meters.
  • CustomShader provides positionMC (model coordinates) for positions. The quality of this highly depends on the position values stored in your gTF model. I find that east-north-up model coordinates are the best if you’re going to use CustomShader. This way +z points up and not at some oblique direction (CesiumJS uses a coordinate system where +z is parallel to the earth’s axis, which is often inconvenient in the shader)

Here’s a rough sketch on how I would attempt this:

  • If you’re able to modify the model, I would recommend these things for best results:
    • the model should be in east-north-up coordinates so that the model appears right-side up when rendered and in the shader +z is the “up” direction.
    • Make sure the model’s origin is at the bottom where it’s flat, this will help with positioning it.
    • Take a note of the coordinates of a rectangle around the bottom of the model, this will be helpful later.
  • In CesiumJS, apply a model matrix to position the model on the globe (see Transforms.eastNorthUpToFixedFrame. There are many Sandcastles in the gallery that use this). Something like this:
// Set the height to 0 so the bottom of the model is exactly at the height of the ellipsoid
const center = Cesium.Cartesian3.fromDegrees(lon, lat, 0);
const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);
  • Sample the terrain in a rectangle that lines up with the base of your model, and create a procedural texture of just the height values. This step might be a little involved to set up, but you have all the information you need:
    • The coordinates of the base of the model so you know how far away from the origin in meters
    • The terrain Sandcastle I linked above will provide some of the framework for setting up a grid of samples.
    • See classes in CesiumJS such as Cartographic, Cartesian, Ellipsoid, and/or EllipsoidTangentPlane for the math of computing sample positions (which may require converting between ENU space and Cartographic space)
  • Pass the procedural texture (essentially an elevation map) to the shader following the sandcastle example
  • Using the fact that your model is in ENU coordinates, positionMC.xy will be very helpful for sampling the elevation map. You just need to scale/shift the coordinates so they’re in the range [0.0, 1.0]
  • Those height offsets are in meters, so you can add vsOutput.positionMC.z to shift it up

The above is just a rough algorithm, you may need to tweak some steps since I haven’t tried this myself. Let me know if it helps your use case!


1 Like

Thank you very much for sharing these ideas, which will be of great help to me. However, this issue is indeed more complex than I imagined, and I need more time to try it out. Thank you again!