Skip to content

⬅️ Back to Table of Contents

📄 PerspectiveCamera.js

📊 Analysis Summary

Metric Count
🔧 Functions 12
🧱 Classes 1
📦 Imports 5
📊 Variables & Constants 13

📚 Table of Contents

🛠️ File Location:

📂 src/cameras/PerspectiveCamera.js

📦 Imports

Name Source
Camera ./Camera.js
RAD2DEG ../math/MathUtils.js
DEG2RAD ../math/MathUtils.js
Vector2 ../math/Vector2.js
Vector3 ../math/Vector3.js

Variables & Constants

Name Type Kind Value Exported
_v3 Vector3 let/var new Vector3()
_minTarget Vector2 let/var new Vector2()
_maxTarget Vector2 let/var new Vector2()
vExtentSlope number let/var 0.5 * this.getFilmHeight() / focalLength
near number let/var this.near
top number let/var near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom
height number let/var 2 * top
width number let/var this.aspect * height
left number let/var - 0.5 * width
view any let/var this.view
fullWidth any let/var view.fullWidth
fullHeight any let/var view.fullHeight
skew number let/var this.filmOffset

Functions

PerspectiveCamera.copy(source: any, recursive: any): this

Parameters:

  • source any
  • recursive any

Returns: this

Calls:

  • super.copy
  • Object.assign
Code
copy( source, recursive ) {

        super.copy( source, recursive );

        this.fov = source.fov;
        this.zoom = source.zoom;

        this.near = source.near;
        this.far = source.far;
        this.focus = source.focus;

        this.aspect = source.aspect;
        this.view = source.view === null ? null : Object.assign( {}, source.view );

        this.filmGauge = source.filmGauge;
        this.filmOffset = source.filmOffset;

        return this;

    }

PerspectiveCamera.setFocalLength(focalLength: number): void

JSDoc:

/**
     * Sets the FOV by focal length in respect to the current {@link PerspectiveCamera#filmGauge}.
     *
     * The default film gauge is 35, so that the focal length can be specified for
     * a 35mm (full frame) camera.
     *
     * @param {number} focalLength - Values for focal length and film gauge must have the same unit.
     */

Parameters:

  • focalLength number

Returns: void

Calls:

  • this.getFilmHeight
  • Math.atan
  • this.updateProjectionMatrix

Internal Comments:

/** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ (x2)

Code
setFocalLength( focalLength ) {

        /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */
        const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;

        this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope );
        this.updateProjectionMatrix();

    }

PerspectiveCamera.getFocalLength(): number

JSDoc:

/**
     * Returns the focal length from the current {@link PerspectiveCamera#fov} and
     * {@link PerspectiveCamera#filmGauge}.
     *
     * @return {number} The computed focal length.
     */

Returns: number

Calls:

  • Math.tan
  • this.getFilmHeight
Code
getFocalLength() {

        const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov );

        return 0.5 * this.getFilmHeight() / vExtentSlope;

    }

PerspectiveCamera.getEffectiveFOV(): number

JSDoc:

/**
     * Returns the current vertical field of view angle in degrees considering {@link PerspectiveCamera#zoom}.
     *
     * @return {number} The effective FOV.
     */

Returns: number

Calls:

  • Math.atan
  • Math.tan
Code
getEffectiveFOV() {

        return RAD2DEG * 2 * Math.atan(
            Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom );

    }

PerspectiveCamera.getFilmWidth(): number

JSDoc:

/**
     * Returns the width of the image on the film. If {@link PerspectiveCamera#aspect} is greater than or
     * equal to one (landscape format), the result equals {@link PerspectiveCamera#filmGauge}.
     *
     * @return {number} The film width.
     */

Returns: number

Calls:

  • Math.min

Internal Comments:

// film not completely covered in portrait format (aspect < 1)

Code
getFilmWidth() {

        // film not completely covered in portrait format (aspect < 1)
        return this.filmGauge * Math.min( this.aspect, 1 );

    }

PerspectiveCamera.getFilmHeight(): number

JSDoc:

/**
     * Returns the height of the image on the film. If {@link PerspectiveCamera#aspect} is greater than or
     * equal to one (landscape format), the result equals {@link PerspectiveCamera#filmGauge}.
     *
     * @return {number} The film width.
     */

Returns: number

Calls:

  • Math.max

Internal Comments:

// film not completely covered in landscape format (aspect > 1)

Code
getFilmHeight() {

        // film not completely covered in landscape format (aspect > 1)
        return this.filmGauge / Math.max( this.aspect, 1 );

    }

PerspectiveCamera.getViewBounds(distance: number, minTarget: Vector2, maxTarget: Vector2): void

JSDoc:

/**
     * Computes the 2D bounds of the camera's viewable rectangle at a given distance along the viewing direction.
     * Sets `minTarget` and `maxTarget` to the coordinates of the lower-left and upper-right corners of the view rectangle.
     *
     * @param {number} distance - The viewing distance.
     * @param {Vector2} minTarget - The lower-left corner of the view rectangle is written into this vector.
     * @param {Vector2} maxTarget - The upper-right corner of the view rectangle is written into this vector.
     */

Parameters:

  • distance number
  • minTarget Vector2
  • maxTarget Vector2

Returns: void

Calls:

  • _v3.set( - 1, - 1, 0.5 ).applyMatrix4
  • minTarget.set( _v3.x, _v3.y ).multiplyScalar
  • _v3.set( 1, 1, 0.5 ).applyMatrix4
  • maxTarget.set( _v3.x, _v3.y ).multiplyScalar
Code
getViewBounds( distance, minTarget, maxTarget ) {

        _v3.set( - 1, - 1, 0.5 ).applyMatrix4( this.projectionMatrixInverse );

        minTarget.set( _v3.x, _v3.y ).multiplyScalar( - distance / _v3.z );

        _v3.set( 1, 1, 0.5 ).applyMatrix4( this.projectionMatrixInverse );

        maxTarget.set( _v3.x, _v3.y ).multiplyScalar( - distance / _v3.z );

    }

PerspectiveCamera.getViewSize(distance: number, target: Vector2): Vector2

JSDoc:

/**
     * Computes the width and height of the camera's viewable rectangle at a given distance along the viewing direction.
     *
     * @param {number} distance - The viewing distance.
     * @param {Vector2} target - The target vector that is used to store result where x is width and y is height.
     * @returns {Vector2} The view size.
     */

Parameters:

  • distance number
  • target Vector2

Returns: Vector2

Calls:

  • this.getViewBounds
  • target.subVectors
Code
getViewSize( distance, target ) {

        this.getViewBounds( distance, _minTarget, _maxTarget );

        return target.subVectors( _maxTarget, _minTarget );

    }

PerspectiveCamera.setViewOffset(fullWidth: number, fullHeight: number, x: number, y: number, width: number, height: number): void

JSDoc:

/**
     * Sets an offset in a larger frustum. This is useful for multi-window or
     * multi-monitor/multi-machine setups.
     *
     * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
     * the monitors are in grid like this
     *```
     *   +---+---+---+
     *   | A | B | C |
     *   +---+---+---+
     *   | D | E | F |
     *   +---+---+---+
     *```
     * then for each monitor you would call it like this:
     *```js
     * const w = 1920;
     * const h = 1080;
     * const fullWidth = w * 3;
     * const fullHeight = h * 2;
     *
     * // --A--
     * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
     * // --B--
     * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
     * // --C--
     * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
     * // --D--
     * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
     * // --E--
     * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
     * // --F--
     * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
     * ```
     *
     * Note there is no reason monitors have to be the same size or in a grid.
     *
     * @param {number} fullWidth - The full width of multiview setup.
     * @param {number} fullHeight - The full height of multiview setup.
     * @param {number} x - The horizontal offset of the subcamera.
     * @param {number} y - The vertical offset of the subcamera.
     * @param {number} width - The width of subcamera.
     * @param {number} height - The height of subcamera.
     */

Parameters:

  • fullWidth number
  • fullHeight number
  • x number
  • y number
  • width number
  • height number

Returns: void

Calls:

  • this.updateProjectionMatrix
Code
setViewOffset( fullWidth, fullHeight, x, y, width, height ) {

        this.aspect = fullWidth / fullHeight;

        if ( this.view === null ) {

            this.view = {
                enabled: true,
                fullWidth: 1,
                fullHeight: 1,
                offsetX: 0,
                offsetY: 0,
                width: 1,
                height: 1
            };

        }

        this.view.enabled = true;
        this.view.fullWidth = fullWidth;
        this.view.fullHeight = fullHeight;
        this.view.offsetX = x;
        this.view.offsetY = y;
        this.view.width = width;
        this.view.height = height;

        this.updateProjectionMatrix();

    }

PerspectiveCamera.clearViewOffset(): void

JSDoc:

/**
     * Removes the view offset from the projection matrix.
     */

Returns: void

Calls:

  • this.updateProjectionMatrix
Code
clearViewOffset() {

        if ( this.view !== null ) {

            this.view.enabled = false;

        }

        this.updateProjectionMatrix();

    }

PerspectiveCamera.updateProjectionMatrix(): void

JSDoc:

/**
     * Updates the camera's projection matrix. Must be called after any change of
     * camera properties.
     */

Returns: void

Calls:

  • Math.tan
  • this.getFilmWidth
  • this.projectionMatrix.makePerspective
  • this.projectionMatrixInverse.copy( this.projectionMatrix ).invert
Code
updateProjectionMatrix() {

        const near = this.near;
        let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom;
        let height = 2 * top;
        let width = this.aspect * height;
        let left = - 0.5 * width;
        const view = this.view;

        if ( this.view !== null && this.view.enabled ) {

            const fullWidth = view.fullWidth,
                fullHeight = view.fullHeight;

            left += view.offsetX * width / fullWidth;
            top -= view.offsetY * height / fullHeight;
            width *= view.width / fullWidth;
            height *= view.height / fullHeight;

        }

        const skew = this.filmOffset;
        if ( skew !== 0 ) left += near * skew / this.getFilmWidth();

        this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem, this.reversedDepth );

        this.projectionMatrixInverse.copy( this.projectionMatrix ).invert();

    }

PerspectiveCamera.toJSON(meta: any): any

Parameters:

  • meta any

Returns: any

Calls:

  • super.toJSON
  • Object.assign
Code
toJSON( meta ) {

        const data = super.toJSON( meta );

        data.object.fov = this.fov;
        data.object.zoom = this.zoom;

        data.object.near = this.near;
        data.object.far = this.far;
        data.object.focus = this.focus;

        data.object.aspect = this.aspect;

        if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );

        data.object.filmGauge = this.filmGauge;
        data.object.filmOffset = this.filmOffset;

        return data;

    }

Classes

PerspectiveCamera

Class Code
class PerspectiveCamera extends Camera {

    /**
     * Constructs a new perspective camera.
     *
     * @param {number} [fov=50] - The vertical field of view.
     * @param {number} [aspect=1] - The aspect ratio.
     * @param {number} [near=0.1] - The camera's near plane.
     * @param {number} [far=2000] - The camera's far plane.
     */
    constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) {

        super();

        /**
         * This flag can be used for type testing.
         *
         * @type {boolean}
         * @readonly
         * @default true
         */
        this.isPerspectiveCamera = true;

        this.type = 'PerspectiveCamera';

        /**
         * The vertical field of view, from bottom to top of view,
         * in degrees.
         *
         * @type {number}
         * @default 50
         */
        this.fov = fov;

        /**
         * The zoom factor of the camera.
         *
         * @type {number}
         * @default 1
         */
        this.zoom = 1;

        /**
         * The camera's near plane. The valid range is greater than `0`
         * and less than the current value of {@link PerspectiveCamera#far}.
         *
         * Note that, unlike for the {@link OrthographicCamera}, `0` is <em>not</em> a
         * valid value for a perspective camera's near plane.
         *
         * @type {number}
         * @default 0.1
         */
        this.near = near;

        /**
         * The camera's far plane. Must be greater than the
         * current value of {@link PerspectiveCamera#near}.
         *
         * @type {number}
         * @default 2000
         */
        this.far = far;

        /**
         * Object distance used for stereoscopy and depth-of-field effects. This
         * parameter does not influence the projection matrix unless a
         * {@link StereoCamera} is being used.
         *
         * @type {number}
         * @default 10
         */
        this.focus = 10;

        /**
         * The aspect ratio, usually the canvas width / canvas height.
         *
         * @type {number}
         * @default 1
         */
        this.aspect = aspect;

        /**
         * Represents the frustum window specification. This property should not be edited
         * directly but via {@link PerspectiveCamera#setViewOffset} and {@link PerspectiveCamera#clearViewOffset}.
         *
         * @type {?Object}
         * @default null
         */
        this.view = null;

        /**
         * Film size used for the larger axis. Default is `35` (millimeters). This
         * parameter does not influence the projection matrix unless {@link PerspectiveCamera#filmOffset}
         * is set to a nonzero value.
         *
         * @type {number}
         * @default 35
         */
        this.filmGauge = 35;

        /**
         * Horizontal off-center offset in the same unit as {@link PerspectiveCamera#filmGauge}.
         *
         * @type {number}
         * @default 0
         */
        this.filmOffset = 0;

        this.updateProjectionMatrix();

    }

    copy( source, recursive ) {

        super.copy( source, recursive );

        this.fov = source.fov;
        this.zoom = source.zoom;

        this.near = source.near;
        this.far = source.far;
        this.focus = source.focus;

        this.aspect = source.aspect;
        this.view = source.view === null ? null : Object.assign( {}, source.view );

        this.filmGauge = source.filmGauge;
        this.filmOffset = source.filmOffset;

        return this;

    }

    /**
     * Sets the FOV by focal length in respect to the current {@link PerspectiveCamera#filmGauge}.
     *
     * The default film gauge is 35, so that the focal length can be specified for
     * a 35mm (full frame) camera.
     *
     * @param {number} focalLength - Values for focal length and film gauge must have the same unit.
     */
    setFocalLength( focalLength ) {

        /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */
        const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;

        this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope );
        this.updateProjectionMatrix();

    }

    /**
     * Returns the focal length from the current {@link PerspectiveCamera#fov} and
     * {@link PerspectiveCamera#filmGauge}.
     *
     * @return {number} The computed focal length.
     */
    getFocalLength() {

        const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov );

        return 0.5 * this.getFilmHeight() / vExtentSlope;

    }

    /**
     * Returns the current vertical field of view angle in degrees considering {@link PerspectiveCamera#zoom}.
     *
     * @return {number} The effective FOV.
     */
    getEffectiveFOV() {

        return RAD2DEG * 2 * Math.atan(
            Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom );

    }

    /**
     * Returns the width of the image on the film. If {@link PerspectiveCamera#aspect} is greater than or
     * equal to one (landscape format), the result equals {@link PerspectiveCamera#filmGauge}.
     *
     * @return {number} The film width.
     */
    getFilmWidth() {

        // film not completely covered in portrait format (aspect < 1)
        return this.filmGauge * Math.min( this.aspect, 1 );

    }

    /**
     * Returns the height of the image on the film. If {@link PerspectiveCamera#aspect} is greater than or
     * equal to one (landscape format), the result equals {@link PerspectiveCamera#filmGauge}.
     *
     * @return {number} The film width.
     */
    getFilmHeight() {

        // film not completely covered in landscape format (aspect > 1)
        return this.filmGauge / Math.max( this.aspect, 1 );

    }

    /**
     * Computes the 2D bounds of the camera's viewable rectangle at a given distance along the viewing direction.
     * Sets `minTarget` and `maxTarget` to the coordinates of the lower-left and upper-right corners of the view rectangle.
     *
     * @param {number} distance - The viewing distance.
     * @param {Vector2} minTarget - The lower-left corner of the view rectangle is written into this vector.
     * @param {Vector2} maxTarget - The upper-right corner of the view rectangle is written into this vector.
     */
    getViewBounds( distance, minTarget, maxTarget ) {

        _v3.set( - 1, - 1, 0.5 ).applyMatrix4( this.projectionMatrixInverse );

        minTarget.set( _v3.x, _v3.y ).multiplyScalar( - distance / _v3.z );

        _v3.set( 1, 1, 0.5 ).applyMatrix4( this.projectionMatrixInverse );

        maxTarget.set( _v3.x, _v3.y ).multiplyScalar( - distance / _v3.z );

    }

    /**
     * Computes the width and height of the camera's viewable rectangle at a given distance along the viewing direction.
     *
     * @param {number} distance - The viewing distance.
     * @param {Vector2} target - The target vector that is used to store result where x is width and y is height.
     * @returns {Vector2} The view size.
     */
    getViewSize( distance, target ) {

        this.getViewBounds( distance, _minTarget, _maxTarget );

        return target.subVectors( _maxTarget, _minTarget );

    }

    /**
     * Sets an offset in a larger frustum. This is useful for multi-window or
     * multi-monitor/multi-machine setups.
     *
     * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
     * the monitors are in grid like this
     *```
     *   +---+---+---+
     *   | A | B | C |
     *   +---+---+---+
     *   | D | E | F |
     *   +---+---+---+
     *```
     * then for each monitor you would call it like this:
     *```js
     * const w = 1920;
     * const h = 1080;
     * const fullWidth = w * 3;
     * const fullHeight = h * 2;
     *
     * // --A--
     * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
     * // --B--
     * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
     * // --C--
     * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
     * // --D--
     * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
     * // --E--
     * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
     * // --F--
     * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
     * ```
     *
     * Note there is no reason monitors have to be the same size or in a grid.
     *
     * @param {number} fullWidth - The full width of multiview setup.
     * @param {number} fullHeight - The full height of multiview setup.
     * @param {number} x - The horizontal offset of the subcamera.
     * @param {number} y - The vertical offset of the subcamera.
     * @param {number} width - The width of subcamera.
     * @param {number} height - The height of subcamera.
     */
    setViewOffset( fullWidth, fullHeight, x, y, width, height ) {

        this.aspect = fullWidth / fullHeight;

        if ( this.view === null ) {

            this.view = {
                enabled: true,
                fullWidth: 1,
                fullHeight: 1,
                offsetX: 0,
                offsetY: 0,
                width: 1,
                height: 1
            };

        }

        this.view.enabled = true;
        this.view.fullWidth = fullWidth;
        this.view.fullHeight = fullHeight;
        this.view.offsetX = x;
        this.view.offsetY = y;
        this.view.width = width;
        this.view.height = height;

        this.updateProjectionMatrix();

    }

    /**
     * Removes the view offset from the projection matrix.
     */
    clearViewOffset() {

        if ( this.view !== null ) {

            this.view.enabled = false;

        }

        this.updateProjectionMatrix();

    }

    /**
     * Updates the camera's projection matrix. Must be called after any change of
     * camera properties.
     */
    updateProjectionMatrix() {

        const near = this.near;
        let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom;
        let height = 2 * top;
        let width = this.aspect * height;
        let left = - 0.5 * width;
        const view = this.view;

        if ( this.view !== null && this.view.enabled ) {

            const fullWidth = view.fullWidth,
                fullHeight = view.fullHeight;

            left += view.offsetX * width / fullWidth;
            top -= view.offsetY * height / fullHeight;
            width *= view.width / fullWidth;
            height *= view.height / fullHeight;

        }

        const skew = this.filmOffset;
        if ( skew !== 0 ) left += near * skew / this.getFilmWidth();

        this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem, this.reversedDepth );

        this.projectionMatrixInverse.copy( this.projectionMatrix ).invert();

    }

    toJSON( meta ) {

        const data = super.toJSON( meta );

        data.object.fov = this.fov;
        data.object.zoom = this.zoom;

        data.object.near = this.near;
        data.object.far = this.far;
        data.object.focus = this.focus;

        data.object.aspect = this.aspect;

        if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );

        data.object.filmGauge = this.filmGauge;
        data.object.filmOffset = this.filmOffset;

        return data;

    }

}

Methods

copy(source: any, recursive: any): this
Code
copy( source, recursive ) {

        super.copy( source, recursive );

        this.fov = source.fov;
        this.zoom = source.zoom;

        this.near = source.near;
        this.far = source.far;
        this.focus = source.focus;

        this.aspect = source.aspect;
        this.view = source.view === null ? null : Object.assign( {}, source.view );

        this.filmGauge = source.filmGauge;
        this.filmOffset = source.filmOffset;

        return this;

    }
setFocalLength(focalLength: number): void
Code
setFocalLength( focalLength ) {

        /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */
        const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;

        this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope );
        this.updateProjectionMatrix();

    }
getFocalLength(): number
Code
getFocalLength() {

        const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov );

        return 0.5 * this.getFilmHeight() / vExtentSlope;

    }
getEffectiveFOV(): number
Code
getEffectiveFOV() {

        return RAD2DEG * 2 * Math.atan(
            Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom );

    }
getFilmWidth(): number
Code
getFilmWidth() {

        // film not completely covered in portrait format (aspect < 1)
        return this.filmGauge * Math.min( this.aspect, 1 );

    }
getFilmHeight(): number
Code
getFilmHeight() {

        // film not completely covered in landscape format (aspect > 1)
        return this.filmGauge / Math.max( this.aspect, 1 );

    }
getViewBounds(distance: number, minTarget: Vector2, maxTarget: Vector2): void
Code
getViewBounds( distance, minTarget, maxTarget ) {

        _v3.set( - 1, - 1, 0.5 ).applyMatrix4( this.projectionMatrixInverse );

        minTarget.set( _v3.x, _v3.y ).multiplyScalar( - distance / _v3.z );

        _v3.set( 1, 1, 0.5 ).applyMatrix4( this.projectionMatrixInverse );

        maxTarget.set( _v3.x, _v3.y ).multiplyScalar( - distance / _v3.z );

    }
getViewSize(distance: number, target: Vector2): Vector2
Code
getViewSize( distance, target ) {

        this.getViewBounds( distance, _minTarget, _maxTarget );

        return target.subVectors( _maxTarget, _minTarget );

    }
setViewOffset(fullWidth: number, fullHeight: number, x: number, y: number, width: number, height: number): void
Code
setViewOffset( fullWidth, fullHeight, x, y, width, height ) {

        this.aspect = fullWidth / fullHeight;

        if ( this.view === null ) {

            this.view = {
                enabled: true,
                fullWidth: 1,
                fullHeight: 1,
                offsetX: 0,
                offsetY: 0,
                width: 1,
                height: 1
            };

        }

        this.view.enabled = true;
        this.view.fullWidth = fullWidth;
        this.view.fullHeight = fullHeight;
        this.view.offsetX = x;
        this.view.offsetY = y;
        this.view.width = width;
        this.view.height = height;

        this.updateProjectionMatrix();

    }
clearViewOffset(): void
Code
clearViewOffset() {

        if ( this.view !== null ) {

            this.view.enabled = false;

        }

        this.updateProjectionMatrix();

    }
updateProjectionMatrix(): void
Code
updateProjectionMatrix() {

        const near = this.near;
        let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom;
        let height = 2 * top;
        let width = this.aspect * height;
        let left = - 0.5 * width;
        const view = this.view;

        if ( this.view !== null && this.view.enabled ) {

            const fullWidth = view.fullWidth,
                fullHeight = view.fullHeight;

            left += view.offsetX * width / fullWidth;
            top -= view.offsetY * height / fullHeight;
            width *= view.width / fullWidth;
            height *= view.height / fullHeight;

        }

        const skew = this.filmOffset;
        if ( skew !== 0 ) left += near * skew / this.getFilmWidth();

        this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem, this.reversedDepth );

        this.projectionMatrixInverse.copy( this.projectionMatrix ).invert();

    }
toJSON(meta: any): any
Code
toJSON( meta ) {

        const data = super.toJSON( meta );

        data.object.fov = this.fov;
        data.object.zoom = this.zoom;

        data.object.near = this.near;
        data.object.far = this.far;
        data.object.focus = this.focus;

        data.object.aspect = this.aspect;

        if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );

        data.object.filmGauge = this.filmGauge;
        data.object.filmOffset = this.filmOffset;

        return data;

    }