2
0
Эх сурвалжийг харах

Revised PerspectiveCamera.

* Revised PerspectiveCamera.

- Allows extra skew for stereo rendering.
- Allows to query effective FOV angle (considering .zoom).
- Proper copy / serialization of all properties.
- Tidier, single code path frustum calculation.
- No longer uses ProjectionMatrix.makePerspective.

* Revised PerspectiveCamera physical interface.
tschw 9 жил өмнө
parent
commit
f7687afe8e

+ 48 - 9
docs/api/cameras/PerspectiveCamera.html

@@ -42,14 +42,11 @@ scene.add( camera );</code>
 
 		<h2>Properties</h2>
 
-		<h3>[property:number zoom]</h3>
-		<div>Gets or sets the zoom factor of the camera. </div>
-
 		<h3>[property:Float fov]</h3>
 		<div>Camera frustum vertical field of view, from bottom to top of view, in degrees.</div>
 
-		<h3>[property:Float aspect]</h3>
-		<div>Camera frustum aspect ratio, window width divided by window height.</div>
+		<h3>[property:number zoom]</h3>
+		<div>Gets or sets the zoom factor of the camera. </div>
 
 		<h3>[property:Float near]</h3>
 		<div>Camera frustum near plane.</div>
@@ -57,18 +54,60 @@ scene.add( camera );</code>
 		<h3>[property:Float far]</h3>
 		<div>Camera frustum far plane.</div>
 
+		<h3>[property:Float focus]</h3>
+		<div>Object distance used for stereoscopy and depth-of-field effects. This parameter does not influence the projection matrix unless a StereoCamera is being used.</div>
+
+		<h3>[property:Float aspect]</h3>
+		<div>Camera frustum aspect ratio, window width divided by window height.</div>
+
+		<h3>[property:Object view]</h3>
+		<div>Frustum window specification or null.</div>
+
+		<h3>[property:Float filmGauge]</h3>
+		<div>Film size used for the larger axis. Default is 35 (millimeters). This parameter does not influence the projection matrix unless .filmOffset is set to a nonzero value.</div>
+
+		<h3>[property:Float filmOffset]</h3>
+		<div>Horizontal off-center offset in the same unit as .filmGauge.</div>
 
 		<h2>Methods</h2>
 
-		<h3>[method:null setLens]( [page:Float focalLength], [page:Float frameSize] )</h3>
+		<h3>[method:Float getEffectiveFOV]()</h3>
+		<div>
+		Returns the current vertical field of view angle in degrees considering .zoom.
+		</div>
+
+		<h3>[method:Float getFocalLength]()</h3>
+		<div>
+		Returns the focal length of the current .fov in respect to .filmGauge.
+		</div>
+
+		<h3>[method:Float getFilmWidth]()</h3>
+		<div>
+		Returns the width of the image on the film. If .aspect is greater than or equal to one (landscape format), the result equals .filmGauge.
+		</div>
+
+		<h3>[method:Float getFilmHeight]()</h3>
+		<div>
+		Returns the height of the image on the film. If .aspect is less than or equal to one (portrait format), the result equals .fimlGauge.
+		</div>
+
+		<h3>[method:null setFocalLength]( [page:Float focalLength] )</h3>
+		<div>
+		Sets the FOV by focal length in respect to the current .filmGauge.
+		</div>
+		<div>
+		By default, the focal length is specified for a 35mm (full frame) camera.
+		</div>
+
+		<h3>[method:null setLens]( [page:Float focalLength], [page:Float filmGauge] )</h3>
 		<div>
 		focalLength — focal length<br />
-		frameSize — frame size
+		frameGauge — film gauge
 		</div>
 
 		<div>
-		Uses focal length (in mm) to estimate and set FOV 35mm (fullframe) camera is used if frame size is not specified.<br />
-		Formula based on [link:http://www.bobatkins.com/photography/technical/field_of_view.html]
+		Sets .fov by focal length. Optionally also sets .filmGauge.
+		This method is deprecated, please use .setFocalLength instead.
 		</div>
 
 		<h3>[method:null setViewOffset]( [page:Float fullWidth], [page:Float fullHeight], [page:Float x], [page:Float y], [page:Float width], [page:Float height] )</h3>

+ 2 - 2
docs/api/examples/cameras/CombinedCamera.html

@@ -130,10 +130,10 @@
 		Sets the zoomfactor.
 		</div>
 
-		<h3>[method:null setLens]([page:number focalLength], [page:Number frameHeight])</h3>
+		<h3>[method:null setLens]([page:number focalLength], [page:Number filmGauge])</h3>
 		<div>
 		focalLength -- The focal length of a lens is defined as the distance from the optical center of a lens (or, the secondary principal point for a complex lens like a camera lens) to the focal point (sensor) when the lens is focused on an object at infinity. <br />
-		frameHeight -- the size of the frame in mm. (default is *35*)
+		filmGauge -- the size of the frame in mm. (default is *35*)
 		</div>
 		<div>
 		Sets the fov based on lens data.

+ 12 - 10
examples/js/cameras/CinematicCamera.js

@@ -31,26 +31,26 @@ THREE.CinematicCamera.prototype.constructor = THREE.CinematicCamera;
 
 
 // providing fnumber and coc(Circle of Confusion) as extra arguments
-THREE.CinematicCamera.prototype.setLens = function ( focalLength, frameHeight, fNumber, coc ) {
+THREE.CinematicCamera.prototype.setLens = function ( focalLength, filmGauge, fNumber, coc ) {
 
 	// In case of cinematicCamera, having a default lens set is important
 	if ( focalLength === undefined ) focalLength = 35;
+	if ( filmGauge !== undefined ) this.filmGauge = filmGauge;
 
-	THREE.PerspectiveCamera.prototype.setLens.call( this, focalLength, frameHeight );
+	this.setFocalLength( focalLength );
 
 	// if fnumber and coc are not provided, cinematicCamera tries to act as a basic PerspectiveCamera
 	if ( fNumber === undefined ) fNumber = 8;
 	if ( coc === undefined ) coc = 0.019;
 
-	this.focalLength = focalLength;
 	this.fNumber = fNumber;
 	this.coc = coc;
 
 	// fNumber is focalLength by aperture
-	this.aperture = this.focalLength / this.fNumber;
+	this.aperture = focalLength / this.fNumber;
 
 	// hyperFocal is required to calculate depthOfField when a lens tries to focus at a distance with given fNumber and focalLength
-	this.hyperFocal = ( this.focalLength * this.focalLength ) / ( this.aperture * this.coc );
+	this.hyperFocal = ( focalLength * focalLength ) / ( this.aperture * this.coc );
 
 };
 
@@ -80,14 +80,16 @@ THREE.CinematicCamera.prototype.focusAt = function ( focusDistance ) {
 
 	if ( focusDistance === undefined ) focusDistance = 20;
 
-	// distance from the camera (normal to frustrum) to focus on (unused)
-	this.focusDistance = focusDistance;
+	var focalLength = this.getFocalLength();
+
+	// distance from the camera (normal to frustrum) to focus on
+	this.focus = focusDistance;
 
 	// the nearest point from the camera which is in focus (unused)
-	this.nearPoint = ( this.hyperFocal * this.focusDistance ) / ( this.hyperFocal + ( this.focusDistance - this.focalLength ) );
+	this.nearPoint = ( this.hyperFocal * this.focus ) / ( this.hyperFocal + ( this.focus - focalLength ) );
 
 	// the farthest point from the camera which is in focus (unused)
-	this.farPoint = ( this.hyperFocal * this.focusDistance ) / ( this.hyperFocal - ( this.focusDistance - this.focalLength ) );
+	this.farPoint = ( this.hyperFocal * this.focus ) / ( this.hyperFocal - ( this.focus - focalLength ) );
 
 	// the gap or width of the space in which is everything is in focus (unused)
 	this.depthOfField = this.farPoint - this.nearPoint;
@@ -95,7 +97,7 @@ THREE.CinematicCamera.prototype.focusAt = function ( focusDistance ) {
 	// Considering minimum distance of focus for a standard lens (unused)
 	if ( this.depthOfField < 0 ) this.depthOfField = 0;
 
-	this.sdistance = this.smoothstep( this.near, this.far, this.focusDistance );
+	this.sdistance = this.smoothstep( this.near, this.far, this.focus );
 
 	this.ldistance = this.linearize( 1 -	this.sdistance );
 

+ 7 - 4
examples/js/cameras/CombinedCamera.js

@@ -146,14 +146,17 @@ THREE.CombinedCamera.prototype.updateProjectionMatrix = function() {
 
 /*
 * Uses Focal Length (in mm) to estimate and set FOV
-* 35mm (fullframe) camera is used if frame size is not specified;
+* 35mm (full frame) camera is used if frame size is not specified;
 * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
 */
-THREE.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+THREE.CombinedCamera.prototype.setLens = function ( focalLength, filmGauge ) {
 
-	if ( frameHeight === undefined ) frameHeight = 24;
+	if ( filmGauge === undefined ) filmGauge = 35;
 
-	var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+	var vExtentSlope = 0.5 * filmGauge /
+			( focalLength * Math.max( this.cameraP.aspect, 1 ) );
+
+	var fov = THREE.Math.RAD2DEG * 2 * Math.atan( vExtentSlope );
 
 	this.setFov( fov );
 

+ 119 - 59
src/cameras/PerspectiveCamera.js

@@ -2,46 +2,121 @@
  * @author mrdoob / http://mrdoob.com/
  * @author greggman / http://games.greggman.com/
  * @author zz85 / http://www.lab4games.net/zz85/blog
+ * @author tschw
  */
 
-THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {
+THREE.PerspectiveCamera = function( fov, aspect, near, far ) {
 
 	THREE.Camera.call( this );
 
 	this.type = 'PerspectiveCamera';
 
-	this.focalLength = 10;
+	this.fov = fov !== undefined ? fov : 50;
 	this.zoom = 1;
 
-	this.fov = fov !== undefined ? fov : 50;
-	this.aspect = aspect !== undefined ? aspect : 1;
 	this.near = near !== undefined ? near : 0.1;
 	this.far = far !== undefined ? far : 2000;
+	this.focus = 10;
+
+	this.aspect = aspect !== undefined ? aspect : 1;
+	this.view = null;
+
+	this.filmGauge = 35;	// width of the film (default in millimeters)
+	this.filmOffset = 0;	// horizontal film offset (same unit as gauge)
 
 	this.updateProjectionMatrix();
 
 };
 
+THREE.PerspectiveCamera._assignProps = function( dest, source ) {
+
+	dest.fov = source.fov;
+	dest.zoom = source.zoom;
+
+	dest.near = source.near;
+	dest.far = source.far;
+	dest.focs = source.focus;
+
+	dest.aspect = source.aspect;
+	dest.view = ! source.view ? null : Object.assign( {}, source.view );
+
+	dest.filmGauge = source.filmGauge;
+	dest.filmOffset = source.filmOffset;
+
+};
+
 THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
 THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera;
 
 
 /**
- * Uses Focal Length (in mm) to estimate and set FOV
- * 35mm (full-frame) camera is used if frame size is not specified;
- * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
+ * Sets the FOV by focal length (DEPRECATED).
+ *
+ * Optionally also sets .filmGauge, otherwise uses it. See .setFocalLength.
  */
+THREE.PerspectiveCamera.prototype.setLens = function( focalLength, filmGauge ) {
+
+	console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " +
+			"Use .setFocalLength and .filmGauge for a photographic setup." );
 
-THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+	if ( filmGauge !== undefined ) this.filmGauge = filmGauge;
+	this.setFocalLength( focalLength );
 
-	if ( frameHeight === undefined ) frameHeight = 24;
+};
 
-	this.fov = 2 * THREE.Math.RAD2DEG * Math.atan( frameHeight / ( focalLength * 2 ) );
+/**
+ * Sets the FOV by focal length in respect to the current .filmGauge.
+ *
+ * The default film gauge is 35, so that the focal length can be specified for
+ * a 35mm (full frame) camera.
+ *
+ * Values for focal length and film gauge must have the same unit.
+ */
+THREE.PerspectiveCamera.prototype.setFocalLength = function( focalLength ) {
+
+	// see http://www.bobatkins.com/photography/technical/field_of_view.html
+	var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;
+
+	this.fov = THREE.Math.RAD2DEG * 2 * Math.atan( vExtentSlope );
 	this.updateProjectionMatrix();
 
 };
 
 
+/**
+ * Calculates the focal length from the current .fov and .filmGauge.
+ */
+THREE.PerspectiveCamera.prototype.getFocalLength = function() {
+
+	var vExtentSlope = Math.tan( THREE.Math.DEG2RAD * 0.5 * this.fov );
+
+	return 0.5 * this.getFilmHeight() / vExtentSlope;
+
+};
+
+THREE.PerspectiveCamera.prototype.getEffectiveFOV = function() {
+
+	return THREE.Math.RAD2DEG * 2 * Math.atan(
+			Math.tan( THREE.Math.DEG2RAD * 0.5 * this.fov ) / this.zoom );
+
+};
+
+THREE.PerspectiveCamera.prototype.getFilmWidth = function() {
+
+	// film not completely covered in portrait format (aspect < 1)
+	return this.filmGauge * Math.min( this.aspect, 1 );
+
+};
+
+THREE.PerspectiveCamera.prototype.getFilmHeight = function() {
+
+	// film not completely covered in landscape format (aspect > 1)
+	return this.filmGauge / Math.max( this.aspect, 1 );
+
+};
+
+
+
 /**
  * Sets an offset in a larger frustum. This is useful for multi-window or
  * multi-monitor/multi-machine setups.
@@ -77,80 +152,65 @@ THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight
  *
  *   Note there is no reason monitors have to be the same size or in a grid.
  */
+THREE.PerspectiveCamera.prototype.setViewOffset = function( fullWidth, fullHeight, x, y, width, height ) {
 
-THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) {
+	this.aspect = fullWidth / fullHeight;
 
-	this.fullWidth = fullWidth;
-	this.fullHeight = fullHeight;
-	this.x = x;
-	this.y = y;
-	this.width = width;
-	this.height = height;
+	this.view = {
+		fullWidth: fullWidth,
+		fullHeight: fullHeight,
+		offsetX: x,
+		offsetY: y,
+		width: width,
+		height: height
+	};
 
 	this.updateProjectionMatrix();
 
 };
 
+THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function() {
 
-THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {
+	var near = this.near,
+		top = near * Math.tan(
+				THREE.Math.DEG2RAD * 0.5 * this.fov ) / this.zoom,
+		height = 2 * top,
+		width = this.aspect * height,
+		left = -0.5 * width,
+		view = this.view;
 
-	var fov = THREE.Math.RAD2DEG * ( 2 * Math.atan( Math.tan( THREE.Math.DEG2RAD * this.fov * 0.5 ) / this.zoom ) );
+	if ( view !== null ) {
 
-	if ( this.fullWidth ) {
+		var fullWidth = view.fullWidth,
+			fullHeight = view.fullHeight;
 
-		var aspect = this.fullWidth / this.fullHeight;
-		var top = Math.tan( THREE.Math.DEG2RAD * fov * 0.5 ) * this.near;
-		var bottom = - top;
-		var left = aspect * bottom;
-		var right = aspect * top;
-		var width = Math.abs( right - left );
-		var height = Math.abs( top - bottom );
+		left += view.offsetX * width / fullWidth;
+		top -= view.offsetY * height / fullHeight;
+		width *= view.width / fullWidth;
+		height *= view.height / fullHeight;
 
-		this.projectionMatrix.makeFrustum(
-			left + this.x * width / this.fullWidth,
-			left + ( this.x + this.width ) * width / this.fullWidth,
-			top - ( this.y + this.height ) * height / this.fullHeight,
-			top - this.y * height / this.fullHeight,
-			this.near,
-			this.far
-		);
-
-	} else {
+	}
 
-		this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far );
+	var skew = this.filmOffset;
+	if ( skew !== 0 ) left += near * skew / this.getFilmWidth();
 
-	}
+	this.projectionMatrix.makeFrustum(
+			left, left + width, top - height, top, near, this.far );
 
 };
 
-THREE.PerspectiveCamera.prototype.copy = function ( source ) {
+THREE.PerspectiveCamera.prototype.copy = function( source ) {
 
 	THREE.Camera.prototype.copy.call( this, source );
-
-	this.focalLength = source.focalLength;
-	this.zoom = source.zoom;
-
-	this.fov = source.fov;
-	this.aspect = source.aspect;
-	this.near = source.near;
-	this.far = source.far;
-
+	THREE.PerspectiveCamera._assignProps( this, source );
 	return this;
 
 };
 
-THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) {
+THREE.PerspectiveCamera.prototype.toJSON = function( meta ) {
 
 	var data = THREE.Object3D.prototype.toJSON.call( this, meta );
-
-	data.object.focalLength = this.focalLength;
-	data.object.zoom = this.zoom;
-
-	data.object.fov = this.fov;
-	data.object.aspect = this.aspect;
-	data.object.near = this.near;
-	data.object.far = this.far;
-
+	THREE.PerspectiveCamera._assignProps( data.object, this );
 	return data;
 
 };

+ 4 - 4
src/cameras/StereoCamera.js

@@ -24,20 +24,20 @@ THREE.StereoCamera.prototype = {
 
 	update: ( function () {
 
-		var focalLength, fov, aspect, near, far;
+		var focus, fov, aspect, near, far;
 
 		var eyeRight = new THREE.Matrix4();
 		var eyeLeft = new THREE.Matrix4();
 
 		return function update ( camera ) {
 
-			var needsUpdate = focalLength !== camera.focalLength || fov !== camera.fov ||
+			var needsUpdate = focus !== camera.focus || fov !== camera.fov ||
 												aspect !== camera.aspect * this.aspect || near !== camera.near ||
 												far !== camera.far;
 
 			if ( needsUpdate ) {
 
-				focalLength = camera.focalLength;
+				focus = camera.focus;
 				fov = camera.fov;
 				aspect = camera.aspect * this.aspect;
 				near = camera.near;
@@ -48,7 +48,7 @@ THREE.StereoCamera.prototype = {
 
 				var projectionMatrix = camera.projectionMatrix.clone();
 				var eyeSep = 0.064 / 2;
-				var eyeSepOnProjection = eyeSep * near / focalLength;
+				var eyeSepOnProjection = eyeSep * near / focus;
 				var ymax = near * Math.tan( THREE.Math.DEG2RAD * fov * 0.5 );
 				var xmin, xmax;