4
votes

In Three.js (which uses JavaScript/ WebGL), how would one create a camera which flies around a sphere at fixed height, fixed forward speed, and fixed orientation in relation to the sphere, with the user only being able to steer left and right?

Imagine an airplane on an invisible string to the center of a globe, flying near ground and always seeing part of the sphere:

enter image description here

(I currently have code which rotates the sphere so to the camera it looks like it's flying -- left and right steering not implemented yet -- but I figure before I go further it might be cleaner to move the camera/ airplane, not the sphere group.)

Thanks!

2
Creating two empty groups and putting the camera inside -- forwardCamGroup( sidewaysCamGroup( camera) ) -- works for some seconds, but then after the rotations continue (forwardCamGroup.rotation.x -= .0025; sidewaysCamGroup.rotation.y += .0025;) the sideways steering gets skewed to behave like backwards steering...Philipp Lenssen
I got something now but it looks much more like strafing sideways movement, as opposed to sideways rotation :/ camGroup.matrix.rotateY(app.mouseXPercent *.00025); camGroup.matrix.rotateX(-.0025); camGroup.rotation.getRotationFromMatrix(camGroup.matrix);Philipp Lenssen
(Alright, solved as per below!)Philipp Lenssen

2 Answers

5
votes

You mean like in my Ludum Dare 23 game? I found this to be a bit more complicated than I expected. It's not difficult, though.

Here I'm assuming that you know the latitude and longitude of the camera and its distance from the center of the sphere (called radius), and want to create a transformation matrix for the camera.

Create the following objects only once to avoid creating new objects in the game loop:

var rotationY = new Matrix4();
var rotationX = new Matrix4();
var translation = new Matrix4();
var matrix = new Matrix4();

Then every time the camera moves, create the matrix as follows:

rotationY.setRotationY(longitude);
rotationX.setRotationX(-latitude);
translation.setTranslation(0, 0, radius);
matrix.multiply(rotationY, rotationX).multiplySelf(translation);

After this just set the camera matrix (assuming camera is your camera object):

// Clear the camera matrix.
// Strangely, Object3D doesn't have a way to just SET the matrix(?)
camera.matrix.identity();
camera.applyMatrix(matrix);
2
votes

Thanks for Martin's answer! I've now got it running fine in another approach as follows (Martin's approach may be perfect too; also many thanks to Lmg!):

Set the camera to be a straight line atop the sphere in the beginning (i.e. a high y value, a bit beyond the radius, which was 200 in my case); make it look a bit lower:

camera.position.set(0, 210, 0);
camera.lookAt( new THREE.Vector3(0, 190, -50) );

Create an empty group (an Object3D) and put the camera in:

camGroup = new THREE.Object3D();
camGroup.add(camera);
scene.add(camGroup);

Track the mouse position in percent in relation to the screen half:

var halfWidth = window.innerWidth / 2, halfHeight = window.innerHeight / 2;
app.mouseX = event.pageX - halfWidth;
app.mouseY = event.pageY - halfHeight;
app.mouseXPercent = Math.ceil( (app.mouseX / halfWidth) * 100 );
app.mouseYPercent = Math.ceil( (app.mouseY / halfHeight) * 100 );

In the animation loop, apply this percent to a rotation, while automoving forward:

camGroup.matrix.rotateY(-app.mouseXPercent * .00025);
camGroup.matrix.rotateX(-.0025);
camGroup.rotation.getRotationFromMatrix(camGroup.matrix);

requestAnimationFrame(animate);
renderer.render(scene, camera);