The ImageMaterialProperty of WallGraphics may incorrectly render and display


As it shows, the image texture of wall is not correctly display in part. Green part looks correct, and red part looks incorrect.
The sandcastle code as follows:

var viewer = new Cesium.Viewer("cesiumContainer", { infoBox: false });
var entities = viewer.entities;
var points = [
  // test case
  113.07496814748878, 36.55374866768215, 100,
  113.08061718310943, 36.55380171367157, 100,
  113.08060212629119, 36.55303212176819, 100,
  113.08161112459593, 36.55304123289039, 100,
  113.08150398573808, 36.55836174343265, 100,
  113.08058233717055, 36.5583527998272, 100,
  113.08057594475674, 36.55910995686964, 100,
  113.07492193714143, 36.55909080361859, 100,
  113.07496814748878, 36.55374866768215, 100,
];
entities.add({
  wall: {
    positions: Cesium.Cartesian3.fromDegreesArrayHeights(points),
    material: new Cesium.ImageMaterialProperty({
      image: '../images/Cesium_Logo_Color.jpg',
      repeat: new Cesium.Cartesian2(10, 1),
    }),
    outline: true,
  },
});
viewer.zoomTo(viewer.entities);

Could someone tell me is this a bug of Cesium or something is wrong with my code?
:grinning:

The reason is the st.x in WallGeometry is not calculated by distance but by the fact that it’s broken up into parts. for example if you have a 100 meters wall geometry which made by 3 positions, and the first part is 30 meters and the other is 70. The st.x in wall geometry will be 0.0~0.5 and 0.5~1.0 rather than 0.0~0.3 and 0.7~1.0. Actually I don’t know if it’s a bug or deliberate design.

My solution is create a custom geometry and use appearance:

/**
 * Crate wall geometry
 *
 * @param {Cartesian3[]} positions
 * @param {number} height
 * @returns {Geometry}
 */
function createWallGeometry(positions, height) {
  const indices = []
  const distances = [0]
  const times = (positions.length - 1) * 2
  let perimeter = 0
  for (let i = 0; i < times; i++) {
    // Indices
    if (i % 2) {
      indices.push(i + 2, i - 1, i + 1)
    } else {
      indices.push(i + 1, i, i + 3)
    }

    // Calculate distances
    if (positions[i + 1]) {
      const distance = Cartesian3.distance(positions[i], positions[i + 1])
      distances.push(distance)
      perimeter += distance
    }
  }

  let percent = 0
  const st = []
  const wallPositions = []
  for (let i = 0; i < positions.length; i++) {
    // St
    percent += distances[i] / perimeter
    if (i === positions.length - 1) percent = 1
    st.push(percent, 0, percent, 1)

    // Positions
    const position = positions[i]
    const bottomPoint = setHeight(position, 0)
    const topPoint = setHeight(position, height)
    wallPositions.push(
      bottomPoint.x,
      bottomPoint.y,
      bottomPoint.z,
      topPoint.x,
      topPoint.y,
      topPoint.z
    )
  }

  return new Geometry({
    attributes: {
      position: new GeometryAttribute({
        componentDatatype: ComponentDatatype.DOUBLE,
        componentsPerAttribute: 3,
        values: wallPositions,
      }),
      st: new GeometryAttribute({
        componentDatatype: ComponentDatatype.FLOAT,
        componentsPerAttribute: 2,
        values: new Float64Array(st),
      }),
    },
    indices: new Uint16Array(indices),
    primitiveType: PrimitiveType.TRIANGLES,
    boundingSphere: BoundingSphere.fromVertices(wallPositions),
  })
}

/**
 * Set height to position
 *
 * @param {Cartesian3} cartesian
 * @param {number} height
 * @returns
 */
export function setHeight(cartesian, height) {
  const cartographic = Cartographic.fromCartesian(cartesian)
  cartographic.height = height
  return Cartographic.toCartesian(cartographic)
}

As for the entity, I don’t know, I didn’t find a way.

1 Like

Thank you, Gu-Miao. Your reply give me a new idea. :smile:

I use your code in my programe, and now it works very well. Thank you so much. It helps me a lot. :grin: