setProjectionFromUnion method

void setProjectionFromUnion(
  1. Camera camera,
  2. Camera cameraL,
  3. Camera 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

@param {ArrayCamera} camera - The camera to update. @param {PerspectiveCamera} cameraL - The left camera. @param {PerspectiveCamera} cameraR - The right camera. /

Implementation

void setProjectionFromUnion(Camera camera, Camera cameraL, Camera 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 new 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();

  // Check if the projection uses an infinite far plane.
  if ( projL[ 10 ] == - 1.0 ) {
    // Use the projection matrix from the left eye.
    // The camera offset is sufficient to include the view volumes
    // of both eyes (assuming symmetric projections).
    camera.projectionMatrix.setFrom( cameraL.projectionMatrix );
    camera.projectionMatrixInverse.setFrom( cameraL.projectionMatrixInverse );
  }
  else {

    // 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 new 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 );
    camera.projectionMatrixInverse.setFrom( camera.projectionMatrix ).invert();
  }
}