ThreeJS - stop slerp camera rotation from drifting around - three.js

I'm currently working on a scene, with an object at the centre (0,0,0) and 6 other object orbiting it. I have a camera set up behind and slightly above an outer object, looking back to centre (camera.lookAt(0,0,0) etc).
I want to click an object and just for the camera to arc towards and face the clicked object. I'm using Quaternion.slerp to achieve this, and it sort of works, yet while the camera's moving, it sort of drifts about lazily then slowly focuses just off centre of the object.
So if I click an object to the left of the camera's FOV, it sort of drifts right a little before swinging left, and vice versa.
I found some help regarding movement with slerp, I was hoping to simply adapt it:
How to animate the camera in three.js to look at an object?
Setup
this._endQ = new Quaternion();
this._iniQ = new Quaternion().copy(this._camera.quaternion);
this._curQ = new Quaternion();
this._vec3 = new Vector3();
const euler = new Euler(this._cameraLookAtDestination.x, this._cameraLookAtDestination.y, this._cameraLookAtDestination.z);
this._endQ.setFromEuler(euler);
In renderloop
Quaternion.slerp(this._iniQ, this._endQ, this._curQ, pointInTime);
this._vec3.x = this._cameraLookAtDestination.x;
this._vec3.y = this._cameraLookAtDestination.y;
this._vec3.z = this._cameraLookAtDestination.z;
this._vec3.applyQuaternion(this._curQ);
this._camera.lookAt(this._vec3);
Is this normal slerp behaviour, or is there something I'm missing?
EDIT:
I also noticed that each time I click an object to look at, the camera seems to reset its orientation, then rotates

Related

ThreeJS - pan camera vertically without tilting?

I am using Mr Doob's periodic table code in my web app:
https://threejs.org/examples/css3d_periodictable.html
I am using it in Helix view. When I rotate the object left, I shift the camera's Y position by a certain amount too. My desire is to give the impression that the Helix is corkscrewing vertically up and down as you rotate the camera. This is the code I'm using, where angle and panAmount are constants that control how much rotation and vertical pan takes place per second:
let g_RotationMatrix = new THREE.Matrix4();
g_RotationMatrix.makeRotationY(angle);
// Apply matrix like this to rotate the camera.
self.object.position.applyMatrix4(g_RotationMatrix);
// Shift it vertically too.
self.object.position.y += panAmount;
// Make camera look at the target.
self.object.lookAt(self.target);
The problem I'm having is that from the camera's perspective, the object appears to tilt towards you and away from you respectively, depending on the rotation direction, as the camera shifts vertically. This makes sense to me because the I'm guessing that the lookat() function causes the camera to look at the center of the target, and not at point on the target that is closest point to it, so the camera has to tilt to focus on the targe's center of mass. I see the same effect when I use the mouse to pan vertically using the Orbit controls. I believe another way to describe the effect is that the object appears to pitch up and down as the camera shifts vertically.
The effect I want instead is that of a window washer on an automated lift being raised up and down the side of building, with the side of the building appearing perfectly flat to the camera regardless of the camera's current Y position.
How can I achieve this effect with the camera?
Make the camera lookAt the target, but at the same y level as the camera.
Assuming self.target is a vector3 object and self.object is the camera:
If, for example, self.target is the camera's rotation center of the object, you wouldn't want to change the actual self.target vector. Make a copy of it first.
const newTarget = new THREE.Vector3( );
function render( ){
// copy the target vector to newTarget so that the original self.target
// will not change and the camera can still rotate around the original
// target.
newTarget.copy( self.target );
// Set newTarget's Y from the camera Y. So that is looks horizontal.
newTarget.y = self.object.position.y;
// Make the camera look at the objects newTarget
self.object.lookAt( newTarget);
}

Can't get Three.js camera up direction

I need to get the camera up direction and i've tried many ways with no luck, i'm not an expert of quaternions so i'm doubting i did it right.
I've tried:
camera.up
camera.up.applyMatrix4(camera.matrixWorld);
new THREE.Vertex3(0,1,0).applyMatrix4(camera.matrixWorld);
camera.up.normalize().applyMatrix4(camera.matrixWorld);
after this i create two planes passing by two points of my interest, and add the plane helper to the scene and i can see they are very far from where i was expecting them. (i'm expecting two planes that looks like the top and bottom of the camera frustum).
P.s. the camera is a shadow camera of a directional light so an orthographic camera, and i manipulate the directional light position and target before doing this operation, but i've called updateMatrixWorld on the light, on it's target and the camera, on the camera i've called also updateProjectionMatrix... still no results
I've made a sandbox to see what i've tried till now, and better visualize what i want to achieve:
https://codesandbox.io/embed/throbbing-cache-j5yse
once i manage to get the green arrow to point to the top of the blue triangle of the camera helper i'm good to go
In the normal render flow, shadow camera matrices are updated as part of rendering the shadow map (WebGLShadowMap.render).
However, if you want the updated matrix values before the render, then you'll need to update them manually (you already understand this part).
The shadow camera is a property of (not a child of) the DirectionalLight. As such, it doesn't follow the same rules as other scene objects when it comes to updating its matrices (because it's not really a child of the scene). Instead, you need to call the shadow property's updateMatrices method (inherited from LightShadow.updateMatrices).
const dl = new THREE.DirectionalLight(0xffffff, 1)
dl.shadow.updateMatrices(dl) // <<------------------------ Updates the shadow camera
This updates the shadow camera with information from the DirectionalLight's own matrix, and its target's matrix, to properly orient the shadow camera.
Finally, it looks like you're trying to get the "world up" of the camera. Personally, I'd use the convenience function localToWorld:
let up = new THREE.Vector3(0, 1, 0)
dl.shadow.camera.localToWorld(up) // destructively converts "up" from local-to-camera into world coordinates
via trial and errors i've figured out that what gave me the correct result was:
calling
directionalLight.shadow.updateMatrices(...)
and then
new THREE.Vector3(0,1,0).applyQuaternion(directionalLight.shadow.camera.quaternion)

How do I place an element in front of the camera, facing it regardless of where it is?

I have this simple scene with a "tooltip" entity composed of some data, I'd like to know how to position it in front of the camera. The tooltip will have to face certain points a few meters away so the user can see it. It must obey camera direction (it can be gathered by calculating it from previousPoint to nextPoint where the camera will move), but only y axis (can't be tilted or anything like that).I tried digging through math but couldn't understand good enough to employ a solution for this little project; I appreciate all the help!
setTimeout(function(){
var camera = document.getElementById("cameraS");
var tt = document.getElementById("ttS");
var cameraPos = camera.getAttribute('position');
var ttPos = tt.getAttribute('position');
tt.setAttribute('position', cameraPos);
tt.setAttribute('rotation', {'y': -90});
}, 5000);
JSFiddle
EDIT
I made an image showing what I'm after: http://imgur.com/a/eDhqE
I have point A and point B; the camera will play an animation moving from previous point to the next, and upon reaching there the tooltip will be displayed a few meters away from the point (box) so we can see it. It must take camera orientation into consideration but it must be perpendicular to the ground (can't be tilted).
There is a command THREE.Object.lookAt(THREE.Vector3); that will rotate an object (assuming (0.0,1.0,0.0) is up) to face a vector. You can use this to have it face your camera.
If you only want Y rotation, you can copy the current rotation, then do look at, then copy the rotation.x and rotation.z from the previous frame rotation copy - so that way it'll only correct the y with .lookAt because you reset x and z.

Raycasting against box geometry within it (three.js)

I'm trying to detect at which wall of the box geometry is the user looking at. Imagine that user is in a room and I want to detect which wall is the camera currently looking at. I'm raycasting from camera like this:
var lookAtVector = new THREE.Vector3(0,0, -1);
lookAtVector.applyQuaternion(camera.quaternion);
raycaster.setFromCamera(lookAtVector, camera);
var intersection = raycaster.intersectObject(box);
console.log(intersection);
So this raycasting works fine outside the "room" but how to get it working inside the room?
One thing to remember is that the camera is always pointing in the negative z direction. When rotating and moving the camera, you are actualy rotating and moving the whole scene. Therefore I think there are two problems with you code.
I think you have to initialize your raycaster like this:
var raycaster = new THREE.Raycaster(camera.position, lookAtVector);
Where lookAtVector is just var lookAtVector = new THREE.Vector3(0,0, -1);, unmodified. This will send a ray from you camera's position in the direction it is facing.
Then I think that maybe you are not rendering you box backside. When constructing your box material, pass along side: THREE.BackSide (or THREE.DoubleSide) as a parameter
EDIT
I was wrong about the lookAtVector. The ray is probably cast before the camera has modified the scene. Therefore you are absolutely right when applying the cameras quaternion on lookAtVector
To raycast inside the box geometry you need to first make the inside of the box visible:
box.material.side = THREE.DoubleSide;
Or
box.material.side = THREE.BackSide;

How to find nearest object with three.js

I have a bunch of data and I know their lat/lon position (circles on the image). Now I have a camera in three.js and I know it's position, rotation and which way is north from it's perspective.
I want to go on top of these circles on mouse click but I'd like that the program would choose the closest one to the player and it should also be in the current field of view.
I've tried finding the angle between camera look direction and circle's position but it still moves quite randomly.
Any ideas where to start with this?
I'm not totally sure what you're asking regarding the "going on top of the circles and mouse click" part but here's a utility function for getting the distance between a camera and a point you know the x, y, z coordinates for.
var getCameraDistanceFrom = function(camera,x,y,z) {
var cameraDistance = new THREE.Vector3();
var target = new THREE.Vector3(x,y,z);
cameraDistance.subVectors(camera.position, target);
return cameraDistance.length();
};
This answer seems to have a good way to check if an object is in the camera's field of view: Determine if a mesh is visible on the viewport according to current camera

Resources