javascriptthree.jsorthographicfrustum

3d object should always "fit" inside the window with Orthographic Camera


I want my 3d objects to always "fit" inside the window This is how my code currently looks like

export function onWindowResize(camera, renderer) {
  const canvas = renderer.domElement;
  const width = window.innerWidth;
  const height = window.innerHeight;
  const connection = getBoundingBox(connectionGroup);
  const needResize = canvas.width !== width || canvas.height !== height;
  const isPortrait = connection.width < connection.height;

  if (needResize) renderer.setSize(width, height);

  const aspect = isPortrait ? width / height : height / width;
  const frustumSize = Math.max(connection.width, connection.height);

  //Front View
  if (isPortrait) {
    camera.left = (-frustumSize * aspect) / 2;
    camera.right = (frustumSize * aspect) / 2;
    camera.top = frustumSize / 2;
    camera.bottom = -frustumSize / 2;
  } else {
    camera.left = -frustumSize / 2;
    camera.right = frustumSize / 2;
    camera.top = (frustumSize * aspect) / 2;
    camera.bottom = (-frustumSize * aspect) / 2;
  }

  camera.updateProjectionMatrix();
}

Currently, everything works as expected: the object fits in the window based on its height or width. A problem occurs if the object's height exceeds the window height but still fits within the maximum width (vice-versa), causing the model to be cropped.

I only need to fit the object on the initial display, user can perform orbit controls freely after that

MAX WIDTH

MAX HEIGHT

MAX WIDTH but models height exceed window/camera height

I tried manually setting camera.zoom, but even then, I couldn't figure out the right value for it. After that, my orbit controls stopped functioning.


Solution

  • Calculations should account for a special case when the ratio of window dimensions (aspect) is less than the ratio of connection dimensions (frustumAspect). The frustumSize should be scaled to reflect that difference (frustumScaled).

    Optionally, camera.left and camera.bottom can be simplified by negating the opposite properties.

    const frustumAspect = isPortrait ? (connection.width / connection.height) :
      (connection.height / connection.width);
    
    const aspectScale = Math.max(frustumAspect / aspect, 1);
    const frustumScaled = frustumSize * aspectScale;
    
    //Front View
    if (isPortrait) {
      camera.right = (frustumScaled * aspect) / 2;
      camera.top = frustumScaled / 2;
    } else {
      camera.right = frustumScaled / 2;
      camera.top = (frustumScaled * aspect) / 2;
    }
    camera.left = -camera.right;
    camera.bottom = -camera.top;