How to render a different camera view inside the same scene

  1. Basic information
    Cesium version: a customized Cesium based on an older version, I can’t confirm which exact version.
    OS: Windows 10
    Browser: Chrome 111.0.5563.147

  2. Target:
    I intend to make a water reflection effect, similiar to what mentioned here: Water effects and animation - #3 by wzc339005483

To do that I need to create a reflect camera, and render it to the framebuffer as a texture. I try to render a extra frame to framebuffer, and use the texture to render the scene with different camera setting to a custom primitive which is the water surface.

  1. What I did/tried:
    I created a new Cesium.View object with a new Cesium.Camera object, and use it as a offscreen view. During first render, I set the scene’s _view to my new Cesium.View object,update the camera, and render the scene, but I only got a blank image in the texture. After that I set the view of the scene back to default and render another scene for on-screen display.

I have already taken care of rendering the framebuffer color texture.

  1. Related code:
    Framebuffer definition:
            let framebufferReflection = new Cesium.Framebuffer({
            context: context,
            colorTextures: [new Cesium.Texture({
              context: context,
              width: context.drawingBufferWidth,
              height: context.drawingBufferHeight,
              pixelFormat: Cesium.PixelFormat.RGBA,
            })],
            depthStencilTexture: new Cesium.Texture({
              context: context,
              width: context.drawingBufferWidth,
              height: context.drawingBufferHeight,
              pixelFormat: Cesium.PixelFormat.DEPTH_STENCIL,
              pixelDatatype: Cesium.PixelDatatype.UNSIGNED_INT_24_8
            })
          });

Code for offscreen render:

          function updateOffscreenCamera(camera, newPos, width, height){
            camera.position = newPos;
            // camera.rotateUp(90);

            camera.frustum.width = width;
            camera.frustum.aspectRatio = width / height;
            return camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC)
          }
          let { scene } = viewer
          // 1. Create offscreen camera and view;
          let offscreenCamera = new Cesium.Camera(scene);
          let offscreenView = new Cesium.View(scene, offscreenCamera, new Cesium.BoundingRectangle(0, 0, context.drawingBufferWidth, context.drawingBufferHeight))

          function tick() {
            
            let { frameState } = scene;
            let camera = viewer.scene.camera;
            // let gl = scene.context._gl
            
            offscreenCamera.position = viewer.camera.position;
            offscreenCamera.direction = viewer.camera.direction;
            offscreenCamera.up = viewer.camera.up;
            offscreenCamera.frustum = viewer.camera.frustum;
            const boundingRectangle = new Cesium.BoundingRectangle(0, 0, context.drawingBufferWidth, context.drawingBufferHeight);
            offscreenView.viewport = boundingRectangle;
            offscreenView.passState.viewport = boundingRectangle;
            
            offscreenView.passState.framebuffer = framebufferReflection
            scene._view = offscreenView
            const { passState } = scene._view
            let invertHeightPositionCartographic = new Cesium.Cartographic(
              viewer.scene.camera.positionCartographic.longitude,
              viewer.scene.camera.positionCartographic.latitude,
              viewer.scene.camera.positionCartographic.height * 1.5
            )
            let invertHeightPosition = Cesium.Cartesian3.fromRadians(
              invertHeightPositionCartographic.longitude, 
              invertHeightPositionCartographic.latitude, 
              invertHeightPositionCartographic.height
            );

            // 2. Update offscreen frame buffer texture
            scene._view.globeDepth.update(context, passState, offscreenView.viewport, scene._defaultView.globeDepth._useHdr);
            updateOffscreenCamera(offscreenView.camera, invertHeightPosition, context.drawingBufferWidth, context.drawingBufferHeight)
            scene.updateFrameState();
            frameState.passes.offscreen = true;
            
            context.uniformState.update(frameState);
            scene.updateEnvironment();
            scene.updateAndExecuteCommands(passState, Cesium.defaultValue(scene.backgroundColor, Cesium.Color.TRANSPARENT));
            scene.resolveFramebuffers(passState);
            scene._view = scene._defaultView;
            context.endFrame();
            const pixels = context.readPixels({
              x: 0,
              y: 0,
              width: context.drawingBufferWidth,
              height: context.drawingBufferHeight,
              framebuffer: framebufferReflection
            });
            // pixel is a blank image, all pixel value is [0,0,0,255]

            viewer.resize();
            scene.render();

            Cesium.requestAnimationFrame(tick);
          }
          tick();
  1. Question:
    Am I on the right track? Would this be able to do what I want? If not, what should I do instead?

Thanks

1 Like

Did this ever work out for you?