How to convert Orthographic camera to Perspective and back?

2.4k views Asked by At

I'm using the combinedcamera.js along with the orbitcontrols.js https://github.com/mrdoob/three.js/blob/master/examples/js/cameras/CombinedCamera.js https://github.com/mrdoob/three.js/blob/master/examples/js/controls/OrbitControls.js

I have it working so that the cameras can be switched, and both can be zoomed in and out. However, orbitcontrols re-positions the perspective camera to simulate the zoom and it doesn't do this with the orthographic camera(it changes the fov,etc)..

This ends up causing the camera far frustum plane to move in perspective mode(I want this), and it doesn't move in Orthographic mode(I want it to move).

I have fixed this, by making both the perspective and orthographic camera get repositioned. The orthographic camera doesn't use its position to determine the zoom so there is a problem.

The problem is that when I switch between cameras, they don't appear to have the same zoom amount.

I guess the question is, how do I make the orthographic camera rely on camera position to determine the zoom amount so that it always looks to have similar zoom as perspective camera?

1

There are 1 answers

0
user3591153 On BEST ANSWER

Okay, so after experimenting a lot, I found a hackish way to get a result that is close enough. I found accidentally that if the far frustum value was set to 25, it would work perfectly.. so I made an equation to compensate if the value is different. It's close enough, but perhaps someone could see where it could be improved?

I replaced in combinedcamera.js

halfHeight /= this.zoom;
halfWidth /= this.zoom;

with

halfHeight /= ((this.cameraP.far/25)*this.zoom);
halfWidth /= ((this.cameraP.far/25)*this.zoom);

And I added this line in combinedcamera.js

this.cameraO.far = this.cameraP.far+((this.cameraP.far/25)*this.zoom)-0.5;

right before this

this.cameraO.updateProjectionMatrix();

here's the full section

 THREE.CombinedCamera.prototype.toOrthographic = function () {

// Switches to the Orthographic camera estimating viewport from Perspective

var fov = this.fov;
var aspect = this.cameraP.aspect;
var near = this.cameraP.near;
var far = this.cameraP.far;

// The size that we set is the mid plane of the viewing frustum

var hyperfocus = ( near + far ) / 2;

var halfHeight = Math.tan( fov * Math.PI / 180 / 2 ) * hyperfocus;
var planeHeight = 2 * halfHeight;
var planeWidth = planeHeight * aspect;
var halfWidth = planeWidth / 2;

halfHeight /= ((this.cameraP.far/25)*this.zoom);
halfWidth /= ((this.cameraP.far/25)*this.zoom);

this.cameraO.left = -halfWidth;
this.cameraO.right = halfWidth;
this.cameraO.top = halfHeight;
this.cameraO.bottom = -halfHeight;

// this.cameraO.left = -farHalfWidth;
// this.cameraO.right = farHalfWidth;
// this.cameraO.top = farHalfHeight;
// this.cameraO.bottom = -farHalfHeight;

// this.cameraO.left = this.left / this.zoom;
// this.cameraO.right = this.right / this.zoom;
// this.cameraO.top = this.top / this.zoom;
// this.cameraO.bottom = this.bottom / this.zoom;

this.cameraO.far = this.cameraP.far+((this.cameraP.far/25)*this.zoom)-0.5;
this.cameraO.updateProjectionMatrix();

this.near = this.cameraO.near;
this.far = this.cameraO.far;
this.projectionMatrix = this.cameraO.projectionMatrix;

this.inPerspectiveMode = false;
this.inOrthographicMode = true;

 };

also I change the this.zoom to just 1 for perspective camera

 THREE.CombinedCamera.prototype.toPerspective = function () {

// Switches to the Perspective Camera

this.near = this.cameraP.near;
this.far = this.cameraP.far;

this.cameraP.fov =  this.fov / 1 ;

this.cameraP.updateProjectionMatrix();

this.projectionMatrix = this.cameraP.projectionMatrix;

this.inPerspectiveMode = true;
this.inOrthographicMode = false;

 };

Also, I had to adjust the orbitcontrols

this.dollyIn = function ( dollyScale ) {

    if ( dollyScale === undefined ) {

        dollyScale = getZoomScale();

    }

        scale /= dollyScale;
        scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) );
};

this.dollyOut = function ( dollyScale ) {

    if ( dollyScale === undefined ) {

        dollyScale = getZoomScale();

    }

        scale *= dollyScale;
        scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) );
};