Skip to content

Commit

Permalink
PerspectiveCamera: Moved frameCorners() to CameraUtils in examples.
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdoob committed May 27, 2021
1 parent 86faaca commit e87bfeb
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 78 deletions.
6 changes: 0 additions & 6 deletions docs/api/en/cameras/PerspectiveCamera.html
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,6 @@ <h3>[method:null updateProjectionMatrix]()</h3>
Updates the camera projection matrix. Must be called after any change of parameters.
</p>

<h3>[method:null frameCorners]( [param:Vector3 bottomLeftCorner], [param:Vector3 bottomRightCorner], [param:Vector3 topLeftCorner], [param:boolean estimateViewFrustum] )</h3>
<p>
Set this PerspectiveCamera's projectionMatrix and quaternion to exactly frame the corners of an arbitrary rectangle using [link:https://web.archive.org/web/20191110002841/http://csc.lsu.edu/~kooima/articles/genperspective/index.html Kooima's Generalized Perspective Projection formulation].
NOTE: This function ignores the standard parameters; do not call updateProjectionMatrix() after this! toJSON will also not capture the off-axis matrix generated by this function.
</p>

<h3>[method:Object toJSON]([param:Object meta])</h3>
<p>
meta -- object containing metadata such as textures or images in objects' descendants.<br />
Expand Down
29 changes: 29 additions & 0 deletions docs/examples/en/utils/CameraUtils.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="../../../" />
<script src="page.js"></script>
<link type="text/css" rel="stylesheet" href="page.css" />
</head>
<body>
<h1>[name]</h1>

<p class="desc">A class containing useful utility functions for camera manipulation.</p>


<h2>Methods</h2>

<h3>[method:null frameCorners]( [param:PerspectiveCamera camera] [param:Vector3 bottomLeftCorner], [param:Vector3 bottomRightCorner], [param:Vector3 topLeftCorner], [param:boolean estimateViewFrustum] )</h3>
<p>
Set a PerspectiveCamera's projectionMatrix and quaternion to exactly frame the corners of an arbitrary rectangle using [link:https://web.archive.org/web/20191110002841/http://csc.lsu.edu/~kooima/articles/genperspective/index.html Kooima's Generalized Perspective Projection formulation].
NOTE: This function ignores the standard parameters; do not call updateProjectionMatrix() after this! toJSON will also not capture the off-axis matrix generated by this function.
</p>

<h2>Source</h2>

<p>
[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/utils/CameraUtils.js examples/jsm/utils/CameraUtils.js]
</p>
</body>
</html>
1 change: 1 addition & 0 deletions docs/list.json
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@

"Utils": {
"BufferGeometryUtils": "examples/en/utils/BufferGeometryUtils",
"CameraUtils": "examples/en/utils/CameraUtils",
"SceneUtils": "examples/en/utils/SceneUtils",
"SkeletonUtils": "examples/en/utils/SkeletonUtils"
}
Expand Down
2 changes: 1 addition & 1 deletion examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@
"webgl_math_obb",
"webgl_math_orientation_transform",
"webgl_mirror",
"webgl_portal",
"webgl_modifier_curve",
"webgl_modifier_curve_instanced",
"webgl_modifier_edgesplit",
Expand All @@ -198,6 +197,7 @@
"webgl_points_dynamic",
"webgl_points_sprites",
"webgl_points_waves",
"webgl_portal",
"webgl_raycast_sprite",
"webgl_raycast_texture",
"webgl_read_float_buffer",
Expand Down
88 changes: 88 additions & 0 deletions examples/js/utils/CameraUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
( function () {

const _va = /*@__PURE__*/new THREE.Vector3(),
// from pe to pa
_vb = /*@__PURE__*/new THREE.Vector3(),
// from pe to pb
_vc = /*@__PURE__*/new THREE.Vector3(),
// from pe to pc
_vr = /*@__PURE__*/new THREE.Vector3(),
// right axis of screen
_vu = /*@__PURE__*/new THREE.Vector3(),
// up axis of screen
_vn = /*@__PURE__*/new THREE.Vector3(),
// normal vector of screen
_vec = /*@__PURE__*/new THREE.Vector3(),
// temporary vector
_quat = /*@__PURE__*/new THREE.Quaternion(); // temporary quaternion


class CameraUtils {

/** Set a PerspectiveCamera's projectionMatrix and quaternion
* to exactly frame the corners of an arbitrary rectangle.
* NOTE: This function ignores the standard parameters;
* do not call updateProjectionMatrix() after this!
* @param {Vector3} bottomLeftCorner
* @param {Vector3} bottomRightCorner
* @param {Vector3} topLeftCorner
* @param {boolean} estimateViewFrustum */
static frameCorners( camera, bottomLeftCorner, bottomRightCorner, topLeftCorner, estimateViewFrustum = false ) {

const pa = bottomLeftCorner,
pb = bottomRightCorner,
pc = topLeftCorner;
const pe = camera.position; // eye position

const n = camera.near; // distance of near clipping plane

const f = camera.far; //distance of far clipping plane

_vr.copy( pb ).sub( pa ).normalize();

_vu.copy( pc ).sub( pa ).normalize();

_vn.crossVectors( _vr, _vu ).normalize();

_va.copy( pa ).sub( pe ); // from pe to pa


_vb.copy( pb ).sub( pe ); // from pe to pb


_vc.copy( pc ).sub( pe ); // from pe to pc


const d = - _va.dot( _vn ); // distance from eye to screen

const l = _vr.dot( _va ) * n / d; // distance to left screen edge

const r = _vr.dot( _vb ) * n / d; // distance to right screen edge

const b = _vu.dot( _va ) * n / d; // distance to bottom screen edge

const t = _vu.dot( _vc ) * n / d; // distance to top screen edge
// Set the camera rotation to match the focal plane to the corners' plane

_quat.setFromUnitVectors( _vec.set( 0, 1, 0 ), _vu );

camera.quaternion.setFromUnitVectors( _vec.set( 0, 0, 1 ).applyQuaternion( _quat ), _vn ).multiply( _quat ); // Set the off-axis projection matrix to match the corners

camera.projectionMatrix.set( 2.0 * n / ( r - l ), 0.0, ( r + l ) / ( r - l ), 0.0, 0.0, 2.0 * n / ( t - b ), ( t + b ) / ( t - b ), 0.0, 0.0, 0.0, ( f + n ) / ( n - f ), 2.0 * f * n / ( n - f ), 0.0, 0.0, - 1.0, 0.0 );
camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); // FoV estimation to fix frustum culling

if ( estimateViewFrustum ) {

// Set fieldOfView to a conservative estimate
// to make frustum tall/wide enough to encompass it
camera.fov = THREE.MathUtils.RAD2DEG / Math.min( 1.0, camera.aspect ) * Math.atan( ( _vec.copy( pb ).sub( pa ).length() + _vec.copy( pc ).sub( pa ).length() ) / _va.length() );

}

}

}

THREE.CameraUtils = CameraUtils;

} )();
76 changes: 76 additions & 0 deletions examples/jsm/utils/CameraUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {
MathUtils,
Quaternion,
Vector3
} from '../../../build/three.module.js';

const _va = /*@__PURE__*/ new Vector3(), // from pe to pa
_vb = /*@__PURE__*/ new Vector3(), // from pe to pb
_vc = /*@__PURE__*/ new Vector3(), // from pe to pc
_vr = /*@__PURE__*/ new Vector3(), // right axis of screen
_vu = /*@__PURE__*/ new Vector3(), // up axis of screen
_vn = /*@__PURE__*/ new Vector3(), // normal vector of screen
_vec = /*@__PURE__*/ new Vector3(), // temporary vector
_quat = /*@__PURE__*/ new Quaternion(); // temporary quaternion

class CameraUtils {

/** Set a PerspectiveCamera's projectionMatrix and quaternion
* to exactly frame the corners of an arbitrary rectangle.
* NOTE: This function ignores the standard parameters;
* do not call updateProjectionMatrix() after this!
* @param {Vector3} bottomLeftCorner
* @param {Vector3} bottomRightCorner
* @param {Vector3} topLeftCorner
* @param {boolean} estimateViewFrustum */
static frameCorners( camera, bottomLeftCorner, bottomRightCorner, topLeftCorner, estimateViewFrustum = false ) {

const pa = bottomLeftCorner, pb = bottomRightCorner, pc = topLeftCorner;
const pe = camera.position; // eye position
const n = camera.near; // distance of near clipping plane
const f = camera.far; //distance of far clipping plane

_vr.copy( pb ).sub( pa ).normalize();
_vu.copy( pc ).sub( pa ).normalize();
_vn.crossVectors( _vr, _vu ).normalize();

_va.copy( pa ).sub( pe ); // from pe to pa
_vb.copy( pb ).sub( pe ); // from pe to pb
_vc.copy( pc ).sub( pe ); // from pe to pc

const d = - _va.dot( _vn ); // distance from eye to screen
const l = _vr.dot( _va ) * n / d; // distance to left screen edge
const r = _vr.dot( _vb ) * n / d; // distance to right screen edge
const b = _vu.dot( _va ) * n / d; // distance to bottom screen edge
const t = _vu.dot( _vc ) * n / d; // distance to top screen edge

// Set the camera rotation to match the focal plane to the corners' plane
_quat.setFromUnitVectors( _vec.set( 0, 1, 0 ), _vu );
camera.quaternion.setFromUnitVectors( _vec.set( 0, 0, 1 ).applyQuaternion( _quat ), _vn ).multiply( _quat );

// Set the off-axis projection matrix to match the corners
camera.projectionMatrix.set( 2.0 * n / ( r - l ), 0.0,
( r + l ) / ( r - l ), 0.0, 0.0,
2.0 * n / ( t - b ),
( t + b ) / ( t - b ), 0.0, 0.0, 0.0,
( f + n ) / ( n - f ),
2.0 * f * n / ( n - f ), 0.0, 0.0, - 1.0, 0.0 );
camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert();

// FoV estimation to fix frustum culling
if ( estimateViewFrustum ) {

// Set fieldOfView to a conservative estimate
// to make frustum tall/wide enough to encompass it
camera.fov =
MathUtils.RAD2DEG / Math.min( 1.0, camera.aspect ) *
Math.atan( ( _vec.copy( pb ).sub( pa ).length() +
( _vec.copy( pc ).sub( pa ).length() ) ) / _va.length() );

}

}

}

export { CameraUtils };
2 changes: 1 addition & 1 deletion examples/tags.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"webgl_math_obb": [ "intersection", "bounding" ],
"webgl_math_orientation_transform": [ "rotation" ],
"webgl_mirror": [ "reflection" ],
"webgl_portal": [ "portal", "frameCorners", "renderTarget" ],
"webgl_portal": [ "frameCorners", "renderTarget" ],
"webgl_morphtargets_horse": [ "animation" ],
"webgl_multiple_elements": [ "differential equations", "physics" ],
"webgl_multiple_elements_text": [ "font" ],
Expand Down
11 changes: 6 additions & 5 deletions examples/webgl_portal.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import * as THREE from '../build/three.module.js';

import { CameraUtils } from './jsm/utils/CameraUtils.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';

let camera, scene, renderer;
Expand All @@ -33,7 +34,7 @@
let smallSphereOne, smallSphereTwo;

let portalCamera, leftPortal, rightPortal, leftPortalTexture, reflectedPosition,
rightPortalTexture, bottomLeftCorner, bottomRightCorner, topLeftCorner, frustumHelper;
rightPortalTexture, bottomLeftCorner, bottomRightCorner, topLeftCorner;

init();
animate();
Expand Down Expand Up @@ -191,7 +192,7 @@
otherPortalMesh.localToWorld( bottomRightCorner.set( - 50.05, - 50.05, 0.0 ) );
otherPortalMesh.localToWorld( topLeftCorner.set( 50.05, 50.05, 0.0 ) );
// set the projection matrix to encompass the portal's frame
portalCamera.frameCorners( bottomLeftCorner, bottomRightCorner, topLeftCorner, false );
CameraUtils.frameCorners( portalCamera, bottomLeftCorner, bottomRightCorner, topLeftCorner, false );

// render the portal
thisPortalTexture.texture.encoding = renderer.outputEncoding;
Expand Down Expand Up @@ -227,9 +228,9 @@
smallSphereTwo.rotation.z = timerTwo * 0.8;

// save the original camera properties
let currentRenderTarget = renderer.getRenderTarget();
let currentXrEnabled = renderer.xr.enabled;
let currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
const currentRenderTarget = renderer.getRenderTarget();
const currentXrEnabled = renderer.xr.enabled;
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
renderer.xr.enabled = false; // Avoid camera modification
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows

Expand Down
65 changes: 0 additions & 65 deletions src/cameras/PerspectiveCamera.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,62 +206,6 @@ class PerspectiveCamera extends Camera {

}

/** Set this PerspectiveCamera's projectionMatrix and quaternion
* to exactly frame the corners of an arbitrary rectangle.
* NOTE: This function ignores the standard parameters;
* do not call updateProjectionMatrix() after this!
* @param {Vector3} bottomLeftCorner
* @param {Vector3} bottomRightCorner
* @param {Vector3} topLeftCorner
* @param {boolean} estimateViewFrustum */
frameCorners( bottomLeftCorner, bottomRightCorner, topLeftCorner, estimateViewFrustum = false ) {

const pa = bottomLeftCorner, pb = bottomRightCorner, pc = topLeftCorner;
const pe = this.position; // eye position
const n = this.near; // distance of near clipping plane
const f = this.far; //distance of far clipping plane

_vr.copy( pb ).sub( pa ).normalize();
_vu.copy( pc ).sub( pa ).normalize();
_vn.crossVectors( _vr, _vu ).normalize();

_va.copy( pa ).sub( pe ); // from pe to pa
_vb.copy( pb ).sub( pe ); // from pe to pb
_vc.copy( pc ).sub( pe ); // from pe to pc

const d = - _va.dot( _vn ); // distance from eye to screen
const l = _vr.dot( _va ) * n / d; // distance to left screen edge
const r = _vr.dot( _vb ) * n / d; // distance to right screen edge
const b = _vu.dot( _va ) * n / d; // distance to bottom screen edge
const t = _vu.dot( _vc ) * n / d; // distance to top screen edge

// Set the camera rotation to match the focal plane to the corners' plane
_quat.setFromUnitVectors( _vec.set( 0, 1, 0 ), _vu );
this.quaternion.setFromUnitVectors( _vec.set( 0, 0, 1 ).applyQuaternion( _quat ), _vn ).multiply( _quat );

// Set the off-axis projection matrix to match the corners
this.projectionMatrix.set( 2.0 * n / ( r - l ), 0.0,
( r + l ) / ( r - l ), 0.0, 0.0,
2.0 * n / ( t - b ),
( t + b ) / ( t - b ), 0.0, 0.0, 0.0,
( f + n ) / ( n - f ),
2.0 * f * n / ( n - f ), 0.0, 0.0, - 1.0, 0.0 );
this.projectionMatrixInverse.copy( this.projectionMatrix ).invert();

// FoV estimation to fix frustum culling
if ( estimateViewFrustum ) {

// Set fieldOfView to a conservative estimate
// to make frustum tall/wide enough to encompass it
this.fov =
MathUtils.RAD2DEG / Math.min( 1.0, this.aspect ) *
Math.atan( ( _vec.copy( pb ).sub( pa ).length() +
( _vec.copy( pc ).sub( pa ).length() ) ) / _va.length() );

}

}

toJSON( meta ) {

const data = super.toJSON( meta );
Expand All @@ -288,13 +232,4 @@ class PerspectiveCamera extends Camera {

PerspectiveCamera.prototype.isPerspectiveCamera = true;

const _va = /*@__PURE__*/ new Vector3(), // from pe to pa
_vb = /*@__PURE__*/ new Vector3(), // from pe to pb
_vc = /*@__PURE__*/ new Vector3(), // from pe to pc
_vr = /*@__PURE__*/ new Vector3(), // right axis of screen
_vu = /*@__PURE__*/ new Vector3(), // up axis of screen
_vn = /*@__PURE__*/ new Vector3(), // normal vector of screen
_vec = /*@__PURE__*/ new Vector3(), // temporary vector
_quat = /*@__PURE__*/ new Quaternion(); // temporary quaternion

export { PerspectiveCamera };

0 comments on commit e87bfeb

Please sign in to comment.