mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.81k stars 35.38k forks source link

Set camera look vector from roll, pitch and yaw values? #1365

Closed Dino4674 closed 12 years ago

Dino4674 commented 12 years ago

When I init my ThreeJS base application I must specify camera target:

camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(0, 0, 1500);
camera.target = new THREE.Vector3(0, 0, 0);

I also have one mesh which I have put on 0,0,0 coordinates.

Now I need to set camera's rotation based on roll, pitch and yaw values that I receive from my mobile phone and these values are from -PI to +PI.

I tried something like this

camera.rotation.x = roll / Math.PI
camera.rotation.y = yaw / Math.PI
camera.rotation.z = pitch / Math.PI

but camera would always look at 0,0,0 and I need it to look at place based on roll, pitch and yaw. How can I accomplish this?

Dino4674 commented 12 years ago

I found this library http://www.c3dl.org/index.php/tutorials/tutorial-9-camera-basics/ and I see that it can set camera's roll, pitch and yaw values.

How to do this with Three.js?

mrdoob commented 12 years ago

Camera always look at 0,0,0? You must have a camera.lookAt() overriding your rotation somewhere in your code.

Dino4674 commented 12 years ago

Indeed I had, in my render function I left camera.lookAt(camera.target). I fotgot that the rotation is what I must change in my function not camera.target.

Can you answer my other question then. What would be the best way to set those rotation values?

Currently I have fixed mesh at 0,0,0 and it is a cube 200,200,200. Camera is fixed at 0,0,1500.

Now I would like to set camera's rotation based on roll, pitch and yaw I get from my mobile device. Currently I am doing this and there is some movement:

camera.rotation.x = roll / Math.PI
camera.rotation.y = yaw / Math.PI
camera.rotation.z = pitch / Math.PI

So the question is. Is this the right math I am doing here and does it matter which axis rotation goes first? Those roll, yaw and pitch I get from my device are values from -PI to +PI

mrdoob commented 12 years ago

That should work I think. By default rotation order is xyz.

Dino4674 commented 12 years ago

Ok, I'll play with it now. Just wanted to know if I was using these values correctly and in which order should I rotate camera. Thank you.

Dino4674 commented 12 years ago

@mrdoob I remember I red somewhere on this issues list about camera which can have roll, pitch and yaw values to be set. I am struggling now with that thing. I have roll, pitch and yaw values and now I see that those values are relative to the camera's axis with some refference point.

So lets say that I init camera and it's rotation is (0,0,0) and roll, pitch and yaw are (0, 0, 0)

What I would like to do is set camera's rotation (x, y, z) based on those roll, pitch and yaw values.

Can you give me some tips about it?

mrdoob commented 12 years ago

What do you have so far?

Dino4674 commented 12 years ago

Well I've been doing some research about rotations and realized that roll, pitch and yaw are not so good for transformations in 3D graphics and that quaternions or matrices are better solution. I see that Object3D can also use quaternions so this is good.

I'm most familiar with roll, pitch and yaw because they are more natural and right now I am reading an article about matrices and quaternions so I can understand them better.

I can easily acces my mobile device's orientation and I can read them as (roll, pitch, yaw) or I can read quaternions (X, Y, Z, W) or I can read rotation matrix.

I guess then I should go with quaternions here and do something like this:


//at init
camera.useQuaternions = true;

//refreshing the camera orientation latter in code
camera.quaternion.x = deviceQuaternion.x;
camera.quaternion.y = deviceQuaternion.y;
camera.quaternion.z = deviceQuaternion.z;
camera.quaternion.w = deviceQuaternion.w;

I tried this directly but it is not behaving as expected, I guess I am not using properly those values. Do I have to do some math also before?

mrdoob commented 12 years ago

Maybe the coordinate system from the device is different from the one we use? We use right handed:

Dino4674 commented 12 years ago

iOS devices also use right handed system:

device

So when I say that I can read device's orientation, It is done from some reference point which I can choose (trueNorth, magneticNorth). And then rotation 0,0,0 is when the device is in this position:

deviceNorth Lets say that device is laying on a flat table and its right side is pointing north. That is (0, 0, 0) orientation.

I know that ThreeJS has reference point when camera is standing straight and looks down the Z-axis in it's own coordinate system.

What I would like to do is "remap" somehow that values from phone so that it gives me (0,0,0) when device is standing straight and looking at the north (it's back size is pointed north) just like ThreeJS camera is looking down the Z-axis when it's rotation is (0, 0, 0)

mrdoob commented 12 years ago

That is (0, 0, 0) orientation.

I think that's incorrect. Orientation needs to be either (1,0,0), (0,1,0), (0,0,1), (-1,0,0), (0,-1,0), ...

mrdoob commented 12 years ago

What I would like to do is "remap" somehow that values from phone so that it gives me (0,0,0) when device is standing straight and looking at the north (it's back size is pointed north) just like ThreeJS camera is looking down the Z-axis when it's rotation is (0, 0, 0)

Oh wait. So you're saying that the rotation is working fine but that your model needs to be rotated so it's standing up instead?

Dino4674 commented 12 years ago

Sorry, I am talking about roll, pitch and yaw values here. They are (0,0,0) when device is in described postion.

Dino4674 commented 12 years ago

Actually I am saying both. I need to rotate the model so it stands up and that should be (0,0,0) just like in ThreeJS. And I need to set correct values to ThreeJS's camera and for that I think quaternions are better then roll, pitch and yaw.

Dino4674 commented 12 years ago

I monitored the values of x,y,z and w on a device.

w is always 0.

x,y,z are (0, 0, 0) when device is flat on table and X+ axis is pointing north (X+ of device).

Values goes from 0 to 0.99999 and sometimes -0.03 or something like this. So the question is, can I directly put those values in Object3D's (Camera's) quaternion variable or do I have to do some math in between because if I put them directly I don't get expected behavior.

WestLangley commented 12 years ago

@Dino4674 Do you understand what your mobile device output represents?

Will you answer the questions I asked you in this related thread? https://github.com/mrdoob/three.js/issues/1439#issuecomment-4312559

I'm sorry, but without additional information, it is very hard to help you.

Dino4674 commented 12 years ago

@WestLangley I understand that output and values (roll, pitch, yaw). They are behaving exactly as they should. Today I started learning about quaternions so I don't understand those (x,y,z,w) values but I guess they are correct since (roll, pitch, yaw) are OK.

Sorry for not answering there, I thought I could solve this with Euler angles but then I learned some things about them and realized that they are not so cool.

  1. Not sure but I think it is Roll -> Pitch -> Yaw. Didn't find in documentation but I think it is not matter since I should use quaternions (although I am still learning them...)
  2. Not sure if I understood from wikipedia but I guess this will answer. Example scenario:

    • Device is laying on a flat table (screen is pointing up) and right side of device (X+ axis) is pointing north. (roll, pitch, yaw) = (0, 0, 0) (x,y,z,w) = (0,0,0,0)
    • I tilt device to the right over it's Y axis by 90 deg (now screen is "looking at north") (roll, pitch, yaw) = (1.57, 0, 0) .. that should be PI/2. (x,y,z,w) = (0, 0.7, 0, 0) .. that should be something about those imaginary values for 90deg in quaternions
    • Put the device back in init position (roll, pitch, yaw) = (0, 0, 0) (x, y, z, w) = (0, 0, 0, 0)
    • Rotate over Z axis by 180 deg. Now device is flat on table but it's left side is pointing north and screen is again pointing up. (roll, pitch, yaw) = (0, 0, 3.14) (x, y, z, w) = (0, 0, 0.999, 0)
    • Then I again tilt device by 90 deg to the right (screen is now looking south) (roll, pitch, yaw) = (1.57, 0, 3.14) (x, y, z, w) = (- 0.7, 0 , 0.7, 0)

      You see in this last rolling, I rolled device by 90deg and it is showing me roll 1.57 just like the first time. But that rotation was over Y axis counterclockwise this time since I first rotated device by Z-axis (yawed) first.

  3. Based on this, pitch values on iOS goes form -PI/2 to +PI/2, not whole PI. What does it mean for ThreeJS's objects?

If I need to tell you some more info, please let me know, thank you.

WestLangley commented 12 years ago

This is hard to do long-distance, I hope someone else who has an iPhone will chime in. (I don't have one.)

It looks to me, and I am making an educated guess, that the triple is ( roll, pitch, yaw ).

I am guessing that the Euler order is ZXY; that is, yaw-pitch-roll.

That means that given a desired reading of ( roll, pitch, yaw ), to orient the device so it gives that reading you:

  1. rotate the device around it's internal z-axis by yaw radians
  2. rotate the device around it's internal x-axis by pitch radians
  3. rotate the device around it's internal y-axis by roll radians

Test out the theory for a lot of triples -- including rotation like (PI/4, PI/2, -Pi/4), to see if the theory is correct.

If my guess is wrong, I think I'll have to ask you to get someone else to give it a shot, as this is a bit difficult long-distance.

The goal is to understand what the readings mean, and what order they are intended to be applied to achieve the specified orientation.

Dino4674 commented 12 years ago

I understand it's hard to do long-distance so let me try to explain with screen shoots.

I've put camera at (0,0,0) and plane mesh at (0,0,-1500). Mesh size is (200, 200). By default, camera is looking down the Z-axis in ThreeJS.

This are mobile's coordinates: device

And when mobile is in this postion, (Roll, Pitch, Yaw) are (0,0,0) and now ThreeJS's X,Y,Z are at the same position as iPhone's X,Y,Z. And device's rotation "matches" ThreeJS's camera rotation. (0,0,0) deviceNorth

Now below are some of the examples of rotating the device: (when I say (0,0,0) I'll be referencing Roll, Pitch, Yaw values)

I rotate ThreeJS's camera like this in code:

camera.rotation.x = deviceRotation.pitch;
camera.rotation.y = deviceRotation.roll;
camera.rotation.z = deviceRotation.yaw;

Values in red label are values from mobile device rotation. And imagine that you are holding a device in your hand while it is looking at the ground and north is at yours right side.

Init position:

(0,0,0) and ThreeJS camera rotation is (0,0,0) init

I roll device to the right now and set the camera's rotation and it is behaving as expected: roll

Now I roll it left and it is behaving as expected: left

Now I pitch device down: pitch

Pitch up: pitchUp

Back in init (0,0,0) and yaw for 180 deg and it is as expected: yaw

But here comes the problems.. if I now roll device to the right I don't get expected values, because roll is still positive and I acctuly did negative rotation over y axis adn that image should be on screen's right side: yawRoll

Roll left: yawRollLeft

Pitch down, image shoud go up, not down: down

Pitch up, image should go down: upPitch

Back in init (0,0,0):

Yaw 90deg OK: yaw90

Pitch up. Image goes left when it should go down: up

Pitch down. Image goes right when it should go up: down

Roll right: roll

Roll left: left

I hope this explains situation better to you.

I guess that I need some way of rotating the camera over it's own axis not the scene's or something like that.

EDIT: I watched values more carefully and I realized that those values on phone are relative to the gravity. So that means that Roll represents an angle between iPhone's X-axis and surface (gravity is vertical to surface). Pitch represents an angle between iPhone's Y-axis and surface. And Yaw represents rotation over gravity vector.

Dino4674 commented 12 years ago

@WestLangley Just to let you know that I found a solution. It helped a lot that you noticed that iPhones Euler's order is ZXY.

All I had to do was:

camera.eulerOrder = 'ZXY';

.. and later in refreshing method:

camera.rotation.x = deviceRotation.pitch;
camera.rotation.y = deviceRotation.roll;
camera.rotation.z = deviceRotation.yaw;
mrdoob commented 12 years ago

Yay! Success! :D