How to Fit Camera to Object

2019-01-01 05:26发布

问题:

Using three.js I have the following.

  • A scene containing several Object3D instances
  • Several predefined camera Vector3 positions
  • A dynamic width/height of the canvas if the screen resizes
  • A user can select an object (from above)
  • A user can select a camera position (from above)

Given an object being viewed and the camera position they have chosen how do I compute the final camera position to \"best fit\" the object on screen?

If the camera positions are used \"as is\" on some screens the objects bleed over the edge of my viewport whilst others they appear smaller. I believe it is possible to fit the object to the camera frustum but haven\'t been able to find anything suitable.

回答1:

I am assuming you are using a perspective camera.

You can set the camera\'s position, field-of-view, or both.

The following calculation is exact for an object that is a cube, so think in terms of the object\'s bounding box, aligned to face the camera.

If the camera is centered and viewing the cube head-on, define

dist = distance from the camera to the _closest face_ of the cube

and

height = height of the cube.

If you set the camera field-of-view as follows

fov = 2 * Math.atan( height / ( 2 * dist ) ) * ( 180 / Math.PI ); // in degrees

then the cube height will match the visible height.

At this point, you can back the camera up a bit, or increase the field-of-view a bit.

If the field-of-view is fixed, then use the above equation to solve for the distance.


EDIT: If you want the cube width to match the visible width, let aspect be the aspect ratio of the canvas ( canvas width divided by canvas height ), and set the camera field-of-view like so

fov = 2 * Math.atan( ( width / aspect ) / ( 2 * dist ) ) * ( 180 / Math.PI ); // in degrees

three.js r.69



回答2:

Based on WestLangleys answer here is how you calculate the distance with a fixed camera field-of-view:

dist = height / 2 / Math.tan(Math.PI * fov / 360);


回答3:

To calculate how far away to place your camera to fit an object to the screen, you can use this formula (in Javascript):

// Convert camera fov degrees to radians
var fov = camera.fov * ( Math.PI / 180 ); 

// Calculate the camera distance
var distance = Math.abs( objectSize / Math.sin( fov / 2 ) );

Where objectSize is the height or width of the object. For cube/sphere objects you can use either the height or width. For a non-cube/non-sphere object, where length or width is greater, use var objectSize = Math.max( width, height ) to get the larger value.

Note that if your object position isn\'t at 0, 0, 0, you need to adjust your camera position to include the offset.

Here\'s a CodePen showing this in action. The relevant lines:

var fov = cameraFov * ( Math.PI / 180 );
var objectSize = 0.6 + ( 0.5 * Math.sin( Date.now() * 0.001 ) );

var cameraPosition = new THREE.Vector3(
    0,
    sphereMesh.position.y + Math.abs( objectSize / Math.sin( fov / 2 ) ),
    0
);

You can see that if you grab the window handle and resize it, the sphere still takes up 100% of the screen height. Additionally, the object is scaling up and down in a sine wave fashion (0.6 + ( 0.5 * Math.sin( Date.now() * 0.001 ) )), to show the camera position takes into account scale of the object.



回答4:

From user151496\'s suggestion about using the aspect ratio, this seems to work, although I\'ve only tested with a few different parameter sets.

var maxDim = Math.max(w, h);
var aspectRatio = w / h;        
var distance = maxDim/ 2 /  aspectRatio / Math.tan(Math.PI * fov / 360);