Advertisement

Quaternions: At which angle is cam looking at ship

Started by March 11, 2013 04:40 PM
4 comments, last by alvaro 11 years, 11 months ago

Hello there!

The problem

I'm working on a retro space action game in Unity3D. I say retro because we do not use 3D models to visualize other ships in space but sprites, rendered from a total of 544 angles. I need to find the right sprite (ie. the right angle) for each ship in space, relative to my own camera orientation. Both the camera and other ships have six degrees of freedom.

While I made some progress using Matrix math and some help I've gotten in the past here on the forums a while back, we have since switched to Unity3D, which works with quaternions. I'm well aware of the fact that I can simply stick to my old attempts and convert unit quaternions to rotation matrices and back, but I would optimally like to hear if there may be a smart solution that works with quaternions throughout.

My attempt so far


Vector3 getEulerAnglesFromQuaternion(Quaternion q1)

{

        float sqw = q1.w*q1.w;

        float sqx = q1.x*q1.x;

        float sqy = q1.y*q1.y;

        float sqz = q1.z*q1.z;

        float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor

        float test = q1.x*q1.y + q1.z*q1.w;

        

        float pitch, yaw, roll;

        

        if (test > 0.499*unit)

        // singularity at north pole

        {

            yaw = 2 * Mathf.Atan2(q1.x,q1.w);

            roll = Mathf.PI/2;

            pitch = 0;

        }

        else if (test < -0.499*unit)

        // singularity at south pole

        {

            yaw = -2 * Mathf.Atan2(q1.x,q1.w);

            roll = -Mathf.PI/2;

            pitch = 0;

        }

        else

        {

            yaw = Mathf.Atan2(2*q1.y*q1.w-2*q1.x*q1.z , sqx - sqy - sqz + sqw);

            roll = Mathf.Asin(2*test/unit);

            pitch = Mathf.Atan2(2*q1.x*q1.w-2*q1.y*q1.z , -sqx + sqy - sqz + sqw);

        }

        

        return new Vector3(pitch, yaw, roll);

}
 
 
// in Ship::Update():
 
Quaternion q1 = Quaternion.LookRotation(Camera.main.transform.position - transform.position);

Quaternion q2 = transform.rotation;

Quaternion q = q1 * q2;

Vector3 eulerAngles = getEulerAnglesFromQuaternion(q) * Mathf.Rad2Deg;
// use eulerAngles to pick the right sprite

My idea was to combine the ship with the camera rotation by multiplication and then use a standard formula to retrieve euler angles from the resulting quaternion. This, however, simply does not yield the expected angle values. I'm not sure if my problem can even be solved in this fashion.

In short

I would like to know if anybody has an idea how to solve my problem of finding the angle at which a camera is looking at something, combining that with the orientation of the entity itself and using the result to find the right sprite to pick for it. I have access to what seems to be very good and complete math functionality in Unity3D and would like, if possible, to find a solution following the engine's quaternion-based approach. Are quaternions even usable in this case? Or am I forced to use matrix math here?

How are those 544 angles defined?

I've given this problem some thought before, and what I would try is to define the positions based on the quaternion, not Euler angles. Similarly to how a cube is used for environment mapping:
* Start with a unit quaternion (q[0] + q[1]*i + q[2]*j + q[3]*k).
* Identify the component with largest absolute value (say it's q[2]).
* Divide the other components by q[2].

Now your rotation space has been divided into 4 3-dimensional blocks. You can now divide the 3-dimensional blocks into cells. For instance, if you use 5x5x5 encoding within each block, you need a total of 500 positions.

I originally thought of this scheme for compressing animations, but it seems to fit the bill here nicely.
Advertisement

That sounds intriguing, excellent idea!

I'm not exactly an expert with quaternions yet, but I do know the basics (x, y, z define an arbitrary axis around which we rotate by w). How does dividing the other components by the largest one (x, y, z, with w or without?) leave me with a 4 cube division? I don't understand that very fundamental step!

The 544 angles are 32 yaws and 17 pitches!

I'm not exactly an expert with quaternions yet, but I do know the basics (x, y, z define an arbitrary axis around which we rotate by w). How does dividing the other components by the largest one (x, y, z, with w or without?) leave me with a 4 cube division? I don't understand that very fundamental step!

Yes, w is included. Imagine x has the largest absolute value. Then you compute (y/x, z/x, w/x), which has each coordinate between -1 and 1. In that sense, it is a cube. Since there are 4 variables, you get 4 different cubes. This is similar to the situation with cubemaps, where you map a point in a sphere to one of 6 square faces.

The 544 angles are 32 yaws and 17 pitches!

Hmmm... Nothing for roll?


I'm not exactly an expert with quaternions yet, but I do know the basics (x, y, z define an arbitrary axis around which we rotate by w). How does dividing the other components by the largest one (x, y, z, with w or without?) leave me with a 4 cube division? I don't understand that very fundamental step!

Yes, w is included. Imagine x has the largest absolute value. Then you compute (y/x, z/x, w/x), which has each coordinate between -1 and 1. In that sense, it is a cube. Since there are 4 variables, you get 4 different cubes. This is similar to the situation with cubemaps, where you map a point in a sphere to one of 6 square faces.


Okay so I see how I end up with a Vector3(y/x, z/x, w/x) if x is larger than y/z/w for example. Let's say the vector is (-1, -1, 1). I understand how this always lies within a unit cube around the origin.

But then you say there are four variables, that means we get four of these cubes. This sounds, to me, like I need to then do the same procedure but not with Vector3(y/x, z/x, w/x) but Vector3(x/y, z/y, w/y) for example? Is this correct or am I misunderstanding you? If this is the correct procedure, then the largest absolute value part is confusing me, do I just let that determine the quaternion element that I start with?

Lots of questions here, sorry! tongue.png I hope you have (or someone else has) some time to explain the concept some more!


The 544 angles are 32 yaws and 17 pitches!

Hmmm... Nothing for roll?


In order to save memory we want to rotate the billboards via code ourselves! The 17 pitches also do not describe a full 360 deg turn, since we only need half a circle. Then the other half of pitches can be translated to a different set of pitches with a 180 deg yaw rotation, if that makes sense! So: 32 yaw angles = 360 deg yaw rotation, 17 pitch angles = 180 deg pitch rotation, no roll angles = rotating via code!
I think you do understand what I described earlier. So yes, the variable with the highest absolute value determines which cube you are in, and within that cube you use the other three variables divided by the one with largest absolute value.

If you are going to rotate the billboards, perhaps going with Euler angles wasn't such a bad idea after all. If you use the right order of the axes, you can manage to separate one of the angles into being a billboard rotation, and then you only have two degrees of freedom to discretize.

So let me read your original question again... smile.png

My idea was to combine the ship with the camera rotation by multiplication and then use a standard formula to retrieve euler angles from the resulting quaternion. This, however, simply does not yield the expected angle values. I'm not sure if my problem can even be solved in this fashion.

That sounds reasonable, except you probably need to multiply one of the quaternions by the inverse of the other, or something like that. If the ship is not in the center of the image you may have to do something slightly different... So, in short, I need to think about it a little more.

This topic is closed to new replies.

Advertisement