Can't get Three.js TrackballControl zoom to work - javascript

My code
//zoom and scaleFactor are both constants
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.OrthographicCamera(
window.innerWidth / - (zoom / scaleFactor),
window.innerWidth / (zoom / scaleFactor),
window.innerHeight / (zoom / scaleFactor),
window.innerHeight / - (zoom / scaleFactor),
-500, 1000
);
scene = new THREE.Scene();
controls = new THREE.TrackballControls(camera, renderer.domElement);
Hello people of stackoverflow, I can't seem to get zoom to work with the trackball control object in Three.js. I used the OrbitControls object and it worked swimmingly, but zoom simply isn't working when i use TrackballControls. I made sure that noZoom is false and played around with zoomSpeed, both to no avail. I also tried debugging the source code for TrackballControls but that didn't get me anywhere.
Is it relevant that I'm using an orthographic camera? It seems plausible to me that the zoom actually just moves the camera forward and back to achieve zoom, hence the zoom doesn't work with orthographic projection, but at the same time that would mess with the perspective of the image using a perspective camera.
I'd appreciate any help. Thank you!

It appears to me that the method of zooming that TrackallControls uses is actually to move the camera back and forth, hence no zoom is achieved when using an orthographic camera. What I did to get zoom is to add the following lines to my source of TrackballControls.js around line 480:
case 2:
// Zoom in pages
_zoomStart.y -= event.deltaY * 0.025;
//add the following 2 lines
object.zoom -= event.deltaY * 0.025;
object.updateProjectionMatrix();
break;
Do the same for the rest of the cases and you now have zoom when you scroll the mouse. It's not as fluid as the normal zoom though. Also, the code you add still runs even when you're using a perspective camera, so you'll have to put a check somewhere in there to prevent that if you want your zoom to work normally with a perspective camera.

Related

Setting camera position and fov manually with trackball controls (THREEJS)

I set position and fov of my perspective camera manually in THREE.JS. It behaves as expected. However, once I try to interact with the scene later on, through the TrackBall Controls, it just displays a black screen, no errors.
JS Fiddle
Relevant code:
var bbox = new THREE.Box3().setFromObject(scene);
var center = bbox.getCenter();
var size = bbox.getSize();
// update some controls properties
controls.target.set(center.x, center.y, center.z);
// position the camera on the y axis
camera.position.set(center.x, center.y - size.y, center.z);
// fit FOV
var dist = size.y / 2;
var fov = 2 * Math.atan( size.z / ( 2 * dist ) ) * ( 180 / Math.PI );
camera.fov = fov;
camera.updateProjectionMatrix();
Which step am I missing in order to be able to then interact properly with the scene
Thanks
==== EDITS
Working fiddle based on accepted answer: Fiddle
I think that it is not possible to correctly project a case when the camera "up" position is parallel to the vector defined by the camera position and target. The camera up position should specify how to orient the view in a plane orthogonal to the vector from camera position to the target but if the component of camera.up along that plane is zero it cannot work. In your code:
the camera "up" position is the default (0,1,0)
the vector from camera position to camera target is (0,size.y,0)
The simplest fix is probably to specify a different camera "up", i.e.
camera.up = new THREE.Vector3(0,0,1.);
or any other vector not parallel to the y direction.

unable to display three.js elements in a scene due to camera position

I have a scene with multiple elements, say a cube, cylinder, sphere and some more. I have set the camera position for the scene at (0,30,40) and am trying to display all other elements using this camera position.
However, I observe that in the code below, where I display a simple moving cube, it does not work with the camera positioning I need for the rest of the scene, but works with only another camera configuration.
See lines 30-32 in my javascript code below (I have commented it). Basically it works for camera position (0,-400,400) but does not work for camera position (0,30,40) which I what I need as part of my project settings, to accommodate the rest of the elements.
var angularSpeed = 0.2;
var lastTime = 0;
function animate(){
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
var angularChange = angularSpeed * timeDiff * 2 * Math.PI / 1000;
cube.rotation.y -= angularChange;
lastTime = time;
renderer.render(scene, camera);
requestAnimationFrame(function(){
animate();
});
}
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
/* Works */
camera.position.x = 0;
camera.position.y = -400;
camera.position.z = 400;
/* Does not work, but I want these to be the camera settings */
// camera.position.x = 0;
// camera.position.y = 30;
// camera.position.z = 40;
camera.rotation.x = 0.70;
var scene = new THREE.Scene();
var cube = new THREE.Mesh(new THREE.CubeGeometry(200,100,100), new THREE.MeshNormalMaterial());
scene.add(cube);
animate();
I have created a jsbin for the same.
How is this usually done? I know I can also change the position of the cube using it's APIs and by playing around with cube.position.x, cube.position.y and cube.position.z, keeping the camera settings required by the project (0,30,40) in this case, and hope to find some cube position where it gets displayed on screen. But I'm not sure if doing a trial and error on the above three variables is the best way to go about it.
Please advise.
EDIT : I have tried to create a scene which includes the following
var cube,cylinder,sphere,plane,polyhedron,torusknot,torus;
My objective was to display them at the center of the screen irrespective of the camera position I decided for the entire scene (only one camera position for all elements).
This is a jsbin where I was able to display multiple elements.
For a different camera positioning (x,y,z), I was able to display them at the center of the page either by either/or both of the following'
Changing the size of the elements. For example, changing the length, breadth and height of the cube
Changing the y position of the element. For example, cube.position.y
However, though, I was able to get it to work this time, I feel it's helped by the fact that only the position.y needed altering. Had x,y,z all needed change, it would be difficult to come up with the position by trial and error.
Is there another way, this is done?
Finding it slightly difficult to believe that someone who bases all the elements in his scene based on a particular camera position, only needs to alter one coordinate (in my case, the y coordinate) to adjust to any other camera position.
About using camera.lookAt(cube.position) which was also suggested
I am apprehensive about using this, because in the same scene, I'm interested in look'ingAt, not just the cube, but also the cylinder,sphere,plane,polyhedron,torusknot,torus;
Make the camera look at the cube position (after the cube added to the scene):
camera.lookAt (cube.position);

WebVR Boilerplate Problems - Manager not rendering in VRMode, Camera looks straight down, fullscreen renders only half of window

I'm trying to implement WebVR into a project that I've built in Three.js and I'm having difficulties getting it to function correctly.
A number of problems arise when I view the scene.
When VRMode is false my scene renders but when switching to VR Mode the scene suddenly stops rendering. I can see the two stereoscopic windows but nothing else appears.
When I load my scene on desktop the camera points straight down. Upon loading over a device the scene is flipped upside down. I am telling the camera to look at the center of my image and it doesn't seem to have any effect. It was working just fine before I fiddled with the controls and rendering to support the WebVR manager and VRControls.
camera.position.set(-300, -800, 200);
camera.up = new THREE.Vector3(0, 0, 1);
camera.lookAt(new THREE.Vector3(0, 0, 0));
I am able to set the position fine.
When I click the fullscreen button on the bottom of the GUI the screen splits into two halves. The top half is black (my body bg and clearfix color is blue) and the lower half renders my scene. I have an event listener that works fine when I manually resize the window so this has to be related to the VR manager.
Any idea what could be causing these errors?
Here is how I define my renderer:
var width = window.innerWidth,
height = window.innerHeight;
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(width, height);
renderer.setClearColor(0x1D76BB, 1);
// Effect and Controls for VR
effect = new THREE.VREffect(renderer);
controls = new THREE.VRControls(camera);
effect.setSize(width, height);
var manager = new WebVRManager(renderer, effect);
Here is a my animate function:
function animate() {
requestAnimationFrame(animate);
if (controls) {
controls.update();
}
if (manager.isVRMode()) {
renderer.setRenderTarget(null); // add this line
manager.render(scene, camera);
} else {
renderer.render(scene, camera);
}
}
My libraries came fresh from bower this morning so I know they are all up to date.
It's hard to guess what's going on without looking at the rest of your code.
I'm not sure if this is related, but the latest version of the WebVRManager class takes care of switching between VR and non-VR mode for you and it falls back to a normal renderer in 2D mode. So you don't need to check for isVRMode anymore and you don't need to call renderer.render. Just use manager.render and it will do the work for you. The manager now also handles resizing for you, so you probably shouldn't be doing that either.
Other than that, you seem to be using the manager correctly.
VRControls (in conjunction with webvr-polyfill, if you're using it) overrides the camera position and orientation, so setting it yourself will have no effect (unless you're overriding it after controls.update, in which case, the controls will have no effect). If you want to set the base camera transform, you can attach the camera to a "dolly" and set the dolly's transform instead:
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
var dolly = new THREE.Object3D();
dolly.add(camera);
dolly.position.z = 200;
dolly.up = new THREE.Vector3(0, 0, 1);
dolly.lookAt(new THREE.Vector3(0, 0, 0));
dolly.rotateOnAxis(new THREE.Vector3(0, 1, 0), Math.PI);
scene.add(dolly);
function animate(timestamp) {
var timer = new Date().getTime() * 0.0002;
dolly.position.x = Math.floor(Math.cos(timer) * 600);
dolly.position.y = Math.floor(Math.sin(timer) * 600);
controls.update();
manager.render(scene, camera, timestamp);
requestAnimationFrame(animate);
}
Note that you have to add the dolly to the scene and that you have to rotate the dolly on its Y axis, after you make it lookAt something since cameras look into the negative Z direction by default.
It may also be helpful to add an AxisHelper to the scene so that you can get a better sense of your bearings while you debug:
var axisHelper = new THREE.AxisHelper(200);
axisHelper.position.z = 100;
scene.add(axisHelper);
Lastly, if you are using webvr-polyfill, it can sometimes, depending on which devices you are using, render its own barrel distortion shader pass. So, if your project involves shaders, they might conflict with this shader pass and prevent your scene from rendering correctly, or at all, in VR mode. If this is the case, you may be able to workaround this by rendering your shaders before you call manager.render as I've described here: https://stackoverflow.com/a/32549777/22417

THREE.js fit Orthographic camera to scene

I've searched through many related questions on stack overflow, but I couldn't quite find the answer to this problem.
I have a big, dynamic group of Object3D's in my scene, with an orthographic camera looking at them head-on.
I want to have a simple function which, when called, will simply match the top/left/bottom/right/zoom properties of the Orthographic camera to properly fit the Object3D group.
I've tried all kinds of things, but none of my code is worth posting. I need to look at this from a whole new angle (pun intended). I have found various other answers which discuss changing the fov of the camera, once you know the distance from the face of bounding box of the group to the camera, but I don't know how to implement that with an orthographic camera, since (as far as I've tried) the fov property doesn't work with it (maybe it actually does, I just don't know).
So I don't particularly like asking for code, but nevertheless I would like a function which would automatically adjsut the appropriate properties of the Orthographic camera to fit the object passed to it as a parameter, for example:
function fitOrthographicCameraToObject3DGroup(group) {
//implement here (my question)
}
Calculate the bounding box of your mesh and apply this code.
This works for me
var camera = new THREE.OrthographicCamera(container.offsetWidth / -2, container.offsetWidth / 2, container.offsetHeight / 2, container.offsetHeight / -2, 100, 100000);
//For centering the meshGroup
var box = new THREE.Box3().setFromObject(meshGroup);
box.center(meshGroup.position);
meshGroup.localToWorld(box);
meshGroup.position.multiplyScalar(-1);
//For fitting the object to the screen when using orthographic camera
Camera.zoom = Math.min(container.offsetWidth / (box.max.x - box.min.x),
container.offsetHeight / (box.max.y - box.min.y)) * 0.4;
Camera.updateProjectionMatrix();
Camera.updateMatrix();
Setting the top/left/bottom/right of the orthographic camera once you have the bounding box should not be a problem. Just take the half lengths of the bounding box.
The zoom is the issue and for that you can confine your scene in a unit cube by scaling up or down your scene by the appropriate amount depending on your bounding box size. Then you dont have to worry about the zoom and the above top/left/bottom/right values become 0.5/0.5/-0.5/-0.5.

Camera arguments in Three.js

This is how a camera is instantiated:
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR
);
What do these values mean?
I was wondering the same thing so I looked it up, it is a view "frustum".
I'll paste here a code comment I wrote in a recent project because it sums it up nicely IMHO.
// "What the f*** is a frustum?" you ask. Well I did.
// Think about it as a truncated pyramid. The tip is the camera. From a certain
// point "down" the pyramid (the "near plane"), stuff can be seen on-screen.
// After the "base" of the pyramid (the "far plane"), stuff is too far to be
// seen. Stuff outside the pyramid is off-screen too. There is also the "aspect
// ratio" (the rectangle that makes up the pyramid's base, so this value is
// width/height) and the "field of view" (think of it as a lens or something,
// it distorts the pyramid so there's more objects on screen - it is set in
// degrees and 45° is more-or-less a "natural" perspective. The bigger the
// number, the more "perspective" there is).
I found this tutorial very useful for understanding all the camera parameters, and the difference between PerspectiveCamera and OrthographicCamera.
PerspectiveCamera
Fov (Field of view) - This is part of scene that can be seen from the position of the camera. As you probably know, we, humans, have almost 180-degree field of view, while some birds might even have a complete 360-degree field of view. However, for computers, we usually use the field of view between 60 and 90 degrees.
Aspect - The aspect ratio is ratio between the horizontal and vertical size of the area where we render the output. As we usually use the entire window, we will just use that ratio. The aspect ratio determines the difference between the horizontal field of view and the vertical field of view as you can see in the figure on the following page. Ordinary value is window.innerWidth / window.innerHeight.
Near - This property defines a min distance from the camera the Three.js renders the scene. Usually, this is a very small value, e.g. 0.1.
Far - This property defines a max distance we see the scene from the position of the camera. If we set this as too low, a part of our scene might not be rendered; if we set it as too high, in some cases, it might affect the rendering performance. Normal value is between 500 and 2000.
OrthographicCamera
Left (Camera frustum left plane) - You should see this as what is the left border of what will be rendered. If we set this value to -100, you won’t see any objects that are farther to the left.
Right (Camera frustum right plane) - Anything farther to the right won't be rendered.
Top (Camera frustum top plane) - The maximum top position to be rendered.
Bottom (Camera frustum bottom plane) The bottom position to be rendered.
Near (Camera frustum near plane) - From this point on, based on the position of the camera, the scene will be rendered.
Far (Camera frustum far plane) - The furthest point, based on the position of the camera, to which the scene will be rendered.
The following picture should be very illustrative:
The main difference between the two camera modes is that in the OrthographicCamera distance plays no role, so all the elements are of the same size, as you can see in the case of the red and yellow ball.
Finally here is some code you can use to switch from one camera to the other:
this.switchCamera = function(SCENE_WIDTH, SCENE_HEIGHT) {
if (camera instanceof THREE.PerspectiveCamera) {
camera = new THREE.OrthographicCamera( SCENE_WIDTH / - 2, SCENE_WIDTH / 2, SCENE_HEIGHT / 2, SCENE_HEIGHT / - 2, 0.1, 1000 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = -1;
camera.lookAt(scene.position);
this.perspective = "Orthographic";
} else {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = -1;
camera.lookAt(scene.position);
this.perspective = "Perspective";
}
};
Notes:
The function camera.lookAt(scene.position) orients the camera to where the scene is located, so it is visible.
Units in three.js are SI units, so the values of left,right,top,bottom should not assumed to be pixels.
The aspect ratio of the camera's frustum should normally match the canvas' aspect ratio.
SCENE_WIDTH, SCENE_HEIGHT, can be determined through the geometries that are added in the scene. The orthographic frustum could be much larger than the scene but it wouldn't be very parsimonious.
Useful links:
http://www.glprogramming.com/red/chapter03.html
Three.js - Orthographic camera
The first param is FOV means field of view, imagine a camera on a tripod, if you change lens to wide angle you get a higher FOV. Try to imagine a cone coming out from the camera, it can only see objects in that area.
ASPECT means aspect ratio, a widescreen TV is 16/9 and old ones were 4/3, usually just give it the screen width/height or the dims of a DIV you would like three.js to use.
fov: Camera frustum vertical field of view.
aspect: Camera frustum aspect ratio.
near: Camera frustum near plane.
far: Camera frustum far plane.
On these pages there some image for the FOV, NEAR plane, anmd FAR plane.
https://threejsfundamentals.org/threejs/lessons/resources/frustum-3d.svg
https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/ViewFrustum.svg/440px-ViewFrustum.svg.png
https://threejsfundamentals.org/threejs/lessons/threejs-cameras.html
https://en.wikipedia.org/wiki/Viewing_frustum
This is the aspect ratio.
https://en.wikipedia.org/wiki/Aspect_ratio_(image)
This is the official docs.
https://threejs.org/docs/#api/en/cameras/PerspectiveCamera

Categories

Resources