Saving out high-resolution screenshot of scene with react-three/fiber

I have a 3D scene running on a Canvas at whatever resolution the user’s current browser dimensions are, and I want the user to be able to take a screenshot that will render and save a high-resolution version (at a fixed rate, e.g. 4K) of the current scene with current camera angle and everything.

The current “dumb” way I’m using is using toDataUrl("image.png", 1), but that doesn’t allow me to pick a different resolution.

public getCurrentImage(): any {
  return this.state.currentCanvasRef.current.toDataURL("image/png", 1);
}

render() {
  return (
    <Canvas
       ref={this.state.currentCanvasRef}
       gl={{ preserveDrawingBuffer: true }} >
      <MyScene />
    </Canvas>
  );
}

There must be a better way to do this - I’ve looked into adding multiple render targets, but I don’t want to always render the whole scene at 4K, just the one screenshot.

Does anyone have a good suggestion or example as to how to do this?

You can use useFBO from drei - just create an FBO (it’s just a fancy name for a RenderTarget / canvas.) It lets you specify a render target resolution independent from the main canvas:

const Component = () => {
  const fbo = useFBO(4096, 2048);
  const {scene, camera} = useThree();

  const renderTo4kFBO = () => {
    gl.setRenderTarget(fbo);
    gl.render(scene, camera);
    gl.setRenderTarget(null);

    const imageData = new ImageData(4096, 2048);
    gl.readRenderTargetPixels(fbo, 0, 0, 4096, 2048, imageData.data);

    const canvas = document.createElement('canvas');
    canvas.width = 4096;
    canvas.height = 2048;

    const ctx = canvas.getContext('2d');
    ctx.putImageData(imageData, 0, 0);

    return canvas.toDataURL('image/png');
  };

  return(
    <anythingOrNull />
  );
);
2 Likes