Search the Community

Showing results for tags 'Linear Algebra'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Categories

  • Audio
    • Music and Sound FX
  • Business
    • Business and Law
    • Career Development
    • Production and Management
  • Game Design
    • Game Design and Theory
    • Writing for Games
    • UX for Games
  • Industry
    • Interviews
    • Event Coverage
  • Programming
    • Artificial Intelligence
    • General and Gameplay Programming
    • Graphics and GPU Programming
    • Engines and Middleware
    • Math and Physics
    • Networking and Multiplayer
  • Visual Arts
  • Archive

Categories

  • News

Categories

  • Audio
  • Visual Arts
  • Programming
  • Writing

Categories

  • Audio Jobs
  • Business Jobs
  • Game Design Jobs
  • Programming Jobs
  • Visual Arts Jobs

Categories

  • GameDev Unboxed

Forums

  • Audio
    • Music and Sound FX
  • Business
    • Games Career Development
    • Production and Management
    • Games Business and Law
  • Game Design
    • Game Design and Theory
    • Writing for Games
  • Programming
    • Artificial Intelligence
    • Engines and Middleware
    • General and Gameplay Programming
    • Graphics and GPU Programming
    • Math and Physics
    • Networking and Multiplayer
  • Visual Arts
    • 2D and 3D Art
    • Critique and Feedback
  • Topical
    • Virtual and Augmented Reality
    • News
  • Community
    • GameDev Challenges
    • For Beginners
    • GDNet+ Member Forum
    • GDNet Lounge
    • GDNet Comments, Suggestions, and Ideas
    • Coding Horrors
    • Your Announcements
    • Hobby Project Classifieds
    • Indie Showcase
    • Article Writing
  • Affiliates
    • NeHe Productions
    • AngelCode
  • Workshops
    • C# Workshop
    • CPP Workshop
    • Freehand Drawing Workshop
    • Hands-On Interactive Game Development
    • SICP Workshop
    • XNA 4.0 Workshop
  • Archive
    • Topical
    • Affiliates
    • Contests
    • Technical

Calendars

  • Community Calendar
  • Games Industry Events
  • Game Jams

Blogs

There are no results to display.

There are no results to display.

Marker Groups

  • Members

Developers


Group


About Me


Website


Industry Role


Twitter


Github


Twitch


Steam

Found 10 results

  1. Sorry for making a new thread about this, but I have a specific question which I couldn't find an answer to in any of the other threads I've looked at. I've been trying to get the method shown here to work several days now and I've run out of things to try. I've more or less resorted to using the barebones example shown there (with some very minor modifications as it wouldn't run otherwise), but I still can't get it to work. Either I have misunderstood something completely, or there's a mistake somewhere. My shader code looks like this: Vertex shader: #version 330 core //Vertex shader //Half the size of the near plane {tan(fovy/2.0) * aspect, tan(fovy/2.0) } uniform vec2 halfSizeNearPlane; layout (location = 0) in vec3 clipPos; //UV for the depth buffer/screen access. //(0,0) in bottom left corner (1, 1) in top right corner layout (location = 1) in vec2 texCoord; out vec3 eyeDirection; out vec2 uv; void main() { uv = texCoord; eyeDirection = vec3((2.0 * halfSizeNearPlane * texCoord) - halfSizeNearPlane , -1.0); gl_Position = vec4(clipPos.xy, 0, 1); } Fragment shader: #version 330 core //Fragment shader layout (location = 0) out vec3 fragColor; in vec3 eyeDirection; in vec2 uv; uniform mat4 persMatrix; uniform vec2 depthrange; uniform sampler2D depth; vec4 CalcEyeFromWindow(in float windowZ, in vec3 eyeDirection, in vec2 depthrange) { float ndcZ = (2.0 * windowZ - depthrange.x - depthrange.y) / (depthrange.y - depthrange.x); float eyeZ = persMatrix[3][2] / ((persMatrix[2][3] * ndcZ) - persMatrix[2][2]); return vec4(eyeDirection * eyeZ, 1); } void main() { vec4 eyeSpace = CalcEyeFromWindow(texture(depth, uv).x, eyeDirection, depthrange); fragColor = eyeSpace.rbg; } Where my camera settings are: float fov = glm::radians(60.0f); float aspect = 800.0f / 600.0f; And my uniforms equal: uniform mat4 persMatrix = glm::perspective(fov, aspect, 0.1f, 100.0f) uniform vec2 halfSizeNearPlane = glm::vec2(glm::tan(fov/2.0) * aspect, glm::tan(fov/2.0)) uniform vec2 depthrange = glm::vec2(0.0f, 1.0f) uniform sampler2D depth is a GL_DEPTH24_STENCIL8 texture which has depth values from an earlier pass (if I linearize it and set fragColor = vec3(linearizedZ), it shows up like it should, so nothing seems wrong there). I can confirm that it's wrong because it doesn't give me similar results to what saving position in the G-buffer or reconstructing using inverse matrices does. Is there something obvious I'm missing? To me the logic seems sound, and from the description on the Khronos wiki I can't see where I go wrong. Thanks!
  2. Hi, there's a great tutorial on frustum culling, but impossible to compile because it uses old DirectX 11 types (Direct3DXPlane instead of XMVECTOR, etc). Can someone please help me update this one class - frustumClass - to the new DirectX11 types (XMMATRIX, XMVECTOR, etc)? http://www.rastertek.com/dx11tut16.html Furthermore, can anyone please explain how he gets the minimum Z distance from the projection matrix by dividing one element by another? He leaves no explanation for this math and it's extremely frustrating. // Calculate the minimum Z distance in the frustum. zMinimum = -projectionMatrix._43 / projectionMatrix._33; r = screenDepth / (screenDepth - zMinimum); projectionMatrix._33 = r; projectionMatrix._43 = -r * zMinimum; Also not sure how to use an XMVECTOR instead of the old Plane class that he uses. Confused as to where all the m12, m13, etc correspond to the elements of an XMVECTOR. I thought you're not supposed to even access the elements of an XMVECTOR directly! So incredibly frustrating. Please help, thanks.
  3. I am doing a little physics project with circle circle collisions for now, and have tried to do impulse resolution for collisions with 2 circles, using the following code. relativeVelocity = (other.doVerletVelocity()).subtract(self.doVerletVelocity()) normDirecVel = relativeVelocity.dotProduct(collisionNormal) restitution = -1 - min(self.restitution, other.restitution) numerator = normDirecVel * restitution impulseScalar = numerator / float(1 / self.mass) + float(1 / other.mass) selfVel = self.doVerletVelocity() otherVel = other.doVerletVelocity() impulse = collisionNormal.scalarMult(impulseScalar) selfDV = impulse.scalarMult(1 / self.mass) otherDV = impulse.scalarMult(1 / other.mass) newSelfVel = selfVel.subtract(selfDV) newOtherVel = otherVel.add(otherDV) self.oldPos = (self.center).subtract(newSelfVel.scalarMult(dt)) other.oldPos = (other.center).subtract(newOtherVel.scalarMult(dt)) The problem seems to be that whatever value I give to self.mass and other.mass, the output stays exactly the same, the values that I used are: center = Vector(0, 0) radius = 1 oldPos = Vector(0, 0) accel = Vector(0, 0) mass = 100 restitution = 0.001 center2 = Vector(0, 3.20) radius2 = 1 oldPos2 = Vector(0, 3.201) accel2 = Vector(0, -1) mass2 = 1 restitution2 = 1 the output was: 0.0 0.0 0.0 2.165000000000114 0.0 0.0 0.0 2.1360000000001174 0.0 0.0 0.0 2.1066000000001206 0.0 0.0 0.0 2.076800000000124 0.0 0.0 0.0 2.046600000000127 0.0 0.0 0.0 2.0160000000001306 0.0 0.0 0.0 1.985000000000134 CIRCLE INTERSECTION 0.0 -1.985000000000134 0.0 3.938600000000271 0.0 -3.970000000000268 0.0 5.891800000000408 0.0 -5.9550000000004015 0.0 7.844600000000544 0.0 -7.940000000000535 0.0 9.797000000000681 I changed the values for the masses to make them higher, bu the output still remained the same, if you could get to the bottom of this, it would be much appreciated.
  4. How do we rotate the camera around x axis 360 degrees, without having the strange effect as in my video below? Mine behaves exactly the same way spherical coordinates would, I'm using euler angles. Tried googling, but couldn't find a proper answer, guessing I don't know what exactly to google for, googled 'rotate 360 around x axis', got no proper answers. References: Code: https://pastebin.com/Hcshj3FQ The video shows the difference between blender and my rotation:
  5. Hi all, More than a decade ago, a problem came up on this forum for computing a fast transpose of a 3x3 matrix using SSE. The most sensible implementation stores the matrix internally as a 3x4 matrix (so, one row stores 4 elements, aligned in a vector). A version, which I believe to be the fastest currently known, was presented: I am pleased to report that I have been able to come up with a version which should be faster: inline void transpose(__m128& A, __m128& B, __m128& C) { //Input rows in __m128& A, B, and C. Output in same. __m128 T0 = _mm_unpacklo_ps(A,B); __m128 T1 = _mm_unpackhi_ps(A,B); A = _mm_movelh_ps(T0,C); B = _mm_shuffle_ps( T0,C, _MM_SHUFFLE(3,1,3,2) ); C = _mm_shuffle_ps( T1,C, _MM_SHUFFLE(3,2,1,0) ); } This should be 5 instructions instead of ajas95's 8 instructions. Of course, to get that level of performance with either version, you need to inline everything, or else you spend tons of time on moving floating point arguments to/from input registers. The other thing that is crucial is that the instruction set be VEX encoded. This allows generating instructions that take three arguments, like `vunpcklps`, instead of instructions like `unpcklps` that take only two. VEX is only available in AVX and higher (usually passing e.g. `-mavx` is sufficient to get the compiler to generate VEX instructions). -G
  6. I'm trying to implement OBB vs OBB SAT collision from Randy Gaul's article. I have been playing with it for days, but I can't figure it out why it doesn't work. I am storing all vertex positions of each box, and also each edge normal on two lists. Then I use them on the algorithm, I tried a lot of changes to make it work but no luck. This is my C++ code: QVector2D Entity::getSupport(QVector2D dir) { float bestProjection = -std::numeric_limits<float>::max(); QVector2D bestVertex; for(quint32 i = 0; i < verticesList.count(); ++i) { QVector2D v = verticesList.at(i); float projection = QVector2D::dotProduct(v,dir); if(projection > bestProjection) { bestVertex = v; bestProjection = projection; } } return bestVertex; } qreal Collision::FindAxisLeastPenetration(Entity *A, Entity *B ) { float bestDistance = -std::numeric_limits<float>::max(); for(quint32 k = 0; k < A->verticesList.count() ; ++k) { QVector2D n = A->normalList.at(k); QVector2D s = B->getSupport(-n); QVector2D v = A->verticesList.at(k); QVector2D r = s-v; qreal d = QVector2D::dotProduct(n,r); if(d > bestDistance) { bestDistance = d; } } return bestDistance; } if (coli->FindAxisLeastPenetration(player,player2) <0 && FindAxisLeastPenetration(player2,player) <0 ) { qDebug() << "Collision detected "; }
  7. Hi guys, I'm trying to build my 3x3 rotation matrix algorithm for the sake of thoroughly understand it as much as I can The code below is what I just wrote from memory in an attempt to explain it to myself and test what I understand and what I do not. I roughly start to get it (saw it last week for the first time and kind of crashed my soul, at the time) and if I decompose it in small single pieces as I did below, it actually works and my point is being rotated to the appropriate coordinates (or almost, since the y component ends up getting 0ed out, so I think I just need to add back the p_proj_to_rA Vector to the final resoult to have it right). This being said, I completely missed the point of building a rotation matrix out of it, even though the whole time I was convinced to be moving toward the goal I mean I've made my matrix class with all the relevant operations/methods, but I just don't know how to do the final step to convert what I have below into a rotation matrix...and I do not mean the fomula, which I have in the book and I can copypaste that and have it work if I want to, what I need is to actually see how to derive that final matrix formula from what I have below, I need to understand how it correlate in a way that if I where to reason about the final rotation matrix formula in the book I could say "of course, it's obvious". So everyone that deeply understand rotation matrix and how to end up with one(without just copy-pasting the formula from a book or using a function that just give you one) is welcome to take on the challenge of making it look obvious to me, so that I can finish my program and end up with the correct one for wathever Axis of rotation I choose Or just link me wathever online resource helped you in the past Thanks Vector p = { 1, 1, 1 };// point that needs to rotate Vector rA = { 0, 1, 0 }; // rotation Axis p.computeMagnitude(); // sqrt( x*x + y*y + z*z ) rA.computeMagnitude();// sqrt( x*x + y*y + z*z ) //projection of our point on the rotation axis, which corresponds to the dotProduct since rA is of lenght 1 // Formula 1 dotProduct(p,rA) = px*rAx + py*rAy + pz*rAz // Formula 2 dotProduct(p,rA) = |p| * |rA| * cos(angleBetween) // proj_rA(p) = dotProduct(p,rA) = |p| * 1 * cos(angleBetween) = |p| * cos(angleBetween) //Solve Formula 2 for angleBetween //|p| * cos(angleBetween) = dotProduct(p,rA) // cos(angleBetween) = (dotProduct(p,rA) / |p|) // angleBetween = acos( dotProduct(p,rA) / |p| ) // proj_rA(p) = |p| * cos(angleBetween) // proj_rA(p) = |p| * cos( acos( dotProduct(p,rA) / |p| ) ) //projection of p on rA: double proj_to_rA = p.magnitude * cos(Vector::angleBetween(p, rA)); //vector p projected on rA, which is basically a longer or shorter version of rA Vector p_proj_to_rA = rA * proj_to_rA; //subtracting from p the (projection of p on rA) zeroes out the dotProduct(p,rA) so the resulting vector must be orthogonal to rA Vector p_perp_to_rA1 = p - p_proj_to_rA; //the cross product between two vector is a vector orthogonal to both of them //Formula 1 crossProduct(A,B) = |A|*|B|*sin(angleBetween) //Formula 2 v1.y*v2.z - v1.z*v2.y, //X // v1.z*v2.x - v1.x*v2.z, //Y // v1.x*v2.y - v1.y*v2.x //Z Vector p_perp_to_rA2 = Vector::crossProduct(rA, p_perp_to_rA1); //since rA, p_perp_to_rA1 and p_perp_to_rA2 are all perpendicular to each other, //if we now only consider p_perp_to_rA1 and p_perp_to_rA2 they describe a 2d plane //perpendicular to rA, and we rotate stuff based on this plane //Now the desired point of that plane is the sum (VectorX on p_perp_to_rA1 + VectorY on p_perp_to_rA2) double desiredRotation = toRad(-30); double X = cos(desiredRotation); double Y = sin(desiredRotation); Vector finalPoint = Vector(p_perp_to_rA1 * X) + Vector(p_perp_to_rA2 * Y); cout << "x,y,z{ 1, 1, 1 } -30 degree on the x,y,z{ 0, 1, 0 } Axis = " << finalPoint << endl; return 0; output:
  8. Has anyone tried finding isometric bounds programatically? What I'm trying to achieve is to use the Cartesian width and height of the object to find it's isometric bounds, but I think that just doesn't work once the image gets complicated. The attachments should explain clearly what I'm trying to achieve (note: the bounds drawing isn't perfect).
  9. Hello,   I was wrote a math library for C, and saw a thread about this, I wanted to share this library, maybe helps who working with C like me and looking for 3D math library for C89/C99, it is really easy to use and really fast!   Repo: https://github.com/recp/cglm   All funcs are inlined but you can prefer to use pre-compiled version of all funcs For instance glm_mat4_mul is inilne and glmc_mat4_mul is pre-compiled   if you don't need pre-compiled then you dont need to build it.   Library uses SIMD/AVX if available but in the future neon maybe supported All functions are optimized and there are lot of convenient funcs.    Features are listed in repo's README so I think there is no need to put them here   Just wanted to share my stuff to help people who are looking new lib or lib for C
  10. Quaternion Powers

    Version 1.2, February, 2003 Updates 1.2 A minor correction with the formula of converting from Quat to Axis. The scale is missing a square root. Thanks to Shi for pointing that out. From version 1.0 - 1.1 The norm of a quaternion should be the square root of the q.q. The mistake was brought to my attention by several kind readers and upon checking the definition of the Euclidean properties for complex numbers, I realize the norm property [bquote] || u+v || [/bquote] is violated for the previous definition of the magnitude. The code in the samples are updated as well. Foreword To me, the term 'Quaternion' sounds out of this world, like some term from quantum theory about dark matter, having dark secret powers. If you, too, are enthralled by this dark power, this article will bring enlightenment (I hope). The article will show you how to do rotations using quaternions, and bring you closer to understanding quaternions (and their powers). If you do spot a mistake please email me at robin@cyberversion.com. Also if you intend to put this on your site, please send me a mail. I like to know where this ends up. Why use Quaternions? To answer the question, let's first discuss some common orientation implementations. Euler representation This is by far the simplest method to implement orientation. For each axis, there is a value specifying the rotation around the axis. Therefore, we have 3 variables [bquote] x, y, z [/bquote] that vary between 0 and 360 degrees (or 0 - 2pi). They are the roll, pitch, and yaw (or pitch, roll, and yaw - whatever) representation. Orientation is obtained by multiplying the 3 rotation matrices generated from the 3 angles together (in a specific order that you define). Note: The rotations are specified with respect to the global coordinate axis frame. This means the first rotation does not change the axis of rotation for the second and third rotations. This causes a situation known as gimbal lock, which I will discuss later. Angle Axis representation This implementation method is better than the Euler representation as it avoids the gimbal lock problem. The representation consists of a unit vector representing an arbitrary axis of rotation, and another variable (0 - 360) representing the rotation around the vector: [bquote] x, y, z [/bquote] Why are these representations bad? Gimbal Lock As rotations in the Euler representation are done with respect to the global axis, a rotation in one axis could 'override' a rotation in another, making you lose a degree of freedom. This is gimbal lock. Say, if the rotation in the Y axis rotates a vector (parallel to the x axis) so that the vector is parallel to the z axis. Then, any rotations in the z axis would have no effect on the vector. Later, I will show you an example of gimbal lock and how you can use quaternions to overcome it. Interpolation Problems Though the angle axis representation does not suffer from gimbal lock, there are problems when you need to interpolate between two rotations. The calculated interpolated orientations may not be smooth, so you will get jerky rotation movements. Euler representation suffers from this problem as well. Let's get started Before we begin, let's establish some assumptions I'll be making. I hate the way many articles leave this important section out, causing a great deal of confusion when it comes to the mathematics. Coordinate System - This article assumes a right hand coordinate system, like OpenGL. If you are using a left handed coordinate system like Direct3D, you may need to transpose the matrices. Note that the Direct3D samples have a quaternion library already, though I recommend you check through their implementation before using it. Rotation Order - The sequence of rotations in the Euler representation is X, then Y, and then Z. In matrix form: [bquote] RotX * RotY * RotZ Very Important [/bquote] Matrix - Matrices are in column major format, like they are in OpenGL. [bquote] Example[nbsp][[nbsp]0[nbsp][nbsp]4[nbsp][nbsp]8[nbsp][nbsp]12 [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]1[nbsp][nbsp]5[nbsp][nbsp]9[nbsp][nbsp]13 [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2[nbsp][nbsp]6[nbsp][nbsp]10[nbsp]14 [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]3[nbsp][nbsp]7[nbsp][nbsp]11[nbsp]15[nbsp]] [/bquote] Vectors and Points - Implemented as a 4x1 matrix so applying a transformation is of the order [bquote] Rotation[nbsp]Matrix*[nbsp][nbsp][[nbsp]vx [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]vy [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]vz [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]1[nbsp][nbsp]][nbsp][nbsp][/bquote] This does not imply that I prefer OpenGL over Direct3D. It just happened that I learned OpenGL first, and so my quaternion knowledge was gained in OpenGL. Note: If you specify rotations in another order, certain quaternion functions will be implemented differently, especially those that deal with Euler representation. What is a Quaternion? A complex number is an imaginary number that is defined in terms of i, the imaginary number, which is defined such that i * i = -1. A quaternion is an extension of the complex number. Instead of just i, we have three numbers that are all square roots of -1, denoted by i, j, and k. This means that [bquote]j * j = -1 k * k = -1[/bquote] So a quaternion can be represented as [bquote]q = w + xi + yj + zk[/bquote] where w is a real number, and x, y, and z are complex numbers. Another common representation is [bquote]q=[ w,v ][/bquote] where v = (x, y, z) is called a "vector" and w is called a "scalar". Although the v is called a vector, don't think of it as a typical 3 dimensional vector. It is a vector in 4D space, which is totally unintuitive to visualize. Identity Quaternions Unlike vectors, there are two identity quaternions. The multiplication identity quaternion is [bquote]q= [1,(0, 0, 0)][/bquote] So any quaternion multiplied with this identity quaternion will not be changed. The addition identity quaternion (which we do not use) is [bquote]q= [0,(0, 0, 0)][/bquote] Using quaternions as orientations The first thing I want to point out is that quaternions are not vectors, so please don't use your preconceived vector mathematics on what I'm going to show. This is going to get very mathematical, so please bear with me. We need to first define the magnitude of a quaternion. [bquote]|| q ||= Norm(q) =sqrt(w2 + x2 + y2 + z2)[/bquote] A unit quaternion has the following property [bquote]w2 + x2 + y2 + z2=1[/bquote] So to normalize a quaternion q, we do [bquote]q = q / || q || = q / sqrt(w2 + x2 + y2 + z2)[/bquote] What is so special about this unit quaternion is that it represents an orientation in 3D space. So you can use a unit quaternion to represent an orientation instead of the two methods discussed previously. To use them as orientations, you will need methods to convert them to other representations (e.g. matrices) and back, which will be discussed soon. Visualizing a unit quaternion You can visualize unit quaternions as a rotation in 4D space where the (x,y,z) components form the arbitrary axis and the w forms the angle of rotation. All the unit quaternions form a sphere of unit length in the 4D space. Again, this is not very intuitive but what I'm getting at is that you can get a 180 degree rotation of a quaternion by simply inverting the scalar (w) component. Note: Only unit quaternions can be used for representing orientations. All discussions from here on will assume unit quaternions. Conversion from Quaternions To be able to use quaternions effectively, you will eventually need to convert them to some other representation. You cannot interpret keyboard presses as quaternions, can you? Well, not yet. Quaternion to Matrix Since OpenGL and Direct3D allow rotations to be specified as matrices, this is probably the most important conversion function, since homogenous matrices are the standard 3D representations. The equivalent rotation matrix representing a quaternion is [bquote] Matrix[nbsp]=[nbsp][nbsp][[nbsp]w2[nbsp]+[nbsp]x2[nbsp]-[nbsp]y2[nbsp]-[nbsp]z2[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xy[nbsp]-[nbsp]2wz[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]+[nbsp]2wy [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xy[nbsp]+[nbsp]2wz[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]w2[nbsp]-[nbsp]x2[nbsp]+[nbsp]y2[nbsp]-[nbsp]z2[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2yz[nbsp]-[nbsp]2wx [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]-[nbsp]2wy[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2yz[nbsp]+[nbsp]2wx[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]w2[nbsp]-[nbsp]x2[nbsp]-[nbsp]y2[nbsp]+[nbsp]z2[nbsp]] [/bquote] Using the property of unit quaternions that w2 + x2 + y2 + z2 = 1, we can reduce the matrix to [bquote] Matrix[nbsp]=[nbsp][nbsp][[nbsp]1[nbsp]-[nbsp]2y2[nbsp]-[nbsp]2z2[nbsp][nbsp][nbsp][nbsp]2xy[nbsp]-[nbsp]2wz[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]+[nbsp]2wy [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xy[nbsp]+[nbsp]2wz[nbsp][nbsp][nbsp][nbsp]1[nbsp]-[nbsp]2x2[nbsp]-[nbsp]2z2[nbsp][nbsp][nbsp][nbsp]2yz[nbsp]-[nbsp]2wx [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]-[nbsp]2wy[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2yz[nbsp]+[nbsp]2wx[nbsp][nbsp][nbsp][nbsp]1[nbsp]-[nbsp]2x2[nbsp]-[nbsp]2y2[nbsp]] [/bquote] Quaternion to Axis Angle To change a quaternion to a rotation around an arbitrary axis in 3D space, we do the following: [bquote] If[nbsp]the[nbsp]axis[nbsp]of[nbsp]rotation[nbsp]is[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp](ax,[nbsp]ay,[nbsp]az) and[nbsp]the[nbsp]angle[nbsp]is[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]theta[nbsp](radians) then[nbsp]the[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]angle=[nbsp]2[nbsp]*[nbsp]acos(w) [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]ax=[nbsp]x[nbsp]/[nbsp]scale [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]ay=[nbsp]y[nbsp]/[nbsp]scale [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]az=[nbsp]z[nbsp]/[nbsp]scale where[nbsp]scale[nbsp]=[nbsp]sqrt[nbsp](x2[nbsp]+[nbsp]y2[nbsp]+[nbsp]z2) [/bquote] Another variation I have seen is that the scale = sin(acos(w)). They may be equivalent, though I didn't try to find the mathematical relationship behind them. Anyway if the scale is 0, it means there is no rotation so unless you do something, the axis will be infinite. So whenever the scale is 0, just set the rotation axis to any unit vector with a rotation angle of 0. A Simple Example In case you are getting confused with what I'm getting at, I will show you a simple example here. Say the camera orientation is represented as Euler angles. Then, in the rendering loop, we position the camera using [bquote] RotateX * RotateY * RotateZ * Translate [/bquote] where each component is a 4x4 matrix. So if we are using a unit quaternion to represent the camera orientation, we have to convert the quaternion to a matrix first [bquote] Rotate (from Quaternion) * Translate [/bquote] A more specific example in OpenGL: Euler Quaternion glRotatef( angleX, 1, 0, 0) glRotatef( angleY, 0, 1, 0) glRotatef( angleZ, 0, 0, 1) // translate // convert Euler to quaternion // convert quaternion to axis angle glRotate(theta, ax, ay, az) // translate The above implementations are equivalent. The point I'm trying to get across is that using quaternions for orientation is the same as using Euler or Axis angle representation and that they can be interchanged through the conversion functions I've described. Note that the above quaternion representation will also incur gimbal lock like the Euler method. Of course, you do not know how to make the rotation to be a quaternion in the first place but we will get to that shortly. Note: If you are using Direct3D or OpenGL, you may not have to deal with matrices directly, but matrix concatenation is something that the API does, so it's worth learning about them. Multiplying Quaternions Since a unit quaternion represents an orientation in 3D space, the multiplication of two unit quaternions will result in another unit quaternion that represents the combined rotation. Amazing, but it's true. Given two unit quaternions [bquote]Q1=(w1, x1, y1, z1); Q2=(w2, x2, y2, z2);[/bquote] A combined rotation of unit two quaternions is achieved by [bquote]Q1 * Q2 =( w1.w2 - v1.v2, w1.v2 + w2.v1 + v1*v2)[/bquote] where [bquote]v1= (x1, y1, z1) v2 = (x2, y2, z2)[/bquote] and both . and * are the standard vector dot and cross product. However an optimization can be made by rearranging the terms to produce [bquote]w=w1w2 - x1x2 - y1y2 - z1z2 x = w1x2 + x1w2 + y1z2 - z1y2 y = w1y2 + y1w2 + z1x2 - x1z2 z = w1z2 + z1w2 + x1y2 - y1x2[/bquote] Of course, the resultant unit quaternion can be converted to other representations just like the two original unit quaternions. This is the real beauty of quaternions - the multiplication of two unit quaternions in 4D space solves gimbal lock because the unit quaternions lie on a sphere. Be aware that the order of multiplication is important. Quaternion multiplication is not commutative, meaning [bquote]q1 * q2 does not equal q2 * q1[/bquote] Note: Both quaternions must refer to the same coordinate axis. I made the mistake of combining two quaternions from different coordinate axes, and I had a very hard time wondering why the result quaternion fails in certain angles only. Conversion To Quaternions Now we learn how to convert other representations to quaternions. Although I do not use all the conversions in the sample program, there are times when you'll need them when you want to use quaternion orientation for more advanced stuff like inverse kinematics. Axis Angle to Quaternion A rotation around an arbitrary axis in 3D space can be converted to a quaternion as follows [bquote] If[nbsp]the[nbsp]axis[nbsp]of[nbsp]rotation[nbsp]is[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp](ax,[nbsp]ay,[nbsp]az)-[nbsp]must[nbsp]be[nbsp]a[nbsp]unit[nbsp]vector and[nbsp]the[nbsp]angle[nbsp]is[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]theta[nbsp](radians) [nbsp][nbsp][nbsp][nbsp]w[nbsp][nbsp][nbsp]=[nbsp][nbsp][nbsp]cos(theta/2) [nbsp][nbsp][nbsp][nbsp]x[nbsp][nbsp][nbsp]=[nbsp][nbsp][nbsp]ax[nbsp]*[nbsp]sin(theta/2) [nbsp][nbsp][nbsp][nbsp]y[nbsp][nbsp][nbsp]=[nbsp][nbsp][nbsp]ay[nbsp]*[nbsp]sin(theta/2) [nbsp][nbsp][nbsp][nbsp]z[nbsp][nbsp][nbsp]=[nbsp][nbsp][nbsp]az[nbsp]*[nbsp]sin(theta/2) [/bquote] The axis must first be normalized. If the axis is a zero vector (meaning there is no rotation), the quaternion should be set to the rotation identity quaternion. Euler to Quaternion Converting from Euler angles to a quaternion is slightly more tricky, as the order of operations must be correct. Since you can convert the Euler angles to three independent quaternions by setting the arbitrary axis to the coordinate axes, you can then multiply the three quaternions together to obtain the final quaternion. So if you have three Euler angles (a, b, c), then you can form three independent quaternions [bquote]Qx = [ cos(a/2), (sin(a/2), 0, 0)] Qy = [ cos(b/2), (0, sin(b/2), 0)] Qz = [ cos(c/2), (0, 0, sin(c/2))][/bquote] And the final quaternion is obtained by Qx * Qy * Qz. Demo - Avoiding Gimbal Lock Finally, we've reached what you all been waiting for: "How can quaternions avoid gimbal lock.?" The basic idea is Use a quaternion to represent the rotation. Generate a temporary quaternion for the change from the current orientation to the new orientation. PostMultiply the temp quaternion with the original quaternion. This results in a new orientation that combines both rotations. Convert the quaternion to a matrix and use matrix multiplication as normal. Firstly, I want to make a disclaimer regarding the sample code. The code is ugly and very poorly organized. But do remember, this is just a cut down version of my program when I was testing quaternions, and I'm not getting paid for this. There are two executable samples that I have included. The first program, CameraEuler.exe, is an example for camera implementation using Euler angles. The main concern should be the Main_Loop function in main.cpp. The main thing you should take note (in the while loop) is There are 3 angles I keep track of for rotation in the X, Y, and Z axis. With every key press, I adjust the corresponding rotation variable. In the while loop, I translate and then just convert the 3 Euler angles to rotation matrices and multiply them into the final transformation matrix. Use the up/down keys to rotate around the X axis, left/right to rotate around the Y axis and Insert/PageUp to rotate around the Z axis. This program suffers from gimbal lock. If you want to see it in action, rotate the camera so that the yaw is 90 deg. Then try rotating in the X and Z direction. See what happens. Now for the quaternion solution. The program is CameraQuat.exe and it is a slight modification of the previous program. The main point you should take note (in the while loop) is The orientation of the camera is a quaternion. There are 3 angles corresponding to the keypress. Note the angles are meant to be an on/off switch (not accumulative). I reset them inside the while loop. Of course this is not the best way to do it but as I said, it is a quick job. I convert the 3 angles to a temporary quaternion. I multiply the temporary quaternion to the camera quaternion to obtain the combined orientation. Note the order of multiplication. The camera rotation is then converted to the Axis Angle representation for transforming the final matrix. When a key is pressed, I generate a temporary quaternion corresponding to the key for a small rotation in that particular axis. I then multiply the temporary quaternion into the camera quaternion. This concatenation of rotations in 4D space will avoid gimbal lock. Try it and see for yourself. The camera quaternion has to be changed into either a matrix form or equivalence form so that you can concatenate it into a final transformation matrix. You have to do this for every quaternion you use, as 4D space and 3D space just don't mix. In the case of OpenGL, I just changed the quaternion to an Axis Angle representation and let the API do the rest. Although I did not use the global Euler angles for rotations in the second program, I have left them there as a guide for you to see the similar Euler rotations in the first program. Note the Euler angles will be incorrect if you rotate more than 1 axis (because it counts the keypress rather than getting the Euler angles from the camera quaternion). It is just a reference for you to see that when you rotate the yaw to 90 deg when the program starts, the gimbal lock problem is no more. Note: I don't recommend you use my math library as it is. Understand the quaternion and write your own. For your information, I am going to throw all of that away and rewrite it too. It is just too messy and ugly for my taste. What I did not show If you notice, I did not show how to convert from a Quaternion to the Euler angle. That's because I have yet to find a conversion that works perfectly. The only way I know is to obtain a matrix from the quaternion and try to extract the Euler angles from the matrix. However, as Euler to matrix conversions are a many-to-one relationship (due to sin and cos), I do not know how to get the reverse even using atan2. If anyone knows how to extract Euler angles from a matrix accurately, please do share with me. The other thing I did not show is the conversion of a matrix to a quaternion. I didn't think I needed this conversion as you can convert the Euler and Axis angle representation to quaternion straight without needing to throw them to a matrix. More you can do - SLERP If you think you are a quaternion master, think again. There is still more to learn about them. Remember I said something about why the Axis Angle representation is bad? Does the word 'interpolation' ring a bell? I don't have the time to write about interpolations using quaternions. This article has taken much longer than I had anticipated. I can give you the basic idea about SLERP (Spherical Linear Interpolation), which is basically generating a series of quaternions between two quaternion orientations (which you specify). The series of quaternions will result in smooth motion between the first and end quaternion (something which both the Euler and Axis Angle representation cannot achieve consistently). Final Words I hope this article can clear up any mystery behind the quaternion theory. A final word of caution again: Don't multiply two quaternions from different coordinate frames. Nothing but pain and hair loss will result from it. With your new found powers, I bid thee farewell. Take care, .. and watch your back.