Halaman

Selasa, 27 Maret 2012

3d: Rotations, Axes, Local Space, Parent Space, World Space

The biggest factor i need to overcome with 3d programming was the math-side of 3d programming, there are many terms, such as vector, matrix, gimbals, local space, world space, projections, quaternions, euclidean, eulers etc.. We need to understand those terms so that i could position my objects correctly

If you are like me, you will be thrilled to read this page : opengl transforms, great article, made me understand the concept of local space and the world space

If you work with 3dsmax, blender, autocad, lightwave, etc you will likely works with axes, you use this axes to translate, rotate, scale your models, even per-element base(vertices, faces, polygons, edges).

There is a concept which helps me a lot to program transformation, and that is the local axes, the forward, up, and left axes which is locally for our object. If you put your matrix data using OpenGL conventions (column major) and you save your transformation to your object, then the elements :

  • local-x (default 1,0,0) would be (m0, m1, m2) 
  • local-y (default 0,1,0) would be (m4, m5, m6)
  • local-z (default 0,0,1) would be (m8, m9, m10)
  • and your object translation would be (m12, m13, m14)

But remember this matrix represents your modelview (that is your model matrix * view matrix)
okayh, now, what is this model matrix, and view matrix?
model matrix represents your model world transformations, and your view matrix is the matrix which translate your model to eyespace (viewers space). Picture this, your object resides at 0, 0, 0 of your computer screen, and your computer screen resides at 0, 0, -1 of your eye. Ok to simplify things up, let's neglect that view matrix, and put our transformation(rotation, scaling, translating) as our MODEL_VIEW matrix ;)

okayh, now although we had this coordinates defined, we need one more thing, and that is the projection matrix, this projection matrix represents the objects on your screen to your eyes, this matrix would be your perspective matrix, or your orthogonal matrix..

now for your grand finale formula to represent a vertex location to your viewer is :
Pos(x, y,z) = M(projection) * M(ModelView) * OriginalPos(x, y, z)
Hmm.. i'm so sorry for the notation there.. :D hope you understand by practice.. ehmm.. OpenGL Es shader language should be a perfect ground for playing things..

but, to get the normal, we need different equations :
Norm(x, y, z) = M(projection) * M(ModelView).inverse().transpose() * NormOriginalPos(x, y, z)
if you would, you could take a look at the formula derivation by Songho.

now here come the part which we determine our local axes (forward, right, and up vectors) by using the current rotation data, and by using the forward vector only
  1. Determine by using the rotation data :
    struct Vector3{
     float x;
     float y;
     float z;
     Vector3() : x(0), y(0), z(0) {}; // initialze when created
    };
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // convert Euler angles(x,y,z) to axes(left, up, forward)
    // Each column of the rotation matrix represents left, up and forward axis.
    // The order of rotation is Roll->Yaw->Pitch (Rx*Ry*Rz)
    // Rx: rotation about X-axis, pitch
    // Ry: rotation about Y-axis, yaw(heading)
    // Rz: rotation about Z-axis, roll
    // Rx Ry Rz
    // |1 0 0| | Cy 0 Sy| |Cz -Sz 0| | CyCz -CySz Sy |
    // |0 Cx -Sx|*| 0 1 0|*|Sz Cz 0| = | SxSyCz+CxSz -SxSySz+CxCz -SxCy|
    // |0 Sx Cx| |-Sy 0 Cy| | 0 0 1| |-CxSyCz+SxSz CxSySz+SxCz CxCy|
    ///////////////////////////////////////////////////////////////////////////////
    void anglesToAxes(const Vector3 angles, Vector3& left, Vector3& up, Vector3& forward)
    {
     const float DEG2RAD = 3.141593f / 180;
     float sx, sy, sz, cx, cy, cz, theta;
    
     // rotation angle about X-axis (pitch)
     theta = angles.x * DEG2RAD;
     sx = sinf(theta);
     cx = cosf(theta);
    
     // rotation angle about Y-axis (yaw)
     theta = angles.y * DEG2RAD;
     sy = sinf(theta);
     cy = cosf(theta);
    
     // rotation angle about Z-axis (roll)
     theta = angles.z * DEG2RAD;
     sz = sinf(theta);
     cz = cosf(theta);
    
     // determine left axis
     left.x = cy*cz;
     left.y = sx*sy*cz + cx*sz;
     left.z = -cx*sy*cz + sx*sz;
    
     // determine up axis
     up.x = -cy*sz;
     up.y = -sx*sy*sz + cx*cz;
     up.z = cx*sy*sz + sx*cz;
    
     // determine forward axis
     forward.x = sy;
     forward.y = -sx*cy;
     forward.z = cx*cy;
  2. Determine by using the Up vector and the Forward Vector (this would apply to cameras.. i guess)
    // minimal implementation of Vector3 struct
    struct Vector3
    {
        float x;
        float y;
        float z;
    
        Vector3() : x(0), y(0), z(0) {};              // constructors
        Vector3(float x, float y, float z) : x(x), y(y), z(z) {};
    
        // functions
        Vector3& normalize();                         //
        Vector3  operator-(const Vector3& rhs) const; // subtract rhs
        Vector3  operator*(const Vector3& rhs) const; // cross product
        Vector3& operator*=(const float scale);       // scale and update itself
    };
    
    Vector3& Vector3::normalize() {
        float invLength = 1 / sqrtf(x*x + y*y + z*z);
        x *= invLength;
        y *= invLength;
        z *= invLength;
        return *this;
    }
    
    Vector3 Vector3::operator-(const Vector3& rhs) const {
        return Vector3(x-rhs.x, y-rhs.y, z-rhs.z);
    }
    
    Vector3 Vector3::cross(const Vector3& rhs) const {
        return Vector3(y*rhs.z - z*rhs.y, z*rhs.x - x*rhs.z, x*rhs.y - y*rhs.x);
    }
    ///////////////////////////////////////////////////////////////////////////////
    // compute transform axis from object position and target point
    ///////////////////////////////////////////////////////////////////////////////
    void lookAtToAxes(const Vector3& position, const Vector3& target,
                      Vector3& left, Vector3& up, Vector3& forward)
    {
        // compute the forward vector
        forward = target - position;
        forward.normalize();
    
        // compute temporal up vector based on the forward vector
        // watch out when look up/down at 90 degree
        // for example, forward vector is on the Y axis
        if(fabs(forward.x) < EPSILON && fabs(forward.z) < EPSILON)
        {
            // forward vector is pointing +Y axis
            if(forward.y > 0)
                up = Vector3(0, 0, -1);
            // forward vector is pointing -Y axis
            else
                up = Vector3(0, 0, 1);
        }
        // in general, up vector is straight up
        else
        {
            up = Vector3(0, 1, 0);
        }
    
        // compute the left vector
        left = up.cross(forward);  // cross product
        left.normalize();
    
        // re-calculate the orthonormal up vector
        up = forward.cross(left);  // cross product
        up.normalize();
    }
    ///////////////////////////////////////////////////////////////////////////////
    // compute transform axis from object position, target and up direction
    ///////////////////////////////////////////////////////////////////////////////
    void lookAtToAxes(const Vector3& pos, const Vector3& target, const Vector3& upDir,
                      Vector3& left, Vector3& up, Vector3& forward)
    {
        // compute the forward vector
        forward = target - pos;
        forward.normalize();
    
        // compute the left vector
        left = upDir.cross(forward);  // cross product
        left.normalize();
    
        // compute the orthonormal up vector
        up = forward.cross(left);     // cross product
        up.normalize();
    }

Tidak ada komentar:

Posting Komentar