I'm trying to zoom to fit the object to the center point when panned or rotate to different point.
I have tried it by capturing the center point of the object through bounding box and center point of the scene and passing it to the camera, but still it doesn't have a clear output. The object is getting fit to the camera but not to the center point when I load bigger models.
I have attached a fiddle
document.querySelector('button').addEventListener('click', () => {
const boundingBox = new THREE.Box3()
boundingBox.setFromObject(cube)
const center = new THREE.Vector3()
boundingBox.getCenter(center)
camera.position.y = center.y
camera.position.x = center.x
camera.updateProjectionMatrix()
const size = new THREE.Vector3()
boundingBox.getSize(size)
const fov = camera.fov * (Math.PI / 180)
const maxDim = Math.max(size.x, size.y, size.z)
let cameraZ = Math.abs((maxDim / 4) * Math.tan(fov * 2))
camera.position.z = cameraZ
camera.updateProjectionMatrix()
camera.lookAt(center)
})
Related
I can't figure out how to properly rotate an object in ThreeJS. The object is a simple box geometry that is rendered from above somewhere on the screen.
Codepen with the full code.
The object is supposed to rotate around it's own Y axis (the vertical axis) to always face the mouse cursor. I can get it to rotate as the cursor moves around the global axis in the middle of the screen, but not when the cursor moves around the object's own local axis.
UPDATE: I got it to work using ray casting. See code further down or in the codepen.
The orthographic camera is set up like this:
camera = new THREE.OrthographicCamera(
window.innerWidth / - 2, // left
window.innerWidth / 2, // right
window.innerHeight / 2, // top
window.innerHeight / - 2, // bottom
0, // near
1000 ); // far
camera.position.set(0, 0, 500)
camera.updateProjectionMatrix()
The object is set up like this:
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
const material = new THREE.MeshBasicMaterial( { color: "grey" } );
const mesh = new THREE.Mesh( geometry, material );
Code for handling rotation at mousemove:
function onMouseMove(event) {
// Get mouse position
let mousePos = new THREE.Vector2();
mousePos.set(
(event.clientX / window.innerWidth) * 2 - 1, // x
-(event.clientY / window.innerHeight) * 2 + 1); // y
// Calculate angle
let angle = Math.atan2(mousePos.y, mousePos.x);
// Add rotation to object
mesh.rotation.set(
0, // x
angle, // y
0) // z
}
I have also tried
mesh.rotateY(angle)
but this only makes the object spinn like a helicopter.
It's obvious the rotation needs to be based on the relationship between the cursor and the local axis rather than the global axis. I just can't figure out how to achieve that.
UPDATE
I have added a codepen at the top of the question.
UPDATE
I got it to work using the following method with ray casting.
let plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
let pointOfIntersection = new THREE.Vector3();
let raycaster = new THREE.Raycaster();
let mousePos = new THREE.Vector2();
mousePos.set(
(event.clientX / window.innerWidth) * 2 - 1, // x
-(event.clientY / window.innerHeight) * 2 + 1))
raycaster.setFromCamera(mousePos, camera);
raycaster.ray.intersectPlane(plane, pointOfIntersection);
mesh.lookAt(pointOfIntersection)
I have some code that converts a perspective camera to an orthographic camera. The problem is that when I make the conversion, the model becomes very tiny and hard to see.
I have calculated the zoom factor for the orthographic camera, based on the distance and the FOV. Are there any other properties that I need to set on the orthographic camera (e.g. clipping plane, etc..)?
I believe the position remains the same. I'm not sure what else I need to calculate.
fieldOfView = viewInfo.fov;
var getCameraPosition = function() {
return viewer._viewport._implementation.getCamera()._nativeCamera.position;
};
// Calculate the delta position between the camera and the object
var getPositionDelta = function(position1, position2) {
return {
x: position1.x - position2.x,
y: position1.y - position2.y,
z: position1.z - position2.z
}
};
var getDistance = function(positionDelta, cameraDirection) {
return dot(positionDelta, cameraDirection);
};
distance = getDistance(positionDelta, cameraDirection),
var depth = distance;
var viewportWidth = view.getDomRef().getBoundingClientRect().width;
var viewportHeight = view.getDomRef().getBoundingClientRect().height;
var aspect = viewportWidth / viewportHeight;
var height_ortho = depth * 2 * Math.atan( fieldOfView * (Math.PI/180) / 2 )
var width_ortho = height_ortho * aspect;
var near = viewInfo.near, far = viewInfo.far;
var newCamera = new THREE.OrthographicCamera(
width_ortho / -2, width_ortho / 2,
height_ortho / 2, height_ortho / -2,
near, far );
newCamera.position.copy( viewInfo.position );
var sCamera = new vk.threejs.OrthographicCamera(); //framework creatio of threejs cam
sCamera.setZoomFactor(orthoZoomFactor);
sCamera.setCameraRef(newCamera);
view.getViewport().setCamera(sCamera);
I also tried setting the same camera properties (e.g. clipping planes etc) of the perspective for the orthographic and I still had the same problem.
I guess I am missing some property or calculation required to put the object in the same position as when it was in perspective camera view.
Let's assume you have a perspective view with a given vertical field of view angle fov_y (in degrees) and you know the size of the viewport width and height. Furthermore, you have the near and far plane. These are the values which you use to setup the THREE.PerspectiveCamera:
perspCamera = new THREE.PerspectiveCamera( fov_y, width / height, near, far );
Also, you know the position of the object and the position of the camera. An object doesn't have only a single position, but you have to choose a representative position for its depth.
First you have to calculate the depth of the object.
var v3_object = .... // THREE.Vector3 : positon of the object
var v3_camera = perspCamera.position;
var line_of_sight = new THREE.Vector3();
perspCamera.getWorldDirection( line_of_sight );
var v3_distance = v3_object.clone().sub( v3_camera );
depth = v3_distance.dot( line_of_sight );
Then you have to calculate the "size" of the rectangle which is projected to the viewport at the depth:
aspect = width / height;
height_ortho = depth * 2 * Math.atan( fov_y*(Math.PI/180) / 2 )
width_ortho = height_ortho * aspect;
With these values the THREE.OrthographicCamera can be setup like this:
var orthoCamera = new THREE.OrthographicCamera(
width_ortho / -2, width_ortho / 2,
height_ortho / 2, height_ortho / -2,
near, far );
orthoCamera.position.copy( perspCamera.position );
The positon and direction of the perspective camera can be committed to the orthographic camera like this:
orthoCamera.position.copy( perspCamera.position );
orthoCamera.quaternion.copy( perspCamera.quaternion );
See also stackoverflow question Three.js - Find the current LookAt of a camera?
I am trying to load(dynamically) object files using THREE.OBJLoader and place them in the center of a scene(or canvas), so that the whole object can be visible in the Camera. Objects are dynamic, so I don't have fixed height or width data.
What I have got:
What I want:
What I have referred to get to this point:
Three.js zoom to fit width of objects (ignoring height)
How to Fit Camera to Object
Smart Centering and Scaling after Model Import in three.js
Adjusting camera for visible Three.js shape
Calculate camera zoom required for object to fit in screen height
Move camera to fit 3D scene
Three.js calculate object distance required to fill screen
How to calculate the z-distance of a camera to view an image at 100% of its original scale in a 3D space
Code:
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
controls = new THREE.TrackballControls(camera);
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;
controls.panSpeed = 2;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
// scene
scene = new THREE.Scene();
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl(path);
mtlLoader.setPath(path);
mtlLoader.setMaterialOptions({
ignoreZeroRGBs: true
});
mtlLoader.load(path, function(materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath(path);
objLoader.load(path, function(object) {
var helperBox = new THREE.BoundingBoxHelper(object, 0x888888);
helperBox.update();
scene.add(helperBox);
//Scene
scene.add(object);
var boxFrmScene = new THREE.Box3().setFromObject(scene);
var height = Math.max(boxFrmScene.size().y, boxFrmScene.size().x);
var dist = height / (2 * Math.tan(camera.fov * Math.PI / 360));
var pos = scene.position;
var boundingSphere = boxFrmScene.getBoundingSphere();
var center = boundingSphere.center;
camera.position.set(center.x, center.y, (dist * 1.1));
camera.lookAt(pos);
}, function(error) {
console.log(error);
});
}, function(error) {
console.log(error);
});
The deadpool object I used is from here : http://tf3dm.com/3d-model/deadpool-42722.html . I don't know if I have been reading the right questions. I would be very glad if someone can point me in the right direction. Thanks in advance.
PS: I'm not good with 3D maths.
Edit:
I have tried solution given at this:How to Fit Camera to Object but it has not solved my issue. In fact its flipping my object upside down. I am trying to position the object to center of the camera.
Edit 2: I have partially fixed this. Now the object is within the camera frustum and fully visible. Now I need to find a way to center it. I changed the whole code below scene.add(object);
var pos = scene.position;
camera.position.set(pos.x, pos.y, 100);
camera.lookAt(pos);
var boxFrmScene = new THREE.Box3().setFromObject(scene);
var height = Math.max(boxFrmScene.size().y, boxFrmScene.size().x);
var fov = camera.fov * (Math.PI / 180);
var distance = Math.abs(height / Math.sin(fov / 2));
camera.position.set(pos.x, pos.y, distance + (height / 2));
camera.updateProjectionMatrix();
Try adding this code after creating mesh,
var box = new THREE.Box3().setFromObject(mesh);
box.center(mesh.position);
mesh.localToWorld(box);
mesh.position.multiplyScalar(-1);
This will bring your object to the center of the screen
I just try to use the THREE.OrbitControls to perform zooming in orthographic projection, but i dont get the behave that i want.
I think that is possible change the viewSize that multiply to left, right, top and bottom to create a something near of a zoom
Anyone hava a better idea?
Yes, you can implement a zooming effect with an OrthographicCamera by using the following pattern:
camera.zoom = 1.5;
camera.updateProjectionMatrix();
This works for PerspectiveCamera, too.
three.js r.70
I have a function to zoom the scene. Please try it.
function zoom_all(center,size) {
// get the current lookAt vector of OrthographicCamera camera
var vectorLookAt = new THREE.Vector3(0, 0, -1);
vectorLookAt.applyQuaternion(camera.quaternion);
vectorLookAt.normalize();
// move back along lookat vector to set new position of camera
vectorLookAt.multiplyScalar(-size);
camera.position = new THREE.Vector3().addVectors(center, vectorLookAt);
// get current size of camera
var viewSize = 2 * camera.top;
var aspectRatio = 2 * camera.right / viewSize; // get aspectRatio of camera
// camera = new THREE.OrthographicCamera(
// -aspectRatio * viewSize / 2, aspectRatio * viewSize / 2,
// viewSize / 2, -viewSize / 2,
// 0.1, 1000);
// update new size for camera
viewSize = size;
// now update camera size
camera.left = -aspectRatio * viewSize / 2;
camera.right = aspectRatio * viewSize / 2;
camera.top = viewSize / 2;
camera.bottom = -viewSize / 2;
camera.near = 0.1;
camera.far = 2 * size;
camera.updateProjectionMatrix();
// you can set light to camera position
spotLight.position.set(camera.position.x, camera.position.y, camera.position.z);
// set center point for orbit control
orbitControls.center.set(center.x, center.y, center.z);
orbitControls.target.set(center.x, center.y, center.z);
}
If anyone is still interested, I edited OrbitControls to work with orthographic camera based on WestLangley's answer:
https://github.com/traversc/OrbitControls-orthographic_camera_fix/blob/master/OrbitControls.js
I have a CubeGeometry which the camera is looking at, and I want the camera to zoom so that the cube is entirely visible, but no larger.
My initial attempt was to convert the cube verticies to the camera coordinate system,
function toScreenXY(position, camera) {
var pos = position.clone();
var projScreenMat = new THREE.Matrix4();
projScreenMat.multiply(camera.projectionMatrix, camera.matrixWorldInverse);
projScreenMat.multiplyVector3( pos );
return pos;
}
function ScaleInView() {
camera.fov = 0.0;
for (var i=0; i<8; i++) {
proj2d = toScreenXY(cube.geometry.vertices[i],camera);
angle = 57.296 * Math.max(Math.atan(proj2d.x/proj2d.z), Math.atan(proj2d.y/proj2d.z));
camera.fov = Math.max(camera.fov,angle);
}
camera.updateProjectionMatrix();
}
I thought this would work, but sometimes it's too small, and other times too large (depending on the position of the camera).
I also need to do this for Orthographic Camera.
Edit:
I know how to do this when the cube is facing the camera, I'm looking for a way to do it when the camera is moved to some arbitrary (r, theta, phi) position (spherical polar coordinates; r is actually constant for my purposes).
Perspective Camera. If the camera is centered and viewing the cube head-on, define
dist = distance from the camera to the front face ( important ! ) 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 );
then the cube height will match the visible height.
Orthographic Camera. If the camera is centered and viewing the cube head-on, define
aspect = the aspect ratio of your window (i.e., width / height)
and
height = height of the cube.
Then construct your camera this way:
camera = new THREE.OrthographicCamera( -aspect * height/2, aspect * height/2, height/2, -height/2, near, far );
The cube height will match the visible height.
In either case, if the camera is not centered, or is otherwise viewing the cube at an angle, then the problem is more complicated.
Also, if the window is narrower than it is high, then the width is the constraining factor, and the problem is more complicated.
Multiplying by camera.matrixWorldInverse gives a vector in the camera's coordinates, but importantly does not apply perspective.
function toCameraCoords(position) {
return camera.matrixWorldInverse.multiplyVector3(position.clone());
}
We can then find the smallest angle that will fit all the box corners in the scene. arctan(D.x / D.z) gives the angle BCD where B is what the camera is looking at, C is the camera's position, and D the position of an object that you want to be visible in the camera coordinates.
In my case, the following ensures that the the cube boundbox is fully visible.
function ScaleInView() {
var tmp_fov = 0.0;
for (var i=0; i<8; i++) {
proj2d = toCameraCoords(boundbox.geometry.vertices[i]);
angle = 114.59 * Math.max( // 2 * (Pi / 180)
Math.abs(Math.atan(proj2d.x/proj2d.z) / camera.aspect),
Math.abs(Math.atan(proj2d.y/proj2d.z))
);
tmp_fov = Math.max(tmp_fov, angle);
}
camera.fov = tmp_fov + 5; // An extra 5 degrees keeps all lines visible
camera.updateProjectionMatrix();
}