Sign in to follow this  
orphean

Problems with Vectors

Recommended Posts

Hi all, I'm making a sort of 'mostly 2d' tile engine but using 3d graphics for the lot of it (nothing new to you guys I'm sure, just textured quads for the most part). For the standard 2d mode I'm just using an orthographic camera and all works as I want. For the 3d stuff I have run into a problem. I want to find a way to lock the camera's view onto a sprite irregardless of the camera's location in the scene. So irregardless of the value of the camera's eye vector I want to rotate the look vector to point at the sprite that's targeted. Vector math is a whole new area for me. I suspect this has something to do with taking the cross product of the sprite's vector and the camera's look vector but I really have no idea. I don't need anyone to spell it out step by step with commented code or anything but a general explanation to do what I'm trying to do would be awesome. ANY help is greatly appreciated. Thanks! Orphean

Share this post


Link to post
Share on other sites
Are you using directx, or opengl?

Cause there a specific functions that generate the views via transofrmation matrix, unless of course you gnerate thme yourself

Edit: you jus generate a transformation matrix, like i said above.

Share this post


Link to post
Share on other sites
I'm using Direct3d.

Thanks for the reply I guess I'll go lookup what I can about generating transformation matrices in the api.

Share this post


Link to post
Share on other sites
Quote:
Original post by orphean
I'm using Direct3d.
It sounds like what you want is a 'look at' matrix; DirectX has functions for this, but it's also fairly straightforward to generate your own. Just ask if you need further details.

Share this post


Link to post
Share on other sites
Quote:
Original post by orphean
Any details you're willing to part with would be fantastic! :)


the first step to constructing the lookat matrix is defining local your coordinate axis wrt your target.

to build the matrix, you'll need to provide the worlds "up" vector (typically +Y, in the directX coord system). knowing the "up" vector and the target location, construct your axis like so:

(note that after finding each of these vectors, they should be normalized)
vector vZ = target position - camera position;
vector vX = vZ (cross) up;
vY = vX (cross) vZ; <--(no need to normailze this, the magnitude of the cross product of two orthonormal vectors is always 1)

the last part of your lookat matrix (it will be a 4x4) comes from taking the inverse of the translation from the world origin to your cameras current position, which, with a little more vector math, gives you the following matrix:
(let P = cameras world position)

| vX.x, vX.y, vX.z, 0|
| vY.x, vY.y, vY.z, 0|
| vZ.x, vZ.y, vZ.z, 0|
|-(vX (dot) P), -(vY (dot) P), -(vZ (dot) P), 1|

disclaimer: my vector math is a little rusty, the matrix may be a bit off, but its something like that

edit: another way of solving this problem, which you elluded to in your first post, would be to make a vector from the camera position to the target, take the cross product of that vector with the cameras current look vector to find an axis orthogonal to both, and rotate your camera about that axis by the angle between the two vectors (as can be found with a dot product). this will accomplish the same thing as the lookat matrix, but I believe it would be a bit slower.

[Edited by - Driv3MeFar on June 11, 2006 4:57:28 AM]

Share this post


Link to post
Share on other sites
"edit: another way of solving this problem, which you elluded to in your first post, would be to make a vector from the camera position to the target, take the cross product of that vector with the cameras current look vector to find an axis orthogonal to both, and rotate your camera about that axis by the angle between the two vectors (as can be found with a dot product). this will accomplish the same thing as the lookat matrix, but I believe it would be a bit slower."

This is exactly what I need. The speed-factor isn't a real issue for this application.

> would be to make a vector from the camera position to the target

Any ideas on how to accomplish this? I know the sprite's location <0,0,0> we'll say. And then we'll say the camera is at <20,30,500>. How do I make a vector from <20,30,500> to the sprite?

And... > find an axis orthogonal to both,

How do I determine axis orthogonality? :)

That is exactly what I need though, and those are my final two stumbling blocks. Thank you so much for the help everyone so far!!!! :D

Orphean

Share this post


Link to post
Share on other sites
Quote:
Original post by orphean
> would be to make a vector from the camera position to the target

Any ideas on how to accomplish this? I know the sprite's location <0,0,0> we'll say. And then we'll say the camera is at <20,30,500>. How do I make a vector from <20,30,500> to the sprite?


If you have two points, you can find a vector simply by subtracting the two. Geometrically, this finds you the displacement between the two points, and a vector is really just a displacement (a physics vector at least, if/when you take linear algebra, a vector is simply an element of a vector space, but I digress). So with the sample points you provided:
vector v = <0, 0, 0> - <20, 30, 500> = <-20, -30, -500>;
you will then want to normalize the vector by dividing it by its magnitude:
v /= |v|, where |v| = sqrt((-20)^2 + (-30)^2 + (-500)^2)

(Actualy, since your not building a lookAt matrix, I don't think you need to normalize, but if you wanted to, thats how you would.)

Quote:
Original post by orphean
And... > find an axis orthogonal to both,

How do I determine axis orthogonality? :)


With a cross product. By definition, the cross product of two vectors is a vector perpendicular (orthogonal) to both of the origninal vectors.

The cross product of two vectors, u and v, is:
u (cross) v = (u.y * v.z - u.z * v.y)i - (u.x * v.z - u.z * v.x)j + (u.x * v.y - u.y * v.x)k
Where i = <1, 0, 0>, j = <0, 1, 0>, and k = <0, 0, 1>

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Perfect.

Driv3MeFar next time I drive to the Seattle area I owe you a beer ;)

Thanks much for the help.

orphean

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Perfect.

Driv3MeFar next time I drive to the Seattle area I owe you a beer ;)

Thanks much for the help.

orphean


Don't thank me yet, wait until you get it working in code.

I will, however, take you up on the beer anytime.

Glad to help.

Share this post


Link to post
Share on other sites
Okay one last thing hopefully.

In the result of the cross product I've noticed that only one item is negative at any one time.

Is that negative item the orthogonal axis? :) (ie, I test to see which item is negative to find my axis?)

Also, the angle seems very large when I take the dot product. Is that normal? It has a tendency to return stuff like -600, etc which doesn't make much sense to me since I would be doing multiple full rotations using that. Or should I just not worry about it.

The numbers were all very large (like 80000-100000+) so I normalized the offset vector like you said and they all got toned down.

Here's my current code.


void ElCamera::Normalize(gobVector3 *v)
{
float magSq = v->x * v->x + v->y * v->y + v->z * v->z;
if(magSq > 0.0f)
{
float oneOverMag = 1.0f / sqrt(magSq);
v->x *= oneOverMag;
v->y *= oneOverMag;
v->z *= oneOverMag;
}
}

void ElCamera::Cross(gobVector3 *out, gobVector3 *a, gobVector3 *b)
{
out->x = a->y * b->z - a->z * b->y;
out->y = a->z * b->x - a->x * b->z;
out->z = a->x * b->y - a->y * b->x;
}

void ElCamera::LookAt(float x, float y, float z, HBFONT fnt)
{
gobVector3 spriteVec;
gobVector3 offsetVec;
gobVector3 camEyeVec;
gobVector3 camLookVec;
gobVector3 crossVec;
float angle;
char buf[2048];

spriteVec.x = x;
spriteVec.y = y;
spriteVec.z = z;

iCamera::GetVectorEye(&camEyeVec);
iCamera::GetVectorLook(&camLookVec);

offsetVec = spriteVec - camEyeVec;
Normalize(&offsetVec);

Cross(&crossVec, &offsetVec, &camLookVec);

angle = offsetVec.Dot(&camLookVec);

// Snipped some output stuff here

}




Here's some sample output:


SpriteVec-> X: 582 Y: 128 Z: 48
camEyeVec-> X: 366 Y: 538 Z: 150
camLookVec-> X: 368 Y: 268 Z: 0
OffsetVec-> X: 0.463622 Y: -0.858559 Z: -0.218933
CrossVec-> X: 58.6739 Y: -80.1293 Z: 438.483
Angle: -60.387



That look right?

Orphean

[Edited by - orphean on June 13, 2006 6:15:23 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by orphean
Okay one last thing hopefully.

In the result of the cross product I've noticed that only one item is negative at any one time.

Is that negative item the orthogonal axis? :) (ie, I test to see which item is negative to find my axis?)

Also, the angle seems very large when I take the dot product. Is that normal?

The numbers were all very large (like 80000-100000+) so I normalized the offset vector like you said and they all got toned down.


The cross product results in a vector, which should be comprised of 3 elements: the x, y, and z components of the vector. Your rotation axis is going to be the vector which contains all three of these components, so you have to use all of them.

This requires rotation about an arbitrary axis, which is fairly complex, but here are the basics:
The cross product will give you a vector U (which you should normalize; above I said that you might not have to, but I was wrong), which is composed of the elements u1, u2, and u3 (your x, y, and z components, respectively). Using these three components, first construct the matrix:

| 0, -u2, u1 |
S = | u2, 0, -u3 |
|-u1, u3, 0 |

Using that matrix, your final rotation matrix for an angle Theta will be (non-trivially):

R = I + sin(Theta)(S) + (1 - cos(Theta))(S^2);

Where I is the identity matrix, and S is as defined above.

As for the dot product results, the dot product formula is u.v = |u||v|cos(Theta), so the cosine of the angle is getting multipied by the magnitude of each vector. If you are dealing with large vectors, this would explain why your results are so huge. Normalizing will fix this problem, as bot |u| and |v| will be 1, so the dot product will result in just the cosine of the angle between the two vectors.
In the code you posted, your angle will be:

acos(offsetVec.Dot(&camLookVec));

Assuming camLookVec is normalized.

[Edited by - Driv3MeFar on June 13, 2006 7:36:47 AM]

Share this post


Link to post
Share on other sites
Okay. This is beginning to make more sense to me so that's a good sign.

First -
R = I + sin(Theta)(R) + (1 - cos(Theta))(S^2);

Should that be R = I + sin(Theta)(*S*) + (1 - cos(Theta))(S^2); since R isn't defined when I'm calculating it?

And then, just to make sure I got my head screwed on straight. I would then multiple the camLookVec by the rotation matrix to get the new LookVec, correct?

Thanks for the help here, seriously.

Orphean

Share this post


Link to post
Share on other sites
Quote:
Original post by orphean
First -
R = I + sin(Theta)(R) + (1 - cos(Theta))(S^2);

Should that be R = I + sin(Theta)(*S*) + (1 - cos(Theta))(S^2); since R isn't defined when I'm calculating it?


Yes, my mistake.

Quote:
Original post by orphean
And then, just to make sure I got my head screwed on straight. I would then multiple the camLookVec by the rotation matrix to get the new LookVec, correct?


Correct. Or, if you wanted to animate the camera moving to that position over time rather than just jumping to it, you could introduce a time variable, 0 <= t <= 1, and every set ammount of time (eg, every game loop, every 10th of a second, or however you wanted to do it), increment t by some small ammount (eg, t += 0.01f), multiply your original Theta value by t, and recalculate the rotation matrix (To do this, you would need to recalculate the orginal rotation matrix, using your original U value, which would then be applied to your original camLookVec. Its important that you do this recalculation every frame of animation, because if you do it incrementaly, floating point errors can accumulate and throw off the animation).

Share this post


Link to post
Share on other sites
Man I am so close I can just feel it ;)

Using the explained method results in something I cannot adequately put into words. Sort of perpendicular to the z plane. When the sprite moves the camera is moving slightly but not pointing towards the sprite.

Here is a picture of the oddball behavior along with the results of the below code.

Apologize for the ridiculous nature of the code, I'm trying to be as clear as possible in it while I'm working it out.


void ElCamera::Normalize(gobVector3 *v)
{
float magSq = v->x * v->x + v->y * v->y + v->z * v->z;
if(magSq > 0.0f)
{
float oneOverMag = 1.0f / sqrt(magSq);
v->x *= oneOverMag;
v->y *= oneOverMag;
v->z *= oneOverMag;
}
}

void ElCamera::Cross(gobVector3 *out, gobVector3 *a, gobVector3 *b)
{
out->x = a->y * b->z - a->z * b->y;
out->y = a->z * b->x - a->x * b->z;
out->z = a->x * b->y - a->y * b->x;
}

struct ElMatrix {
float _11, _12, _13;
float _21, _22, _23;
float _31, _32, _33;
};

void ElCamera::LookAt(float x, float y, float z, HBFONT fnt)
{
gobVector3 spriteVec;
gobVector3 offsetVec;
gobVector3 camEyeVec;
gobVector3 camLookVec;
gobVector3 crossVec;
gobVector3 camNewLookVec;
ElMatrix identityMatrix;
ElMatrix SMatrix;
ElMatrix sinMatrix;
ElMatrix cosMatrix;
ElMatrix rotMatrix;
float theta;
float dotProduct;
char buf[2048];

spriteVec.x = x;
spriteVec.y = y;
spriteVec.z = z;

iCamera::GetVectorEye(&camEyeVec);
iCamera::GetVectorLook(&camLookVec);

offsetVec = spriteVec - camEyeVec;

Normalize(&offsetVec);
Normalize(&camLookVec);

Cross(&crossVec, &offsetVec, &camLookVec);
Normalize(&crossVec);

dotProduct = offsetVec.x * camLookVec.x +
offsetVec.y * camLookVec.y +
offsetVec.z * camLookVec.z;
theta = acos(dotProduct);

/*
| 0, -u2, u1 | u1 = crossVec.x;
S = | u2, 0, -u3 | u2 = crossVec.y;
|-u1, u3, 0 | u3 = crossVec.z;

*/

SMatrix._11 = 0,
SMatrix._12 = -crossVec.y;
SMatrix._13 = crossVec.x;
SMatrix._21 = crossVec.y;
SMatrix._22 = 0;
SMatrix._23 = -crossVec.z;
SMatrix._31 = -crossVec.x;
SMatrix._32 = crossVec.z;
SMatrix._33 = 0;

/*
| 1, 0, 0, |
I = | 0, 1, 0, |
| 0, 0, 1 |
*/

identityMatrix._11 = 1;
identityMatrix._12 = 0;
identityMatrix._13 = 0;
identityMatrix._21 = 0;
identityMatrix._22 = 1;
identityMatrix._23 = 0;
identityMatrix._31 = 0;
identityMatrix._32 = 0;
identityMatrix._33 = 1;

/*
sinMatrix = S * sin(theta)
*/

double rotSin = sin(theta);
sinMatrix._11 = SMatrix._11 * rotSin;
sinMatrix._12 = SMatrix._12 * rotSin;
sinMatrix._13 = SMatrix._13 * rotSin;
sinMatrix._21 = SMatrix._21 * rotSin;
sinMatrix._22 = SMatrix._22 * rotSin;
sinMatrix._23 = SMatrix._23 * rotSin;
sinMatrix._31 = SMatrix._31 * rotSin;
sinMatrix._32 = SMatrix._32 * rotSin;
sinMatrix._33 = SMatrix._33 * rotSin;

/*
cosMatrix = (1 - cos(theta)) * (S*S)
*/

double rotCos = (1 - cos(theta));
cosMatrix._11 = rotCos * (SMatrix._11 * SMatrix._11);
cosMatrix._12 = rotCos * (SMatrix._12 * SMatrix._12);
cosMatrix._13 = rotCos * (SMatrix._13 * SMatrix._13);
cosMatrix._21 = rotCos * (SMatrix._21 * SMatrix._21);
cosMatrix._22 = rotCos * (SMatrix._22 * SMatrix._22);
cosMatrix._23 = rotCos * (SMatrix._23 * SMatrix._23);
cosMatrix._31 = rotCos * (SMatrix._31 * SMatrix._31);
cosMatrix._32 = rotCos * (SMatrix._32 * SMatrix._32);
cosMatrix._33 = rotCos * (SMatrix._33 * SMatrix._33);

/*
R = I + sin(Theta)(S) + (1 - cos(Theta))(S^2);
*/

rotMatrix._11 = identityMatrix._11 + sinMatrix._11 + cosMatrix._11;
rotMatrix._12 = identityMatrix._12 + sinMatrix._12 + cosMatrix._12;
rotMatrix._13 = identityMatrix._13 + sinMatrix._13 + cosMatrix._13;
rotMatrix._21 = identityMatrix._21 + sinMatrix._21 + cosMatrix._21;
rotMatrix._22 = identityMatrix._22 + sinMatrix._22 + cosMatrix._22;
rotMatrix._23 = identityMatrix._23 + sinMatrix._23 + cosMatrix._23;
rotMatrix._31 = identityMatrix._31 + sinMatrix._31 + cosMatrix._31;
rotMatrix._32 = identityMatrix._32 + sinMatrix._32 + cosMatrix._32;
rotMatrix._33 = identityMatrix._33 + sinMatrix._33 + cosMatrix._33;

/*

Multiply camLookVec by the rotation matrix.

*/

camNewLookVec.x = camLookVec.x*rotMatrix._11 +
camLookVec.y*rotMatrix._12 +
camLookVec.z*rotMatrix._13;

camNewLookVec.y = camLookVec.x*rotMatrix._21 +
camLookVec.y*rotMatrix._22 +
camLookVec.z*rotMatrix._23;

camNewLookVec.z = camLookVec.x*rotMatrix._31 +
camLookVec.y*rotMatrix._32 +
camLookVec.z*rotMatrix._33;

mLookVec.x = camNewLookVec.x;
mLookVec.y = camNewLookVec.y;
mLookVec.z = camNewLookVec.z;

iCamera::SetVectorLook(&camNewLookVec);

// Output code snipped.
}





What concerns me in particular, is look at the values of the CameraLookVec.

X: -1.31294e-006 Y: 0.999999 Z: 0.00116981

That seems... incorrect. :D

The only thing I can think of maybe is in this operation:

cosMatrix = (1 - cos(theta)) * (S*S)

I'm treating the S^2 just like a scalar but maybe I should be multiply the matrix against itself using the matrix multiplication stuff?

If anyone could see anything else wrong with what I'm doing that would be great. I have this feeling I'm really close and its killing me :)

orphean

[Edited by - orphean on June 13, 2006 10:04:22 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by orphean
I'm treating the S^2 just like a scalar but maybe I should be multiply the matrix against itself using the matrix multiplication stuff?


That would be a problem, yes. "Squaring a matrix" means matrix-multiplying it with itself, not squaring the values element-wise.

Share this post


Link to post
Share on other sites
Thank you for the clarification! :)

I switched to using proper matrix multiplication for the S^2. The numbers are no longer being expressed in scientific notation so thats a step forward.

However I'm getting precisely the same behavior as I explained above.


I am just baffled by this. I know I must be doing something wrong but I just can't see it.

Orphean.

Share this post


Link to post
Share on other sites
edit: I think the results your getting are correct, I just may have provided you with the wrong way of doing things.

The rotation method I described is for rotating objects about an arbitrary axis. It is unideal for a camera, because there is usualy (as is the case here) some rotation about the Z axis, which causes the camera's "up" vector to rotate, leading to the tilt you're seeing.

To rotate your camera, I would look into the D3D function:

HRESULT RotateYawPitchRoll(
FLOAT Yaw,
FLOAT Pitch,
FLOAT Roll
);

Where Yaw is the rotation about the y-axis, pitch is about the x-axis, and roll is about the z-axis (should be 0 for your camera).

To find the Yaw and Pitch values, orthographically project your vectors on the X and Y axis, respectively (that is, set their X or Y values = 0), and use dot products to find your angles.

For example:
offsetVec = <0, 1/(2 * sqrt(1.25)), 1/sqrt(1.25)>;
camEyeVec = <1/sqrt(2), 0, 1/sqrt(2)>;

float yawAngle = acos( <0, 1/2sqrt(1.25), 1/sqrt(1.25)> dot <0, 0, 1/sqrt(2)> );
float pitchAngle = acos( <0, 0, 1/sqrt(1.25)> dot <1/sqrt(2), 0, 1/sqrt(2)> );
float rollAngle = 0;

Those two angles should be whats needed to rotate about X and Y to allign your camera with the sprite.

Note that this accomplishes pretty much the same thing as the lookAt matrix described above.

[Edited by - Driv3MeFar on June 13, 2006 5:31:47 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this