I'm working on free-view tool for 360-degree panorama made with three.js. I want the camera to rotate when user drags point from the screen leaving that point exactly under the mouse pointer.
Geometry is simple box geometry around world origin, camera is perspective camera located at the origin:
this.mesh = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2, 0, 0, 0),
new THREE.MeshFaceMaterial(_array_of_THREE.MeshBasicMaterial_));
this.camera = new THREE.PerspectiveCamera(90, width / height, 0.1, 2);
I have a solution that works inaccurately, it is based on the following steps:
- When user starts dragging, I remember the ray in world coordinates pointing to the screen point of drag start.
- Whenever I want to adjust camera (currently - on drag end only), I compute ray in world coordinates that points on current position of mouse pointer.
- I then compute axis and angle required to bring first ray to the second by rotation.
- I rotate vector of camera direction by that angle around that axis.
- Finally, I set camera to look at the new direction.
Here is the code for steps 2-5:
adjustCamera: function(cameraDirection, worldRay, screenPoint){
var angle;
this.camera.lookAt(cameraDirection);
this.raycaster.setFromCamera(screenPoint, this.camera);
this.axis.copy(this.raycaster.ray.direction);
angle = this.axis.angleTo(worldRay);
this.axis.cross(worldRay).normalize();
this.ray.copy(cameraDirection);
this.ray.applyAxisAngle(this.axis, angle).normalize();
this.camera.lookAt(this.ray);
}
I realized why this schema doesn't work. Camera orientation changed this way gets some roll (when rotation axis has non-zero z
coordinate ), and this is eliminated by lookAt
- it strips roll away, leaving only pitch and yaw. This leads to some inaccuracy, and it grows when initial and final rays are further away and when initial camera position has higher pitch. And I'm stuck here now, having no idea how to compute camera position without roll.
So, the question is: How can I accurately rotate the camera to bring specific ray to specific screen point? (preferrably suitable for the schema I'm using)
EDIT: There actually could be more than one (seems that no more than two, provided the ray doesn't point to nadir or zenith) correct (with no roll) camera positions that project world ray to the specific point on the screen.
Imagine following example: ray close to zenith should be matched with point in upper half of the screen. The first camera option is obvious, and the second in this case is rotated around vertical axis by 180 degrees and with higher pitch. In the first option zenith is projected on the screen above the lock point, and in the second zenith shows below.
In this ambiguous case option closest to initial camera direction should be chosen.