setProjectionFromUnion method

void setProjectionFromUnion(
  1. Camera camera,
  2. PerspectiveCamera cameraL,
  3. PerspectiveCamera cameraR
)

Assumes 2 cameras that are parallel and share an X-axis, and that the cameras' projection and world matrices have already been set. And that near and far planes are identical for both cameras. Visualization of this technique: https://computergraphics.stackexchange.com/a/4765

Implementation

void setProjectionFromUnion(Camera camera, PerspectiveCamera cameraL, PerspectiveCamera cameraR ) {

  cameraLPos.setFromMatrixPosition( cameraL.matrixWorld );
  cameraRPos.setFromMatrixPosition( cameraR.matrixWorld );

  final ipd = cameraLPos.distanceTo( cameraRPos );

  final projL = cameraL.projectionMatrix.storage;
  final projR = cameraR.projectionMatrix.storage;

  // VR systems will have identical far and near planes, and
  // most likely identical top and bottom frustum extents.
  // Use the left camera for these values.
  final near = projL[ 14 ] / ( projL[ 10 ] - 1 );
  final far = projL[ 14 ] / ( projL[ 10 ] + 1 );
  final topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ];
  final bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ];

  final leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ];
  final rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ];
  final left = near * leftFov;
  final right = near * rightFov;

  // Calculate the camera's position offset from the
  // left camera. xOffset should be roughly half `ipd`.
  final zOffset = ipd / ( - leftFov + rightFov );
  final xOffset = zOffset * - leftFov;

  // TODO: Better way to apply this offset?
  cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale );
  camera.translateX( xOffset );
  camera.translateZ( zOffset );
  camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale );
  camera.matrixWorldInverse.setFrom( camera.matrixWorld ).invert();

  // Find the union of the frustum values of the cameras and scale
  // the values so that the near plane's position does not change in world space,
  // although must now be relative to the union camera.
  final near2 = near + zOffset;
  final far2 = far + zOffset;
  final left2 = left - xOffset;
  final right2 = right + ( ipd - xOffset );
  final top2 = topFov * far / far2 * near2;
  final bottom2 = bottomFov * far / far2 * near2;

  camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 );

}