21
\$\begingroup\$

Assume you're writing matrix code that handles rotation, translation etc for 3d space.

Now the transformation matrices have to be 4x4 to fit the translation component in.

However, you don't actually need to store a w component in the vector do you?

Even in perspective division, you can simply compute and store w outside of the vector, and perspective divide before returning from the method.

For example:

// post multiply vec2=matrix*vector
Vector operator*( const Matrix & a, const Vector& v )
{
  Vector r ;
  // do matrix mult
  r.x = a._11*v.x + a._12*v.y ...

  real w = a._41*v.x + a._42*v.y ...

  // perspective divide
  r /= w ;

  return r ;
}

Is there a point in storing w in the Vector class?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ That's not the implementation for a normal matrix vector multiplication, the perspective divide does not belong there. Also it is quite misleading, because the wrong parts of the calculation are highlighted. If you want to find out what the w-component is for, look at the complete implementation, then you see that the last row/column (the translation part) of the matrix is only applied, if the w-component is 1, i.e. for points. You should highlight those parts: r.x = ... + a._14*v.w; r.y = ... + a._24*v.w; r.z = ... + a._34*v.w; r.w = ... + a._44*v.w; look at my answer for details \$\endgroup\$ Commented Jun 25, 2011 at 9:01

5 Answers 5

30
\$\begingroup\$

EDIT Disclaimer: For convenience in this answer vectors with w==0 are called vectors and with w==1 are called points. Although as FxIII pointed out, that is not a mathematically correct terminology. However, since the point of the answer is not the terminology, but the need to distinguish both types of vectors, I'll stick to it. For practical reason this convention is widely used in game-development.


It is not possible to distinguish between vectors and points without a 'w' component. It is 1 for points and 0 for vectors.

If vectors are multiplied with a 4x4 affine transformation-matrix that has a translation in its last row/column, the vector would also be translated, which is wrong, only points must be translated. The zero in the 'w' component of a vector takes care of that.

Highlighting this part of the matrix-vector multiplication makes it more clear:

    r.x = ... + a._14 * v.w; 
    r.y = ... + a._24 * v.w; 
    r.z = ... + a._34 * v.w; 
    r.w = ... + a._44 * v.w;

a._14, a._24 and a._34 is the translational part of the affine matrix.
Without a 'w' component one has to set it implicitly to 0 (vector) or to 1 (point) 

I.e. it would be wrong to translate a vector, for instance a rotation axis, the result is simply wrong, By having it's 4th component zero you can still use the same matrix that transforms the points to transform the rotation axis and the result will be valid and its length is preserved as long as there is no scale in the matrix. That is the behavior you want for vectors. Without the 4th component you would have to create 2 matrices (or 2 different multiplication functions with an implicit 4th parameter. and make 2 different function calls for points and vectors.

In order to use the vector registers of modern CPUs (SSE, Altivec, SPUs) you have to pass 4x 32 bit floats anyway (its a 128 bit register), plus you have to take care of the alignment, usually 16 bytes. So you don't have the chance to safe the space for the 4th component anyway.


EDIT: The answer to the question is basically

  1. Either store the w-component: 1 for positions and 0 for vectors
  2. Or call different matrix-vector multiplication functions and implicitly pass the 'w' component by choosing one of either functions

One must pick one of them, it is not possible to store only {x, y, z} and still use only one matrix-vector multiplication function. XNA for example uses the latter approach by having 2 Transform functions in its Vector3 class, called Transform and TransformNormal

Here is a code example that shows both approaches and demonstrates the need to distinguish both kind of vectors in 1 of the 2 possible ways. We will move a game-entity with a position and a look-direction in the world by transforming it with a matrix. If we don't use the 'w' component, we can't use the same matrix-vector multiplication anymore, as this example demonstrates. If we do it anyway, we will get a wrong answer for the transformed look_dir vector:

#include <cstdio>
#include <cmath>

struct vector3
{
    vector3() {}
    vector3(float _x, float _y, float _z) { x = _x; y = _y; z = _z; }
    float x, y, z;    
};

struct vector4
{
    vector4() {}
    vector4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; }
    float x, y, z, w;
};

struct matrix
{
    // convenience column accessors
    vector4&        operator[](int col)         { return cols[col]; }
    const vector4&  operator[](int col) const   { return cols[col]; }
    vector4 cols[4];
};

// since we transform a vector that stores the 'w' component, 
// we just need this one matrix-vector multiplication
vector4 operator*( const matrix &m, const vector4 &v )
{
    vector4 ret;
    ret.x = v.x * m[0].x + v.y * m[1].x + v.z * m[2].x + v.w * m[3].x;
    ret.y = v.x * m[0].y + v.y * m[1].y + v.z * m[2].y + v.w * m[3].y;
    ret.z = v.x * m[0].z + v.y * m[1].z + v.z * m[2].z + v.w * m[3].z;
    ret.w = v.x * m[0].w + v.y * m[1].w + v.z * m[2].w + v.w * m[3].w;
    return ret;
}

// if we don't store 'w' in the vector we need 2 different transform functions
// this to transform points (w==1), i.e. positions
vector3 TransformV3( const matrix &m, const vector3 &v )
{
    vector3 ret;
    ret.x = v.x * m[0].x + v.y * m[1].x + v.z * m[2].x + 1.0f * m[3].x;
    ret.y = v.x * m[0].y + v.y * m[1].y + v.z * m[2].y + 1.0f * m[3].y;
    ret.z = v.x * m[0].z + v.y * m[1].z + v.z * m[2].z + 1.0f * m[3].z;
    return ret;
}

// and this one is to transform vectors (w==0), like a direction-vector
vector3 TransformNormalV3( const matrix &m, const vector3 &v )
{
    vector3 ret;
    ret.x = v.x * m[0].x + v.y * m[1].x + v.z * m[2].x + 0.0f * m[3].x;
    ret.y = v.x * m[0].y + v.y * m[1].y + v.z * m[2].y + 0.0f * m[3].y;
    ret.z = v.x * m[0].z + v.y * m[1].z + v.z * m[2].z + 0.0f * m[3].z;
    return ret;
}

// some helpers to output the results
void PrintV4(const char *msg, const vector4 &p )  { printf("%-15s: %10.6f %10.6f %10.6f %10.6f\n",  msg, p.x, p.y, p.z, p.w ); }
void PrintV3(const char *msg, const vector3 &p )  { printf("%-15s: %10.6f %10.6f %10.6f\n",         msg, p.x, p.y, p.z); }

#define STORE_W     1

int main()
{
    // suppose we have a "position" of an entity and its 
    // look direction "look_dir" which is a unit vector

    // we will move this entity in the world

    // the entity will be moved in the world by a translation 
    // in x+5 and a rotation of 90 degrees around the y-axis 
    // let's create that matrix first

    // the rotation angle, 90 degrees in radians
    float a = 1.570796326794896619f;
    matrix moveEntity;
    moveEntity[0] = vector4( cos(a), 0.0f, sin(a), 0.0f);
    moveEntity[1] = vector4(   0.0f, 1.0f,   0.0f, 0.0f);
    moveEntity[2] = vector4(-sin(a), 0.0f, cos(a), 0.0f);
    moveEntity[3] = vector4(   5.0f, 0.0f,   0.0f, 1.0f);

#if STORE_W

    vector4 position(0.0f, 0.0f, 0.0f, 1.0f);
    // entity is looking towards the positive x-axis
    vector4 look_dir(1.0f, 0.0f, 0.0f, 0.0f);

    // move the entity using the matrix
    // we can use the same function for the matrix-vector multiplication to transform 
    // the position and the unit vector since we store 'w' in the vector
    position = moveEntity * position;
    look_dir = moveEntity * look_dir;

    PrintV4("position", position);
    PrintV4("look_dir", look_dir);

#else

    vector3 position(0.0f, 0.0f, 0.0f);
    // entity is looking towards the positive x-axis
    vector3 look_dir(1.0f, 0.0f, 0.0f);

    // move the entity using the matrix
    // we have to call 2 different transform functions one to transform the position 
    // and the other one to transform the unit-vector since we don't 
    // store 'w' in the vector
    position = TransformV3(moveEntity, position);
    look_dir = TransformNormalV3(moveEntity, look_dir);

    PrintV3("position", position);
    PrintV3("look_dir", look_dir);

#endif

    return 0;
}

Initial Entity state:

position       :   0.000000   0.000000   0.000000   1.000000
look_dir       :   1.000000   0.000000   0.000000   0.000000

Now a transformation with a translation of x+5 and a rotation of 90 degrees around the y-axis will be applied to this entity. The correct answer after the tranformation is:

position       :   5.000000   0.000000   0.000000   1.000000
look_dir       :   0.000000   0.000000   1.000000   0.000000

We will only get the correct answer if we distinguish vectors with w==0 and positions with w==1 in one of the above presented ways.

\$\endgroup\$
15
  • \$\begingroup\$ @Maik Semder You are a little wrong... It is not possible to distingush between vectors an points because those are the same thing!(they are isomorphic) 1 for vectors and 0 for infinite directioned verctors (as i say in my answer). The remainder of the response has little sense because of wrong assumptions. \$\endgroup\$
    – FxIII
    Commented Jun 25, 2011 at 14:38
  • 1
    \$\begingroup\$ @FxIII I fail to see your point (no pun intended) and the relevance to this question. You are saying vectors and points are the same, so it makes no sense to store 'w' anyway, seriously? Now either you are gonna revolutionize computer graphics or you don't get the point of this question. \$\endgroup\$ Commented Jun 25, 2011 at 14:58
  • 1
    \$\begingroup\$ @FxIII That's nonsense, you might want to study some 3D math frameworks used in game-development, i.e. Sony's vectormath, you will find a lot of such implementations, particluary look at the implementation of vmathV4MakeFromV3 and vmathV4MakeFromP3 in vec_aos.h, study the difference and what they put into the 4th component, 1.0 for P3 and 0.0 for V3, 3D point and 3D vector obviously. \$\endgroup\$ Commented Jun 25, 2011 at 17:16
  • 3
    \$\begingroup\$ @FxIII that is also the reason why the XNA Vector3 class has a "Transform" and a "TransformNormal" member-function, the reason is the math of linear algebra. What you basically do by choosing one of those Transform functions is passing an implicit 'w' parameter of '1' or '0', which basically includes the 4th row of the matrix into the calculation or not. Summarize: If you don't store the 'w' component, then you have to treat those vectors differently by calling different transformation functions. \$\endgroup\$ Commented Jun 25, 2011 at 20:40
  • 1
    \$\begingroup\$ Vectors and points are isomorphic as said, hence there is no algebraic difference between them. However, what the homogenous model of projective space tries to represent is that the SET of vector SPACES, and points are not isomorphic. The set of vector spaces is in effect a type of closure for R^3 that includes the points on the infinite sphere. Points with w=0 are often incorrectly referred to as "vectors" - these are actually isomorphic to the direction sphere, and would be more accurately termed simply "directions"... And no, losing w may often work, but mostly you will be finding trouble. \$\endgroup\$
    – Crowley9
    Commented Jun 28, 2011 at 4:28
4
\$\begingroup\$

If you're making a Vector class, then I presume the class will store the description of a 3D vector. 3D vectors have x, y, and z magnitudes. So unless your vector needs an arbitrary w magnitude, no, you won't store it in the class.

There is a big difference between a vector and a transformation matrix. Given that both DirectX and OpenGL deal with matrices for you, I don't typically store a 4x4 matrix in my code; rather, I store Euler rotations (or Quaternions if you'd like - which coincidentally do have a w component) and x,y,z translation. The translation is a vector if you'd like, and the rotation technically would fit in a vector as well, where each component would store the rotation amount around its axis.

If you want to dive a bit deeper into the math of a vector, a Euclidean vector is just a direction and a magnitude. So typically this is represented by a triplet of numbers, where each number is the magnitude along an axis; its direction is implied by the combination of these three magnitudes, and the magnitude can be found with the Euclidean distance formula. Or, sometimes it really is stored as a direction (a vector with length=1) and a magnitude (a float), if that's what is convenient (e.g. if the magnitude changes more often than the direction, it may be more convenient to just change that magnitude number than to take a vector, normalize it, and multiply the components by the new magnitude).

\$\endgroup\$
1
  • 6
    \$\begingroup\$ Modern OpenGL doesn't deal with matrices for you. \$\endgroup\$ Commented Jun 25, 2011 at 9:56
4
\$\begingroup\$

The fourth dimension in 3D vector is used to compute the affine transformations that will be impossible to compute using matrixes alone. The space remains three-dimensional so this mean that the fourth is mapped in the 3d space in some way.

Map a dimensions means that different 4D vectors indicates the same 3D point. The map is that if A = [x',y',z'.w'] and B = [x",y",z",w"] they represents the same point if x'/x" = y'/y" = z'/z" =w'/w" =α i.e. the component are proportional for the same coefficient α.

Said that you can express a point - say (1,3,7) - in infinite manners like (1,3,7,1) or (2,6,14,2) or (131,393,917,131) or in general (α·1,α·3,α·7,α).

This means that you can scale a 4D vector to another representing the same 3D point so that w = 1: the form (x,y,z,1) is the canonical form.

When you apply a matrix to this vector you may obtain a vector that has not the w = 1, but you can always scale the results to store it in canonical form. So the answer appears to be "you should use 4D vectors when doing math but don't store the fourth component".

This is quite true but there are some points that you can't put in canonical form: points like (4,2,5,0). Those points are special ones, they represent directed infinite point and can be normalized to unit vector consistently: you can safely go to the infinite and return (even twice) without being Chuck Norris. You will get a miserable division by zero if you try to force those vectors in canonical form.

Now you know, so the choice is yours!

\$\endgroup\$
1
\$\begingroup\$

Yes, you do. Your transformation is incorrect for some kinds of vector. You can see this in the D3DX mathematical library- they have two different matrix-vector multiplication functions, one for w = 0 and one for w = 1.

\$\endgroup\$
0
\$\begingroup\$

Depends on what you want and need. :)

I'd store it, b/c it IS necessary for transforms and such (you can't multiply a 3 vector with a 4x4 matrix), though if you always just have a w of 1, I guess you could just fake it.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .