开发者

Circular rotation around an arbitrary axis

I am programming Starcraft 2 custom maps and got some proglems with math in 3D. Currently I am trying to create and rotate a point around an arbitrary axis, given by x,y and z (the xyz vector is normalized).

I've been trying around a lot and read through a lot of stuff on the internet, but I just cant get how it works correctly. My current script (you probably dont know the language, but it's nothing special) is the result of breaking everything for hours (doesn't work correctly):

    point CP;
fixed AXY;
point D;
point DnoZ;
point DXY_Z;
fixed AZ;
fixed LXY;
missile[Missile].Angle = (missile[Missile].Angle + missile[Missile].Acceleration) % 360.0;
missile[Missile].Acceleration += missile[Missile].AirResistance;
if (mis开发者_如何学JAVAsile[Missile].Parent > -1) {
    D = missile[missile[Missile].Parent].Direction;
    DnoZ = Point(PointGetX(D),0.0);
    DXY_Z = Normalize(Point(SquareRoot(PointDot(DnoZ,DnoZ)),PointGetHeight(D)));
    AZ = MaxF(ACos(PointGetX(DXY_Z)),ASin(PointGetY(DXY_Z)))+missile[Missile].Angle;
    DnoZ = Normalize(DnoZ);
    AXY = MaxF(ACos(PointGetX(DnoZ)),ASin(PointGetY(DnoZ)));
    CP = Point(Cos(AXY+90),Sin(AXY+90));
    LXY = SquareRoot(PointDot(CP,CP));
    if (LXY > 0) {
        CP = PointMult(CP,Cos(AZ)/LXY);
        PointSetHeight(CP,Sin(AZ));
    } else {
        CP = Point3(0.0,0.0,1.0);
    }
} else {
    CP = Point(Cos(missile[Missile].Angle),Sin(missile[Missile].Angle));
}
missile[Missile].Direction = Normalize(CP);
missile[Missile].Position = PointAdd(missile[Missile].Position,PointMult(missile[Missile].Direction,missile[Missile].Distance));

I just cant get my mind around the math. If you can explain it in simple terms that would be the best solution, a code snipped would be good as well (but not quite as helpful, because I plan to do more 3D stuff in the future).


http://en.wikipedia.org/wiki/Rotation_matrix. Look under the section Rotation matrix from axis and angle. For your convenience, here's the matrix you need. It's a bit hairy. theta is the angle, and ux, uy, and uz are the x, y, and z components of the normalized axis vector

Circular rotation around an arbitrary axis

If you don't understand matrices and vectors, post back and I'll help you.


A useful method for doing such rotations is to do them with quaternions. In practice, I've found them to be easier to use and have the added bonus of avoiding Gimbal lock.

Here is a nice walk through that explains how and why they are used for rotation about an arbitrary axis (it's the response to the user's question). It's a bit higher level and would be good for someone who is new to the idea, so I recommend starting there.

Update to avoid link corrosion

The text from the linked site:

As you have no doubt already concluded, rotation around the axis passing through the origin and a point (a,b,c) on the unit sphere in three-dimensions is a linear transformation, and hence can be represented by matrix multiplication. We will give a very slick method for determining this matrix, but to appreciate the compactness of the formula it will be wise to start with a few remarks.

Rotations in three-dimensions are rather special linear transformations, not least because they preserve the lengths of vectors and also (when two vectors are rotated) the angles between the vectors. Such transformations are called "orthogonal" and they are represented by orthogonal matrices:

M M' = I

where we conveniently denote the transpose by '. In other words the transpose of an orthogonal matrix is its inverse.

Consider the data which is needed to define the transformation. You've already given notation for the axis of rotation, ai + bj + ck, conveniently assumed to be a unit vector. The only other datum is the angle of rotation, which for lack of a more natural character I will denote by r (for rotation?) and which we will assume to be given in radians.

Now the rotations are actually a bit special even among orthogonal transformations, and in fact they are also called special orthogonal transformations (or matrices) in virtue of their property of being "orientation preserving". Compare them with reflections, which are also length and angle preserving, and you will find that the geometric characteristic of preserving orientation (or "handedness" if you prefer) has a numerical counterpart in the determinant of the matrix. A rotation's matrix has determinant 1, while a reflection's matrix has determinant -1. It turns out that the product (or composition) of two rotations is again a rotation, which agrees with the fact that the determinant of a product is the product of the determinants (or 1 in the case of a rotation).

Now we can describe a step by step approach that one might follow to construct the desired matrix (before we shortcut the whole process and jump to the Answer!). Consider first a step in which we rotate the unit vector:

u = ai + bj + ck

so that it coincides with one of the "standard" unit vectors, perhaps k (the positve z-axis). Now we know how to rotate around the z-axis; it's a matter of doing the usual 2x2 transformation on the x,y coordinates alone:

       cos(r) sin(r)   0
M  =  -sin(r) cos(r)   0
         0      0      1

Finally we need to "undo" that initial rotation that took u to k, which is easy because the inverse of that transformation is (we recall) represented by the matrix transpose. In other words, if matrix R represents a rotation taking u to k, then R' takes k to u, and we can write out the composition of transformations like this:

R' M R

It is easily verified that this product of matrices, when multiplied times u, gives u back again:

R' M R u = R' M k = R' k = u

Therefore this is indeed rotation about the axis defined by u.

One advantage of this expression is that it cleanly separates out the dependence of M on the angle r from the dependence of Q and Q' on the "axis" vector u. However if we have to carry out the computations in detail, we will obviously have a lot of matrix multiplication to do.

So, to the shortcut. It turns out when all the dust settles that the multiplication among rotations is isomorphic to multiplication of unit quaternions. Quaternions, in case you've not seen them before, are a kind of four-dimensional generalization of complex numbers. They were "invented" by William Hamilton in 1843:

[Sir William Rowan Hamilton] http://www-gap.dcs.st-and.ac.uk/~history/Mathematicians/Hamilton.html

and today's 3D graphics programmers are greatly in his debt.

Each unit quaternion q = q0 + q1*i + q2*j + q3*k then defines a rotation matrix:

     (q0² + q1² - q2² - q3²)      2(q1q2 - q0q3)          2(q1q3 + q0q2)

Q  =      2(q2q1 + q0q3)     (q0² - q1² + q2² - q3²)      2(q2q3 - q0q1)

          2(q3q1 - q0q2)          2(q3q2 + q0q1)     (q0² - q1² - q2² + q3²)

To verify that Q is an orthogonal matrix, ie. that Q Q' = I, means in essence that the rows of Q form an orthonormal basis. So, for example, the first row should have length 1:

(q0² + q1² - q2² - q3²)² + 4(q1q2 - q0q3)² + 4(q1q3 + q0q2)²

  = (q0² + q1² - q2² - q3²)² + 4(q1q2)² + 4(q0q3)² + 4(q1q3)² + 4(q0q2)²

  = (q0² + q1² + q2² + q3²)²

  =  1

and the first two rows should have dot product zero:

  [ (q0² + q1² - q2² - q3²), 2(q1q2 - q0q3), 2(q1q3 + q0q2) ]

   * [ 2(q2q1 + q0q3), (q0² - q1² + q2² - q3²), 2(q2q3 - q0q1) ]

 = 2(q0² + q1² - q2² - q3²)(q2q1 + q0q3)

   + 2(q1q2 - q0q3)(q0² - q1² + q2² - q3²)

   + 4(q1q3 + q0q2)(q2q3 - q0q1)

 = 4(q0²q1q2 + q1²q0q3 - q2²q0q3 - q3²q2q1)

   + 4(q3²q1q2 - q1²q0q3 + q2²q0q3 - q0²q2q1)

 =  0

It can also be shown in general that det(Q) = 1, and thus that Q is really a rotation.

But around what axis is Q the rotation? And by what angle? Well, given angle r and unit vector:

u = ai + bj + ck

as before, the corresponding quaternion is:

q = cos(r/2) + sin(r/2) * u

  = cos(r/2) + sin(r/2) ai + sin(r/2) bj + sin(r/2) ck

Thus with:

q0 = cos(r/2), q1 = sin(r/2) a, q2 = sin(r/2) b, q3 = sin(r/2) c,

we are able to get the desired property that multiplication by Q "fixes" u:

Q u = u

Rather than chug through the long-winded algebra, let's do a simple example.

Let u = 0i + 0.6j + 0.8k be our unit vector and r = pi be our angle of rotation.

Then the quaternion is:

q = cos(pi/2) + sin(pi/2) * u

  = 0 + 0i + 0.6j + 0.8k

and the rotation matrix:

        -1     0     0

Q =      0  -0.28  0.96

         0   0.96  0.28

In this concrete case it is easy to verify that Q Q' = I and det(Q) = 1.

Also we compute that:

Q u = [ 0, -0.28*0.6 + 0.96*0.8, 0.96*0.6 + 0.28*0.8 ]'

    = [ 0, 0.6, 0.8 ]'

    =  u

ie. the unit vector u defines the axis of rotation because it is "fixed" by Q.

Finally we illustrate that the angle of rotation is pi (or 180 degrees) by considering how Q acts on the unit vector in the direction of the positive x-axis, which is perpendicular to u:

i + 0j + 0k,  or as a vector, [ 1, 0, 0 ]'

Then Q [ 1, 0, 0 ]' = [-1, 0, 0 ]' which is the rotation of [ 1, 0, 0 ]' through angle pi about u.

As a reference for this representation of rotations by quaternions and some additional methods of representation (and what they are good for), see the details here:

[Representing 3D rotations] http://gandalf-library.sourceforge.net/tutorial/report/node125.html

SUMMARY

Given angle r in radians and unit vector u = ai + bj + ck or [a,b,c]', define:

q0 = cos(r/2),  q1 = sin(r/2) a,  q2 = sin(r/2) b,  q3 = sin(r/2) c

and construct from these values the rotation matrix:

     (q0² + q1² - q2² - q3²)      2(q1q2 - q0q3)          2(q1q3 + q0q2)

Q  =      2(q2q1 + q0q3)     (q0² - q1² + q2² - q3²)      2(q2q3 - q0q1)

          2(q3q1 - q0q2)          2(q3q2 + q0q1)     (q0² - q1² - q2² + q3²)

Multiplication by Q then effects the desired rotation, and in particular:

Q u = u


To perform a 3D rotation, you simply need to offset the point of rotation to the origin and sequentially rotate around each axis, storing the results between each axis rotation for use with the next rotation operation. The algorithm looks like as follows:

Offset the point to the origin.

Point of Rotation = (X1, Y1, Z1)
Point Location    = (X1+A, Y1+B, Z1+C)

(Point Location - Point of Rotation) = (A, B, C).

Perform rotation about the Z Axis.

    A' = A*cos ZAngle - B*sin ZAngle
    B' = A*sin ZAngle + B*cos ZAngle
    C' = C.

Next, perform a rotation about the Y Axis.

    C'' = C'*cos YAngle - A'*sin YAngle
    A'' = C'*sin YAngle + A'*cos YAngle
    B'' = B'   

Now perform the last rotation, about the X Axis.

    B''' = B''*cos XAngle - C''*sin XAngle
    C''' = B''*sin XAngle + C''*cos XAngle
    A''' = A''

Finally, add these values back to the original point of rotation.

Rotated Point = (X1+A''', Y1+B''', Z1+C''');

I found this link to be very helpful. It defines how to perform individual rotations about the X, Y and Z axes.

Mathematically, you can define the set of operations like so:

Circular rotation around an arbitrary axis


A very neat way to program this, especially if you are able to operate with matrices (like in Matlab) is the Rodrigues' Rotation Formula.

The formula creates a rotation matrix

Circular rotation around an arbitrary axis

around an axis defined by the unit vector

Circular rotation around an arbitrary axis

by an angle

Circular rotation around an arbitrary axis

using a very simple equation:

Circular rotation around an arbitrary axis

Where

Circular rotation around an arbitrary axis

is the identity matrix and

Circular rotation around an arbitrary axis

is a matrix given by the components of the unit vector

Circular rotation around an arbitrary axis

:

Circular rotation around an arbitrary axis

Note that it is very important that the vector

Circular rotation around an arbitrary axis

is a unit vector, i.e. the norm of

Circular rotation around an arbitrary axis

must be 1.

You can check that for the euclidean axis, the formula is exact as those found on Wikipedia and published here by Aakash Anuj.

I only use this formula for rotations since I discovered it. Hope it helps to anyone.


Here is what you can use to rotate about any axis, be it x,y or z. Rx, Ry and Rz denote rotation about the aces x,y,z respectively.

Circular rotation around an arbitrary axis


For rotations about an arbitrary axis in three dimensions with matrices, I have a page here. The linked explanation and derivation of the matrices (here) includes the following rotation/translation matrix. This is the matrix that yields the result of rotating the point (x,y,z) about the line through (a,b,c) with direction vector ⟨u,v,w⟩ by the angle theta.

Circular rotation around an arbitrary axis

The result is this point in three dimensions:

Circular rotation around an arbitrary axis

The page includes a link to a source code download. If you want to interactively do rotations, you can do so at this site. Try the sample rotation link to get a feel for what's going on.

Circular rotation around an arbitrary axis


Axis-angle converts directly to quaternion; given that the axis is a unit vector, and the angle is the spin around that axis. The axis is given regular (x,y,z) direction coordinates. The quaternion is (cos(theta),sin(theta)*x, sin(theta)*y, sin(theta)*z) then multiply as appropriate.

The basis can be formed by rotating (1,0,0),(0,1,0),(0,0,1) around the axis-angle using Rodrigues Rotation Formula which results in the following... and then the forward, right, and up vectors can be used to scale the point to the correct location. (This is just a matrix, but essentially transposed so that axii are immediately extractable themselves, giving you the relative 'up' at any particular point)

    const nt = q.θ * del;  // scaled angle by some dT
    const s  = Math.sin( nt ); // sin/cos are the function of exp()
    const c1 = Math.cos( nt );
    const c = 1- c1;

    const qx = /*Axis unit vector X*/;
    const qy = /*Axis unit vector Y*/;
    const qz = /*Axis unit vector Z*/;

    const cnx = c*qx;
    const cny = c*qy;
    const cnz = c*qz;

    const xy = cnx*qy;  // x * y / (xx+yy+zz) * (1 - cos(2t))
    const yz = cny*qz;  // y * z / (xx+yy+zz) * (1 - cos(2t))
    const xz = cnz*qx;  // x * z / (xx+yy+zz) * (1 - cos(2t))

    const wx = s*qx;     // x / sqrt(xx+yy+zz) * sin(2t)
    const wy = s*qy;     // y / sqrt(xx+yy+zz) * sin(2t)
    const wz = s*qz;     // z / sqrt(xx+yy+zz) * sin(2t)

    const xx = cnx*qx;  // y * y / (xx+yy+zz) * (1 - cos(2t))
    const yy = cny*qy;  // x * x / (xx+yy+zz) * (1 - cos(2t))
    const zz = cnz*qz;  // z * z / (xx+yy+zz) * (1 - cos(2t))

    const basis = { right  :{ x : c1 + xx, y : wz + xy, z : xz - wy }
                  , up     :{ x : xy - wz, y : c1 + yy, z : wx + yz }
                  , forward:{ x : wy + xz, y : yz - wx, z : c1 + zz }
                  };

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜