Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to increase perspective without stretching foreground in three.js

I have a three.js scene with a textured floor plane that needs a very specific perspective to align with a static 2D overlay

image here

My problem is that the more I increase the camera FOV to get the perspective I need the more the foreground stretches and I don't want that. I seem to need a very high FOV (~ 120 - 150) to make the texture follow the wall and this is so high that the camera is rendering things positioned BEHIND it. I need to move the camera almost to the center of the scene just to show the whole floor and this just feels wrong. How can I adjust this scene so I get the right perspective without the distortion?

Live example: http://warriorhut.net/testing/shapes/backend/room/view.basic.php

The relevant camera settings are:

var WIDTH = 1024;
var HEIGHT = 683;
var FOV = 140; // Increases perspective as it goes higher but also distortion

camera =  new THREE.PerspectiveCamera(FOV, WIDTH/HEIGHT, .1, 3000);

camera.position.x = 0;
camera.position.y = 200;
camera.position.z = 0;  
//camera.lookAt(scene.position);
camera.lookAt(new THREE.Vector3( 0, 100, -400 )); // look at the back of the room
camera.updateProjectionMatrix();
like image 801
SpliFF Avatar asked Dec 15 '22 14:12

SpliFF


2 Answers

Adding a virtual object to an existing image requires matching the original camera parameters as closely as possible. In this case because the image is synthetic, ideally you would obtain those parameters from whoever generated it. Otherwise we can try to infer some of the parameters by taking measurements on the image.

I'll make the standard assumption of a pinhole camera, and further assume that the optical axis is at the center of the image. Parallel lines in the scene will meet at a vanishing point. In this image we can determine only one vanishing point with any accuracy:

enter image description here

The parallel lines here are also parallel to the ground plane, which means that the vanishing point is on the horizon. The horizon is thus slightly below the center of this image, which means that the camera is pitched slightly upwards.

How much upwards? The vanishing point is about 0.022 image heights (15 pixels) below the center of the image. We can express the indicated camera pitch in terms of the vertical field of view:

pitch = arctan(2 * tan(vfov/2) * 0.022)

We can similarly determine the yaw of the camera with respect to the parallel lines. The vanishing point is about 0.010 image heights right of center so the camera yaws slightly left.

yaw = arctan(2 * tan(vfov/2) * -0.010)

To express this in the three.js application, I modified the camera configuration section of the code like this:

var FOV = 75;

[...]

camera.position.x = 0;
camera.position.y = 200;
camera.position.z = 512;

var lookTarget = new THREE.Vector3().copy(camera.position);
var tanScale = 2 * Math.tan(FOV/2 * Math.PI/180);
lookTarget.x += -0.010 * tanScale;
lookTarget.y +=  0.022 * tanScale;
lookTarget.z += -1;
camera.lookAt(lookTarget);

camera.updateProjectionMatrix();

I moved the camera backwards (toward positive Z) a bit to make most of the floor geometry visible (this doesn't change the vanishing point of the rendered geometry). Then I changed FOV to 75 degrees (still quite a wide angle view), and the texture pattern now automatically converges at the vanishing point with much less perspective distortion:

enter image description here

Note that you can now see the end of the floor geometry because assuming a smaller field of view increases the assumed depth of the image, so you may need to increase the size of the floor. You can modify the field of view to adjust the texture foreshortening and the camera code should keep the vanishing point in the same place. I don't believe there are sufficient features in this particular image to let us estimate its actual focal length, but it it may be possible in other images - in that case we could deduce the field of view as well.

like image 166
rhashimoto Avatar answered May 14 '23 10:05

rhashimoto


Solved through trial and error. Solution was to increase the camera tilt upwards to increase the distance falloff rather than increasing FOV

enter image description here

var FOV = 100;
camera.position.x = -100;
camera.position.y = 540;
camera.position.z = 3500;   // Moved further back
//camera.lookAt(scene.position);
camera.lookAt(new THREE.Vector3( 0, 590, -1000 )); // look at the back of the room AND TILT UPWARDS!
like image 26
SpliFF Avatar answered May 14 '23 12:05

SpliFF