Intermittent black line rendering artifacts when adding images via Entity

Hello Cesium team,

I am currently using an Entity-based approach combined with callbacks to implement continuous image updates for animation playback.

During implementation, I encountered a rendering issue where black lines occasionally appear on the images.

The issue has the following characteristics:

  • It can still occur even when the animation is paused and the scene is completely static.

  • When interacting with the map (e.g., dragging, zooming, or changing the camera view), the black lines may appear or disappear.

  • The behavior is not consistently reproducible and occurs intermittently.

Could you please advise whether there are recommended solutions or best practices to avoid this issue in this kind of implementation?

Thank you for your support.

fmvEntity = cesiumViewer.entities.add({
  polygon: {
    hierarchy: new Cesium.CallbackProperty(() => currentHierarchy, false),
    material: new Cesium.ImageMaterialProperty({
      image: new Cesium.CallbackProperty(() => currentCanvas, false)
    }),
    classificationType: Cesium.ClassificationType.TERRAIN,
   }
});

Hi @szkwen,

Before diving into suggestions, it would help to know a bit more about how currentCanvas is being used:

  • Is it a <canvas> element you’re drawing to each frame in a requestAnimationFrame loop?
  • Is it a video element or some other image source?
  • Are you drawing to the same canvas object in place each frame, or swapping references?

The black line artifacts you’re describing at polygon edges can have a few different causes depending on the answer, the most likely being a race condition between writing to the canvas and CesiumJS uploading it as a WebGL texture, or texture edge sampling behavior in the polygon renderer. Knowing how your canvas is updated will let me give you a more targeted answer.

In the meantime, a Sandcastle repro with a minimal version of your setup would really help pin down the exact cause.

Hi @darcyvdd,
Thanks for your reply.

Let me clarify point 3 first:

Are you drawing to the same canvas object in place each frame, or swapping references?

I’m intentionally using a double-buffered canvas approach (swapping between two canvas instances) to work around a known issue reported in the Cesium community:

BUG: CallbackProperty for ImageMaterialProperty image doesn’t update image

In my implementation, I alternate canvases like this:

curCanvas = curCanvas === 'a' ? 'b' : 'a';

This is necessary because updating the same canvas in-place does not reliably trigger texture refresh in Cesium in my case.


Point 2: Is it a video element or some other image source?

It’s an image source (bitmap), rendered via:

activeCtx.drawImage(bitmap, 0, 0);

Point 1: Is it a element drawn each frame in a requestAnimationFrame loop?

It’s not strictly requestAnimationFrame. I’m using a timed update loop:

setTimeout(() => {
    // Alternate canvases
    const activeCanvas = useCanvasA ? canvasA : canvasB;
    const activeCtx = useCanvasA ? ctxA : ctxB;
    activeCtx.drawImage(lastBitmap, 0, 0);
    currentCanvas = activeCanvas;
    useCanvasA = !useCanvasA;
    // Ensure it is initialized only once.
    if (!fmvEntity) {
      fmvEntity = cesiumViewer.entities.add({
        polygon: {
          hierarchy: new Cesium.CallbackProperty(() => currentHierarchy, false),
          material: new Cesium.ImageMaterialProperty({
            image: new Cesium.CallbackProperty(() => currentCanvas, false)
          }),
          classificationType: Cesium.ClassificationType.TERRAIN,
        }
      });
    }
  cesiumViewer.scene.requestRender();
}, 33);

I also explicitly call scene.requestRender() to force re-rendering in request render mode.


And this is my sandcastle. After starting it, when I zoom in and out on the map, I can clearly see flickering black lines.

Okay after way more time troubleshooting than I’d care to admit… The fix is simpler than expected:

Just add perPositionHeight: true to your polygon definition:

const fmvEntity = viewer.entities.add({
  polygon: {
    hierarchy: new Cesium.CallbackProperty(() => currentHierarchy, false),
    perPositionHeight: true,  // <-- this one line
    material: new Cesium.ImageMaterialProperty({
      image: new Cesium.CallbackProperty(() => currentCanvas, false),
    }),
    classificationType: Cesium.ClassificationType.TERRAIN,
  }
});

I noticed that while zooming the artifacts/black lines seemed to align with tile boundaries, I tried adjusting just the classification type, removing it altogether, and then tried per position height with actual z height and that solved it, walked it back and re-added classification type and tried z = 0 and found it’s just the perPositionHeight: true that was needed