Rotate camera X on local axis using Three.js - three.js

I'm new to Three.js and fairly new to 3d space engines and what I'm trying to achieve is a 360 equirectangular image viewer.
What my script does so far is to create a camera at 0,0,0 and a sphere mesh at the same location with normals inverted and an emission map of my 360 image.
Representation of scene using Blender's viewport.
The user should be enabled to rotate the camera using mouse drag or keyboard arrows, so using mouse listeners I created the drag feature which calculates the amount of rotation in the camera's Y axis (blue) and X axis (red) at each render frame. I also created min and max rotation limit on X (so the user couldn't spin backward), as follows:
var render = function () {
requestAnimationFrame( render );
if((camera.rotation.x < Math.PI/6 && speedX >= 0) || (camera.rotation.x > -Math.PI/6 && speedX <= 0))
camera.rotation.x += speedX * (Math.PI/180);
camera.rotation.y += speedY * (Math.PI/180);
renderer.render(scene, camera);
};
Where speedX and speedY represent the amount of rotation in each axis.
So far so good, but since those rotation coordinates are relative to the world and not the camera itself the X rotation makes the camera go wild, since after a couple of rotated degrees in the Y axis, the camera's X axis is no longer the same as the world's X axis.
My question, finally, is: how do I rotate the camera on it's own X axis at each frame?

If you want a camera's rotation to have meaning in terms of yaw (heading), pitch, and roll, you need set:
camera.rotation.order = 'YXZ'; // default is 'XYZ'
For more information, see this stackoverflow answer.
three.js r.82

Related

threejs rotate the object gradually to where camera is looking using orbit control

I'm planning to use Orbit Control to do a simple 3rd person camera view,
But I cant seem to figure out how to do it.
when I rotate the camera around an object, and press say “W” key to move forward, I want the object “look” to gradually rotate and move to the new direction the camera is facing.
How can I do that?
It's possible to do exactly that by gradually rotating the object to the camera direction.
Made a codepen here which uses a generic replacement to orbit controls for simplicity:
https://codepen.io/cdeep/pen/QWMWyYW
// Get the X-Z plane in which camera is looking to move the player
camera.getWorldDirection(tempCameraVector);
const cameraDirection = tempCameraVector.setY(0).normalize();
// Get the X-Z plane in which player is looking to compare with camera
model.getWorldDirection(tempModelVector);
const playerDirection = tempModelVector.setY(0).normalize();
// Get the angle to x-axis. z component is used to compare if the angle is clockwise or anticlockwise since angleTo returns a positive value
const cameraAngle = cameraDirection.angleTo(xAxis) * (cameraDirection.z > 0 ? 1 : -1);
const playerAngle = playerDirection.angleTo(xAxis) * (playerDirection.z > 0 ? 1 : -1);
// Get the angle to rotate the player to face the camera. Clockwise positive
const angleToRotate = playerAngle - cameraAngle;
// Get the shortest angle from clockwise angle to ensure the player always rotates the shortest angle
let sanitisedAngle = angleToRotate;
if(angleToRotate > Math.PI) {
sanitisedAngle = angleToRotate - 2 * Math.PI
}
if(angleToRotate < -Math.PI) {
sanitisedAngle = angleToRotate + 2 * Math.PI
}
// Rotate the model by a tiny value towards the camera direction
model.rotateY(
Math.max(-0.05, Math.min(sanitisedAngle, 0.05))
);

Three.js: How to rotate a Sphere on Axis using camera rotation values

I have a special control called SphericalControls. Its similar to OrbitControls, but it keeps camera at position 0,0,0 and instead rotates camera on x and y to look around a scene. It is placed in the middle of a SphereBufferGeometry which has a 360 equirectangular image projected upon it. The user can look around the 360 image, and as he does the camera x and y rotation values change.
When a user clicks a button, I need to take these x and y rotation values and rotate the sphere to the rotation of the camera. I then set camera back to x:0 and y:0.
The result is that the camera is reset and the 360 scene has now rotated to show the same rotation view that the camera was previously looking at. So to the user, the view stays basically static, just the values for camera.rotation and sphere rotation have swapped.
This works great if I offset the texture on the sphere:
sphereObj.material.map.wrapS = THREE.RepeatWrapping;
sphereObj.material.map.offset.x = ((camera.rotation.x) / (Math.PI * 2));
sphereObj.material.map.needsUpdate = true;
sphereObj.material.needsUpdate = true;
camera.rotation.set(0, 0);
// Success!
But what I need to do is not offset the texture, but rotate the entire geometry. I have tried:
var axis = new THREE.Vector3(0, 1, 0).normalize();;
var offsetRadian = ((camera.rotation.x) / (Math.PI * 2));
sphere.rotateOnAxis(axis, offsetRadian);
// Fail
But the result is that the sphere rotation is off by approx 30%. Any help is appreciated.
Every objects' rotational data is stored in their respective .quaternion object. Both camera and sphereObj have a quaternion, so what you could do is copy the camera's rotational data into the sphere:
// Get camera's rotation
targetRotation = camera.quaternion;
// Invert rotation
targetRotation.inverse();
// Set sphere's rotation
sphereObj.quaternion.copy(targetRotation);
camera.rotation.set(0, 0, 0);
I'm not entirely sure if you need the .inverse() line... if you're noticing the sphere is rotating in the opposite direction, just get rid of it to get the desired result.

How to setup a camera that follows a circle path?

I'm trying to create a camera that follows an object that rotates on a orbit around a sphere. But everytime the camera reaches the polar coordinates of the orbit, the direction changes. I just set the position of the camera according to the object that is has to follow and calling lookAt afterwards:
function render() {
rotation += 0.002;
// set the marker position
pt = path.getPoint( t );
// set the marker position
marker.position.set( pt.x, pt.y, pt.z );
marker.lookAt( new THREE.Vector3(0,0,0) );
// rotate the mesh that illustrates the orbit
mesh.rotation.y = rotation
// set the camera position
var cameraPt = cameraPath.getPoint( t );
camera.position.set( cameraPt.x, cameraPt.y, cameraPt.z );
camera.lookAt( marker.position );
t = (t >= 1) ? 0 : t += 0.002;
renderer.render( scene, camera );
}
Here's a complete fiddle: http://jsfiddle.net/krw8nwLn/69/
I've created another fiddle with a second cube which represents the desired camera behaviour: http://jsfiddle.net/krw8nwLn/70/
What happens is that the camera's lookAt function will always try to align the camera with the horizontal plane (so that the "up" direction is always (0, 1, 0). And when you reach the top and bottom of the ellipse path, the camera will instantaneously rotate 180° so that up is still up. You can also see this in your "desired behaviour" example, as the camera cube rotates so that the colors on the other side are shown.
A solution is to not use lookAt for this case, because it does not support cameras doing flips like this. Instead set the camera's rotation vector directly. (Which requires some math, but you look like a math guy.)

three.js: Limiting camera's rotation

I'm working with three.js, attempting to model a real world camera. As such, I'd like to limit its axis of rotation to 90 degrees along x and y axises.
Is there a simply way to do this? My current code isn't working particularly well (and goes crazy when you attempt to move the camera past the X and Y boundaries simultaneously)
if(xRot != null && xRot != undefined){
camera.rotateX(xRot);
}
if(yRot != null && yRot != undefined){
camera.rotateY(yRot);
}
if(camera.rotation.x < minCameraRotX){
camera.rotation.x = minCameraRotX;
}else if (camera.rotation.x > maxCameraRotX){
camera.rotation.x = maxCameraRotX;
}
if(camera.rotation.y < minCameraRotY){
camera.rotation.y = minCameraRotY;
}else if(camera.rotation.y > maxCameraRotY){
camera.rotation.y = maxCameraRotY;
}
Any advice would be greatly appreciated!!!
I actually managed to find a solution by checking some of the existing code in a Three.js demo for a library called PointerLock. The idea is to actually stack multiple objects inside each other: start with an object that moves horizontally (the yaw object), place another object inside the yaw object that moves vertically (the pitch object), and then place the actual camera inside the pitch object.
Then, you only rotate the outside objects (yaw and pitch) along their respective axises, so if you rotate both, they'll self-correct. For example, if you rotate the yaw 45 degrees along the y-axis (making it turn to the right) and then rotate the pitch 45 degrees (making it turn downward), the pitch will go 45 degrees downward from the yaw's already rotated position.
Given that the camera is inside both, it just points wherever the yaw and pitch direct it.
Here is the code
/*
* CAMERA SETUP
*
* Root object is a Yaw object (which controls horizontal movements)
* Yaw object contains a Pitch object (which controls vertical movement)
* Pitch object contains camera (which allows scene to be viewed)
*
* Entire setup works like an airplane with a
* camera embedded in the propellor...
*
*/
// Yaw Object
var yawObject = new THREE.Object3D();
// Pitch Object
var pitchObject = new THREE.Object3D();
// Camera Object
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// Max Camera angles (in radians)
var minCameraRotX = 0.5;
var maxCameraRotX = 0.5;
var minCameraRotY = 1;
var maxCameraRotY = 1;
// Setup
yawObject.add( pitchObject );
pitchObject.add( camera );
scene.add(yawObject);
...
var rotateCamera = function(xRot, yRot, zRot){
yawObject.rotation.y += yRot;
pitchObject.rotation.x += xRot;
// Enforce X-axis boundaries (rotates around y-axis)
yawObject.rotation.y = Math.max( minCameraRotY, Math.min( maxCameraRotY, yawObject.rotation.y ) );
// Enforce Y-axis boundaries (rotates around x-axis)
pitchObject.rotation.x = Math.max( minCameraRotX, Math.min( maxCameraRotX, pitchObject.rotation.x ) );
}
Here is the source code I referenced: https://github.com/mrdoob/three.js/blob/acda8a7c8f90ce9b71088e903d8dd029e229678e/examples/js/controls/PointerLockControls.js
Also, this is sort of cheesy, but this little plane cartoon helped me visual exactly what was going on in my setup

Three.js - Drawing a torus but unable to understand the equation defined it

I try to do an animation which represents a sphere around which camera is rotating and I have drawn a circle on it (drawn with a THREE.TorusGeometry).
Then, I project a plane on the current point defined by the direction from camera position to the origin (0,0,0).
For a circle defined by y=0 and x²+z²=1 (i.e a circle defined into Oxz plane = equatorial plane of the sphere), you can see the result on :
link 1 : circle defined by y=0 and x²+z²=1
As you can see, the coordinates of plane are well drawn but I can't get to understand why the yellow circle is not drawn into Oxz plane (in this link, you can see that it is in Oxy plane).
Before the matrix multiplication, I defined above the vector of Torus by :
var coordTorus = new THREE.Vector3(radius*Math.cos(timer), 0, radius*Math.sin(timer));
i.e, by x'²+z'²=1 and y'=0 (choice 2). In this case, I don't get a valid result for the yellow circle, it is drawn into Oxy plane and not into Oxz plane like expected.
To get a good result, I have to define x'²+y'²=1 and z'=0 in local plane but I can't understand why ?
If someone could tell me the explication ?
It was hard to extract from all the code where exactly your problem was. I cleaned things up and solved it differently and I think this Fiddle shows what you wanted.
Instead of rotating all objects I rotated only the camera which seems much simpler then your solution:
/**
* Rotate camera
*/
function rotateCamera() {
// For camera rotation
stepSize += 0.002;
alpha = 2 * Math.PI * stepSize;
if (alpha > 2 * Math.PI) {
stepSize = 0;
}
// Rotate camera around a circle
camera.position.x = center.x + distance * Math.cos(alpha);
camera.position.z = center.y + distance * Math.sin(alpha);
// Camera should look at center
camera.lookAt(new THREE.Vector3(0, 0, 0));
}
And then I added your tangent plane to the camera instead of the scene:
So it rotates with the camera.
camera.add(plane);

Resources