Cesium realizes architectural night scene mapping

Online preview
Demo source code

The new version of Cesium(1.87.0) supports CustomShader and can write custom GLSL code for cesium 3dfileset. Before that, to modify the style of 3D Tileset, you can only use the style attribute. The most you can do is to make different buildings display different colors according to the height.

The basic usage of CustomShader can be viewed Official documents , I will not repeat here, but mainly introduce how to use CustomShader to realize architectural mapping.

Roof coloring

First of all, the roof will not be pasted with the same drawings around, but will be uniformly treated as dark color.

There is no direct attribute indicating which wall the current point is on, but reading the document, we can find that Cesium provides the normalMC attribute, that is, the unit normal vector of the plane where the point is located. The roof is generally parallel to the ground. Compare its normal vector with the normal vector of the ground. If the included angle is small, it can be regarded as the roof.

The FragmentShader code is as follows (normalMC is not available in the fragment shader and needs to be passed through varying in the vertex shader. The process is omitted here):

void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
  if (dot(vec3(0.0, 0.0, 1.0), v_normalMC) > 0.95) {
    material.diffuse = vec3(0.079, 0.107, 0.111);

Do dot (dot product) for two vectors to get the cosine value of the angle between the two vectors, and then do the inverse cosine to get the angle. However, the inverse cosine operation is relatively slow, and there is no need for accurate angle value, so judge the size of the cosine value

Roof coloring complete code

Surrounding map

After processing the roof, process four sides. The key of mapping is to determine a two-dimensional coordinate, and then directly call the texture2D method to obtain the corresponding color on the picture. Visually, the Z coordinate in the Shader must be the ordinate of the corresponding picture. The problem is how the X and Y coordinates in the Shader correspond to the abscissa of the picture

In fact, we can't use single X and Y coordinates here. In this way, one side of many buildings will have a single color, which can't achieve the effect of mapping. Finally, I used the normal vector attribute to calculate their angle with vec3(1.0, 0.0, 0.0) & vec3 (0.0, 1.0, 0.0). If the angle with vec3(1.0, 0.0, 0.0) is small, I use its y coordinate, and vice versa.

Building Shader code:

const createBuildingShader = () => {
  return new Cesium.CustomShader({
    lightingModel: Cesium.LightingModel.UNLIT,
    varyings: {
      v_normalMC: Cesium.VaryingType.VEC3
    uniforms: {
      u_texture: {
        value: new Cesium.TextureUniform({
          url: '/demos/buildings/wall.png'
        type: Cesium.UniformType.SAMPLER_2D
    vertexShaderText: `
void vertexMain(VertexInput vsInput, inout vec3 positionMC) {
  v_normalMC = vsInput.attributes.normalMC;
    fragmentShaderText: /* see below */ ``

Clip shader code:

void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
  vec3 positionMC = fsInput.attributes.positionMC;
  float width = 75.0;
  float height = 75.0;
  if (dot(vec3(0.0, 0.0, 1.0), v_normalMC) > 0.95) {
    material.diffuse = vec3(0.079, 0.107, 0.111);
  } else {
    float textureX = 0.0;
    float dotYAxis = dot(vec3(0.0, 1.0, 0.0), v_normalMC);
    // cos(45deg) is approximately equal to 0.71
    if (dotYAxis > 0.71 || dotYAxis < -0.71) {
      textureX = mod(positionMC.x, width) / width;
    } else {
      textureX = mod(positionMC.y, width) / width;

    float textureY = mod(positionMC.z, height) / height;
    vec3 rgb = texture2D(u_texture, vec2(textureX, textureY)).rgb;
    material.diffuse = rgb;

Finally, some road streamers can be added to make the city more modern, which is the effect of the beginning of the article.

Tags: Visualization webgl cesium

Posted on Sun, 21 Nov 2021 21:27:34 -0500 by csatucd