Three JS Camera control mouse + kbd - three.js

As the title suggests I want to move a camera in ThreeJS.
Im currently letting the camera move on mousedown making it circle a point at which it looks and I want to let keyboard controls move the camera in x and z, thus moving the point at which it looks as well as the position of the camera. The lookAt target is currently 0,0,0.
Having tried the translateX, and translateZ it would move the cameras position relative to its own tilt and angle. Thus, looking down, "forward" would bring the camera down in world Y-axis where I want to move it in world X and Z only.
Camera is perspective if it matters, and the scrollwheel sets radious of orbiting cirle.
Version is r55.
Here's the current code (mostly copied from examples) that doesnt move camera at all.
if (userMouse.isDown){
theta = - ( ( userMouse.position.x - userMouse.down.position.x ) * userMouse.acceleration ) + userMouse.down.theta;
phi = ( ( userMouse.position.y - userMouse.down.position.y ) * userMouse.acceleration ) + userMouse.down.phi;
phi = Math.min( 160, Math.max( 20, phi ) );
}
camera.position.x = userCam.radious * Math.sin( theta * Math.PI / 360 ) * Math.cos( phi * Math.PI / 360 );
camera.position.y = userCam.radious * Math.sin( phi * Math.PI / 360 );
camera.position.z = userCam.radious * Math.cos( theta * Math.PI / 360 ) * Math.cos( phi * Math.PI / 360 );
camera.lookAt(userCam.target);
camera.updateMatrix();
Ive looked through examples and scoured the Interwebs, but have not found this setup.
My guess is I would use some transformWorldMatrix or use the rotation of the camera to get the direction and then just add a movement value.
Im sure its not terribly tricky, I just cant wrap my head around it.
So; how would I move the camera in world X and Z with regard to where its looking?
Sorry if this is a noob question.

Related

Three js camera with gyroscope ( deviceorientation event)

I have a camera animation for desktop which goes like this:
in mousemove event I have
mouseX = (event.clientX - windowHalfX) / 100;
mouseY = (event.clientY - windowHalfY) / 100;
and in the render function I have
camera.position.x += (mouseX - camera.position.x) * 0.05;
camera.position.y += (-mouseY - camera.position.y) * 0.05;
I want to achieve the same effect with a mobile gyroscope. I tried this:
in the deviceorientation event
gammaRotation = event.gamma;
betaRotation = event.beta;
and in the renderer function I have mostly the same thing as with the desktop version
camera.position.x += (gammaRotation/100 - camera.position.x) * 0.05;
camera.position.y += (-betaRotation/100 - camera.position.y) * 0.05;
But when the beta is 90 ( thats when the phone is vertical to the ground 90 degrees) my camera jumps to one site to another and then goes back to working correctly. It only "jumps" when beta is 90.
I guess it's a math error, but can't really work it out because I don't know how to enable inspector for Iphone right now...
I think you're misusing the deviceorientation data. The alpha, beta, gamma properties of the event represents device rotations around the z, x, y axes respectively, so you should assign them to the camera's rotation, not its position, like in this demo.
The jump from 90 to -90 is to be expected with rotations in 3D space for the same reason that a 2D rotation of +180 is the same as -180. There are multiple ways to describe the same rotation, so you can't convert rotations into positions reliably.

Rotating a sphere to a specific point. Not the camera

i need some help on this one.
I have search and couldn't find solutions for this, because all the solutions were about rotating a camera around a sphere.
In my case, the camera is still. I have a webgl globe with points around using latitude an longitude. I just want to click a point and rotate the globe mesh so that it centers that point.
I have the point Vector3, it's latitude and longitude but i can't figure how to do this.
Is someone around that can help me? Or know any example like this?
Thank you in advance
To rotate a sphere to match a lat/lon the camera center, you can do this:
var verticalOffset = 0.1;
sphere.rotation.x = latitude * ( Math.PI / 180 ) - verticalOffset;
sphere.rotation.y = ( 270 - longitude ) * ( Math.PI / 180 );
So then you can call this with a tween:
var tween = new TWEEN.Tween(sphere.rotation)
.to({ x: latitude * ( Math.PI / 180 ) - verticalOffset, y: ( 270 - longitude ) * ( Math.PI / 180 ) }, 2000)
.start();
I have created a jsfiddle demonstrating this, where a marker is set at Lissabon:
http://jsfiddle.net/L0rdzbej/208/
Update
In case the sphere has been turned around a lot, the Y-rotation value climbs up. Then tweening the rotation as suggested above results in a lot of reverse spinning until the point is displayed at camera center.
To avoid this, it is possible to set the rotations via quaternions. The tweening must be slerp'ed using a temporal quaternion.
For demonstration purposes I have set the spheres initial Y rotation to PI * 12.1, then applying the quaternion.
http://jsfiddle.net/L0rdzbej/217/
var phi = latitude * Math.PI / 180;
var theta = ( 270 - longitude) * Math.PI / 180;
var euler = new THREE.Euler(phi, theta, 0, 'XYZ');
// rotation (using slerp)
var qstart = new THREE.Quaternion().copy(sphere.quaternion); // src quaternion
var qend = new THREE.Quaternion().setFromEuler(euler); //dst quaternion
var qtemp = new THREE.Quaternion();
var o = {t: 0};
new TWEEN.Tween(o).to({t: 1}, 2500)
.onUpdate(function () {
THREE.Quaternion.slerp(qstart, qend, qtemp, o.t);
sphere.quaternion.copy( qtemp );
})
.start();

convert Point3D To Screen2D get wrong result in three.js

I use function like this in three.js 69
function Point3DToScreen2D(point3D,camera){
var p = point3D.clone();
var vector = p.project(camera);
vector.x = (vector.x + 1) / 2 * window.innerWidth;
vector.y = -(vector.y - 1) / 2 * window.innerHeight;
return vector;
}
It works fine when i keep the scene still.
But when i rotate the scene it made a mistake and return wrong position in the screen.It occurs when i rotate how about 180 degrees.It shoudn't have a position in screen but it showed.
I set a position var tmpV=Point3DToScreen2D(new THREE.Vector3(-67,1033,-2500),camera); in update and show it with css3d.And when i rotate like 180 degrees but less than 360 , the point shows in the screen again.Obviously it's a wrong position that can be telled from the scene and i haven't rotate 360 degrees.
I know little about the Matrix,So i don't know how the project works.
Here is the source of project in three.js:
project: function () {
var matrix;
return function ( camera ) {
if ( matrix === undefined ) matrix = new THREE.Matrix4();
matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
return this.applyProjection( matrix );
};
}()
Is the matrix.getInverse( camera.matrixWorld ) redundant? I tried to delete it and it didn't work.
Can anyone help me?Thanks.
You are projecting a 3D point from world space to screen space using a pattern like this one:
var vector = new THREE.Vector3();
var canvas = renderer.domElement;
vector.set( 1, 2, 3 );
// map to normalized device coordinate (NDC) space
vector.project( camera );
// map to 2D screen space
vector.x = Math.round( ( vector.x + 1 ) * canvas.width / 2 ),
vector.y = Math.round( ( - vector.y + 1 ) * canvas.height / 2 );
vector.z = 0;
However, using this approach, points behind the camera are projected to screen space, too.
You said you want to filter out points that are behind the camera. To do that, you can use this pattern first:
var matrix = new THREE.Matrix4(); // create once and reuse
...
// get the matrix that maps from world space to camera space
matrix.getInverse( camera.matrixWorld );
// transform your point from world space to camera space
p.applyMatrix( matrix );
Since the camera is located at the origin in camera space, and since the camera is always looking down the negative-z axis in camera space, points behind the camera will have a z-coordinate greater than zero.
// check if point is behind the camera
if ( p.z > 0 ) ...
three.js r.71
Like the example above but you can check vector.z to determine if it's in front.
var vector = new THREE.Vector3();
var canvas = renderer.domElement;
vector.set( 1, 2, 3 );
// map to normalized device coordinate (NDC) space
vector.project( camera );
// map to 2D screen space
vector.x = Math.round( ( vector.x + 1 ) * canvas.width / 2 ),
vector.y = Math.round( ( - vector.y + 1 ) * canvas.height / 2 );
//behind the camera if z isn't in 0..1 [frustrum range]
if(vector.z > 1){
vector = null;
}
To delve a little deeper into this answer:
// behind the camera if z isn't in 0..1 [frustrum range]
if(vector.z > 1){
vector = null;
}
This is not true. The mapping is not continuous. Points beyond the far
plane also map to z-values greater than 1
What exactly does the z-value of a projected vector stand for? X and Y are in normalised clipspace [-1,1] , what about z?
Would this be true?
projectVector.project(camera);
var inFrontOfCamera = projectVector.z < 1;
Since the camera is located at the origin in camera space, and since the camera is always looking down the negative-z axis in camera space, points behind the camera will have a z-coordinate greater than 1.
//check if point is behind the camera
if ( p.z > 1 ) ...
NOTICE: If this condition is satisfied, then the projected coordinates need to be centrosymmetric
{x: 0.233, y: -0.566, z: 1.388}
// after transform
{x: -0.233, y: 0.566, z: 1.388}

90 degree field of view without distortion in THREE.PerspectiveCamera

I am building a website running on THREE.js to generate a 3D world. From experience with video games, I know they usually use a camera field of view angle of about 90 degrees. When I set PerspectiveCamera in THREE.js to such a high FOV value, however, the scene is severely distorted. This distortion is somehow removed in games while preserving the large field of view. How is this done? Can I do this in THREE.js, too? Thanks!
This is how the camera is created:
new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
100,
10000000
);
The resulting image is this. See how the earth is stretched in the horizontal direction? That's what I am trying to get rid of.
In three.js, camera.fov is the vertical field-of-view in degrees.
The horizontal field-of-view is determined by the vertical field-of-view and the aspect ratio of the display image.
hFOV = 2 * Math.atan( Math.tan( camera.fov * Math.PI / 180 / 2 ) * camera.aspect ) * 180 / Math.PI; // degrees
A reasonable value for camera.fov is 40 to 50 degrees. This yields minimal distortion, and depending on the aspect ratio of the display, yields a horizontal FOV of 80 or 90 degrees.
In your example, you have specified a vertical FOV of 75 degrees, which implies a horizontal FOV of about 110 degrees.
three.js r.69
Based on WestLangley's awesome answer, here is how to get a fixed horizontal fov in three.js:
var horizontalFov = 90;
camera.fov = (Math.atan(Math.tan(((horizontalFov / 2) * Math.PI) / 180) / camera.aspect) * 2 * 180) / Math.PI;

THREE.JS: Get object size with respect to camera and object position on screen

I am newbie to 3D programming, I did started to explore the 3D world from WebGL with Three.JS.
I want to predetermine object size while I change the camera.position.z and object's "Z" position.
For example:
i have a cube mesh at size of 100x100x100.
cube = new THREE.Mesh(
new THREE.CubeGeometry(100, 100, 100, 1,1,1, materials),
new THREE.MeshFaceMaterial()
);
and cam with aspect ratio of 1.8311874
camera = new THREE.PerspectiveCamera( 45, aspect_ratio, 1, 30000 );
I want to know size (2D width & height) of that cube object on screen when,
camera.position.z = 750;
cube.position.z = 500;
Is there is any way to find it/predetermine it?
You can compute the visible height for a given distance from the camera using the formulas explained in Three.js - Width of view.
var vFOV = camera.fov * Math.PI / 180; // convert vertical fov to radians
var height = 2 * Math.tan( vFOV / 2 ) * dist; // visible height
In your case the camera FOV is 45 degrees, so
vFOV = PI/4.
(Note: in three.js the camera field-of-view FOV is the vertical one, not the horizontal one.)
The distance from the camera to the front face (important!) of the cube is 750 - 500 - 50 = 200. Therefore, the visible height in your case is
height = 2 * tan( PI/8 ) * 200 = 165.69.
Since the front face of the cube is 100 x 100, the fraction of the visible height represented by the cube is
fraction = 100 / 165.69 = 0.60.
So if you know the canvas height in pixels, then the height of the cube in pixels is 0.60 times that value.
The link I provided shows how to compute the visible width, so you can do that calculation in a similar fashion if you need it.

Resources