下面的网页谈到了使用半角问题,但最后也表示无把握:
http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/notations/vectorDivision/index.htm 我也贴在这里: Maths - Quaternion Notations - Vector Division I'm not sure that this is a very practical or useful way to define quaternions? However in might be interesting to investigate. p2=q * p1 * conj(q) Why can't we have a simple transform, similar to matrix transforms, where a point is transformed just by multiplying by the quaternion: p2=q * p1 where: p2 = is a vector representing a point after being rotated q = is a quaternion representing a rotation. p1= is a vector representing a point before being rotated This almost works, but not quite, so it might be instructive to try and work out the reasons. If we rearrange the equation we get: q = p1 / p2 The division of two vectors does not have a solution, as explained here, by the usual types of multiplication used with vectors (cross multiplication). However, using quaternions, we can define a way to do this division. We can define a vector using quaternions by setting the real part to zero, and the imaginary pars to the vector coordinates: p1 = 0 + a i + b j + c k p2 = 0 + e i + f j + g k Dividing these vectors gives: q = (a*e + b*f +c*g) + (g*b - f*c) i + (e*c - a*g) j + (f*a - e*b) k So we get a quaternion where the real part is the dot product and the complex parts are the cross product of the vectors. This quaternion can be used to represent the rotation of one vector to another, see angle between vectors. Can this quaternion, used in this way, be combined and used to represent subsequent rotations? It turns out that this will only work if the axies of both rotations are aligned. So this can't be used to concatenate general rotations, to do that we must use: p2=q * p1 * conj(q) where q is defined in terms of angle/2 in the normal way. It seems like we have to do half the rotation by pre-multiplying by q and then the other half of the rotation by post-multiplying by conj(q). Its almost like doing the rotation in two parts like this cancels out the errors? Is this true? |
对我有用[0] 丢个板砖[0] 引用 | 举报 |
管理
|
#3 得分:50
回复于: 2011-08-10 08:17:21
这样的问题多半是出于使用过程中积累的经验,有时候你偏要问个问什么还不好回答。
|
|
对我有用[0] 丢个板砖[0] 引用 | 举报 |
管理
|
#4 得分:0
回复于: 2011-08-10 11:06:38
下面文章介绍了Gimbal Lock 出现的时刻,但好像也没有讲清这一时刻为什么会产生困难?也没有提到半角问题:
What is Gimbal Lock and why does it occur? 见 http://www.anticz.com/eularqua.htm What is gimbal lock? Gimbal lock is the phenomenon of two rotational axis of an object pointing in the same direction. Simply put, it means your object won't rotate how you think it ought to rotate. Gimbal lock is frustrating problem that every CG artist will face sometime in their career and it always happens at the worst possible time. Gimbal lock occurs when animating an object with a rotational matrix known as Eular (pronounced Oiler) angles. It's a general limitation of that type of rotational matrix. Any system that uses Eular angles (Maya, Max, Lightwave, Softimage) will have problems with gimbal lock. The reason for this is that Eular angles evaluate each axis independently in a set order. In the case of 3DS Max that order is generally X,Y,Z (you can change the order to whatever you'd like however) meaning ... first the object travels down the X axis. When that operation is complete it then travels down the Y axis, and finally the Z axis. The problem with gimbal lock occurs when you rotate the object down the Y axis, say 90 degrees. Since the X component has already been evaluated it doesn't get carried along with the other two axis. What winds up happening is the X and Z axis get pointed down the same axis. |
|
对我有用[0] 丢个板砖[0] 引用 | 举报 |
管理
|
#5 得分:0
回复于: 2011-08-12 03:10:07
有一外国佬说他已看过1000多个网页,还是没法弄清,结果自己写了一篇,
但我看了后觉得:步骤确实讲清楚了,而为什么要这样的问题仍未讲清楚! |
|
对我有用[0] 丢个板砖[0] 引用 | 举报 |
管理
|
#6 得分:0
回复于: 2011-08-12 07:33:34
A Simple Quaternion-based Camera I looked through a thousand web pages for a good quaternion camera tutorial, and I could not find one. There is a good example on gametutorials.com that uses quaternions, but it uses them to rotate general objects and makes things a little more difficult than they actually need to be to use quaternions to make a camera. Therefore, in this article I am going to present a simple quaternion-based camera that can be used to rotate the view of your camera using the mouse. So, what is a Quaternion? Quaternions aren't actually as scary as they sound. Everything I read regarding quaternions talked about imaginary numbers, hyper-complex numbers, spinors, and other scary sounding things. There was too much maths jargon for my liking. If you understand what a vector is, it isn't very hard to understand what a quaternion is. One way to represent a quaternion is Q = xi + yj + zk + w where i,j & k are coordinate basis vectors for three dimensions. I don't particularly like this representation for computer graphics, especially where cameras are concerned. I prefer thinking of a quaternion as an object that contains a vector and a scalar. We'll call the vector v and keep the scalar as w. Q = [ w, v ] where v = xi + yj + zk. For use in the sample code below, here's a quaternion data structure: struct quaternion { double x, y, z, w; }; This is a much easier representation to comprehend for me. Now for our purposes, quaternion addition, subtraction, etc., aren't needed. We only need to know how to normalize (scale to length=1), multiply and compute the conjugate of a quaternion in order to generate a rotation. These tasks are actually very simple and are described below. Normalizing a quaternion Normalizing a quaternion is almost the same as normalizing a vector. We start by computing the length of the quaternion to be normalized, which is done using the Euclidean distance forumula: |Q| = sqrt( w^2 + x^2 + y^2 + z^2) A function to implement this formula might look like this: double length(quaternion quat) { return sqrt(quat.x * quat.x + quat.y * quat.y + quat.z * quat.z + quat.w * quat.w); } Not too hard is it? Now to get the normalized vector Q, which we'll call Q*, we just divide every element of Q by |Q|. Q* = Q/|Q| = [w/|Q|, v/|Q|] Here's some sample code for this function: quaternion normalize(quaternion quat) { double L = length(quat); quat.x /= L; quat.y /= L; quat.z /= L; quat.w /= L; return quat; } This will give us a quaternion of length 1 (which is very important for rotations). Still nothing scary right? Well, it doesn't really get much harder than this. The conjugate of a quaternion Let's compute the conjugate of a quaternion and call it Q'. The conjugate is simply Q' = [ w, -v ] Here's the code for the conjugate: quaternion conjugate(quaternion quat) { quat.x = -quat.x; quat.y = -quat.y; quat.z = -quat.z; return quat; } The last thing you need to know is quaternion multiplication. Then you'll be ready to make a quaternion based camera. Multiplying quaternions Multiplication with quaternions is a little complicated as it involves dot-products cross-products. However, if you just use the following forumula that expands these operations, it isn't too hard. To multiply quaternion A by quaternion B, just do the following: C = A * B such that: C.x = | A.w*B.x + A.x*B.w + A.y*B.z - A.z*B.y | C.y = | A.w*B.y - A.x*B.z + A.y*B.w + A.z*B.x | C.z = | A.w*B.z + A.x*B.y - A.y*B.x + A.z*B.w | C.w = | A.w*B.w - A.x*B.x - A.y*B.y - A.z*B.z | Here's some code for multiplication: quaternion mult(quaternion A, quaternion B) { quaternion C; C.x = A.w*B.x + A.x*B.w + A.y*B.z - A.z*B.y; C.y = A.w*B.y - A.x*B.z + A.y*B.w + A.z*B.x; C.z = A.w*B.z + A.x*B.y - A.y*B.x + A.z*B.w; C.w = A.w*B.w - A.x*B.x - A.y*B.y - A.z*B.z; return C; } That's not too that hard is it? Now we'll look at how to use these operations to make a quaternion based camera. The quaternion camera To make a camera you typically use three vectors: Position, View, and Up (or you may call them what you like). For a first person camera - which we will be using - we're only going to consider rotating the View vector. With quaternions we can rotate a vector around an arbitrary axis (same as with axis-angles) very easily. To achieve this, first we need to turn our View vector into a quaternion, then define a rotation quaternion and lastly, apply the rotation quaternion to the View quaternion to make the rotation. To make the View quaternion, V, the x, y, and z values are taken from the View vector and we simply add a 0 for the scalar component w. Thus, V = [0, View] Then you need to make a quaternion to represent the rotation. To do this, you need the vector you want to rotate about, and the angle you wish to rotate by. We'll just simply term the vector to rotate about A, and the angle theta. Here is the formula to build your rotation quaternion, which we'll call R. vector A = [x, y, z] R.x = A.x * sin(theta/2) R.y = A.y * sin(theta/2) R.z = A.z * sin(theta/2) R.w = cos(theta/2) So now we have the vector (View) and its quaternion V that we want to rotate by an angle theta about the vector A. The rotation quaternion R defines this rotation. After the rotation, we'll have the new quaternion representing our view, given by W. The rotation operation is simply W = R * V * R' where R' is the conjugate of R. To get our new view vector, we just take the vector components out of W. NewView = [W.x W.y W.z] The following function (using SDL, use glut or whatever you like) sets the view based on the distance from the current mouse coordinates to the centre of the screen. I learned how to do this from gametutorials.com, and modified the code for my purposes. In this code, positive x is to the right and positive y is down the screen. void Camera::SetViewByMouse(void) { // the coordinates of our mouse coordinates int MouseX, MouseY; // the middle of the screen in the x direction int MiddleX = SCREENWIDTH/2; // the middle of the screen in the y direction int MiddleY = SCREENHEIGHT/2; // vector that describes mouseposition - center Vector MouseDirection(0, 0, 0); // static variable to store the rotation about the x-axis, since // we want to limit how far up or down we can look. // We don't need to cap the rotation about the y-axis as we // want to be able to turn around 360 degrees static double CurrentRotationAboutX = 0.0; // The maximum angle we can look up or down, in radians double maxAngle = 1; // This function gets the position of the mouse SDL_GetMouseState(&MouseX, &MouseY); // if the mouse hasn't moved, return without doing // anything to our view if((MouseX == MiddleX) && (MouseY == MiddleY)) return; // otherwise move the mouse back to the middle of the screen SDL_WarpMouse(MiddleX, MiddleY); // get the distance and direction the mouse moved in x (in // pixels). We can't use the actual number of pixels in radians, // as only six pixels would cause a full 360 degree rotation. // So we use a mousesensitivity variable that can be changed to // vary how many radians we want to turn in the x-direction for // a given mouse movement distance // We have to remember that positive rotation is counter-clockwise. // Moving the mouse down is a negative rotation about the x axis // Moving the mouse right is a negative rotation about the y axis MouseDirection.x = (MiddleX - MouseX)/MouseSensitivity; MouseDirection.y = (MiddleY - MouseY)/MouseSensitivity; CurrentRotationX += MouseDirection.y; // We don't want to rotate up more than one radian, so we cap it. if(CurrentRotationX > 1) { CurrentRotationX = 1; return; } // We don't want to rotate down more than one radian, so we cap it. if(CurrentRotationX < -1) { CurrentRotationX = -1; return; } else { // get the axis to rotate around the x-axis. Vector Axis = CrossProduct(View - Position, Up); // To be able to use the quaternion conjugate, the axis to // rotate around must be normalized. Axis = Normalize(Axis); // Rotate around the y axis RotateCamera(MouseDirection.y, Axis.x, Axis.y, Axis.z); // Rotate around the x axis RotateCamera(MouseDirection.x, 0, 1, 0); } } This function actually rotates our view. After we are done, just plug your camera vectors (Position, View, and Up) into gluLookAt(Position.x, Position.y, Position.z, View.x, View.y, View.z, Up.x, Up.y, Up.z). Here is the code for the rotation. void RotateCamera(double Angle, double x, double y, double z) { quaternion temp, quat_view, result; temp.x = x * sin(Angle/2); temp.y = y * sin(Angle/2); temp.z = z * sin(Angle/2); temp.w = cos(Angle/2); quat_view.x = View.x; quat_view.y = View.y; quat_view.z = View.z; quat_view.w = 0; result = mult(mult(temp, quat_view), conjugate(temp)); View.x = result.x; View.y = result.y; View.z = result.z; } Again, at the end of the above functions, you should call gluLookAt(Position.x, Position.y, Position.z, View.x, View.y, View.z, Up.x, Up.y, Up.z). and your camera should work just perfectly. Maybe some other time, I'll do a third person camera tutorial, and explain how to use SLERP. |