Search Unity

Matrix4x4 * Vector3

Discussion in 'Scripting' started by lassade, Jul 7, 2015.

  1. lassade

    lassade

    Joined:
    Jan 27, 2013
    Posts:
    127
    Just for knowing ...

    Multiplying a Matrix4x4 by Vector3 is very counter intuitive, for instance if i have a local to world matrix (Matrix4x4), and want to multiply by a world coordinate like a point (Vector3) would expect that the fourth coordinate to be one, but is not.

    For instance the code below finds the axis aligned bounds of a given mesh in worlds coordinates.

    Code (CSharp):
    1. // ...
    2.  
    3. var meshBounds = mesh.bounds;
    4. var meshTransform = gameObject.transform;
    5. var localToWorldMatrix = meshTransform.localToWorldMatrix;
    6.  
    7. meshBounds.center = localToWorldMatrix * meshBounds.center; // Will give the wrong result
    8. meshBounds.size = localToWorldMatrix * meshBounds.size;
    I get it that if i want to get the real mesh size in line 7 the fourth coordinate must be 0, the opposite occurs in line 6 here the fourth coordinate must be 1. Its understandable that both calls in line 6 and 7 are ambiguous (if we want w to be 0 or 1) and some thing must be done to sove it.

    So to get the right thing to do is convert to Vector4 and assign 1 to w.

    Code (CSharp):
    1. // ...
    2. var meshBounds = mesh.bounds;
    3. var meshTransform = gameObject.transform;
    4. var localToWorldMatrix = meshTransform.localToWorldMatrix;
    5.  
    6. var center4 = (Vector4) elementBounds.center;
    7. center4.w = 1;
    8.  
    9. meshBounds.center = localToWorldMatrix * center4; // Result is right
    10. meshBounds.size = localToWorldMatrix * meshBounds.size;
    Why this happens:
    Matrix4x4 only have an overload to multiplication between Matrix4x4 and Vector4, (multiplication by Matrix4x4 and Vector3 it self does not make sense the rows and columns does not match), but Vector4 as an implicit operator to convert from Vector3 to Vector4. Calling Matrix4x4 * Vector3 is the same as Matrix4x4 * ((Vector4) Vector3) so 'w' will always be equal to 0.

    The thing is will be nice to have a error or warn thrown like in float to int or double to float cast.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    This is why Matrix4x4 has the three functions MultiplyPoint, MultiplyPoint3x4 and MultiplyVector defined:
    http://docs.unity3d.com/ScriptReference/Matrix4x4.MultiplyPoint.html

    http://docs.unity3d.com/ScriptReference/Matrix4x4.MultiplyPoint3x4.html

    http://docs.unity3d.com/ScriptReference/Matrix4x4.MultiplyVector.html

    Here's the logic behind it. A Matrix4x4 is just that a 4x4 matrix. It can represent anything you want stored in a Matrix4x4.

    The operators on it are specifically defined for the logical operations a 4x4 matrix can perform. A 4x4 matrix can not be multiple against a 1x3 matrix (a Vector3 is effectively a 1x3 or 3x1 matrix). But it COULD by multiplied by a 1x4, by the rules of matrix multiplication in mathematics.

    What you're expecting though is what would happen if you had used a 4x4 matrix to represent a 3 dimensional spacial transformation (SRT: scale, rotation, translation). When multiplying those the matrix must first be set up correctly, and also special rules defined for how you're multiplied (the 4th row is usually ignored).

    Instead of these being built into the * operator... they chose to define it as a function. Which also allows for some specifics to be made about the kind of Vector3 multiplication you want.

    MultiplyPoint - treat the vector3 as a point that should be transformed by the matrix, include any scaling information that may exist in row 4, if it were set to anything other than 0,0,0,1

    MultiplyPoint3x4 - treat the vector3 as a point that should be transformed by the matrix, ignore any scaling information that may exist in row 4

    MultiplyVector - treat the vector3 as direction and magnitude (as a true vector), multiplying by only the 3x3 portion of the vector that represents rotation and scale (direction and magnitude respectively).
     
  3. lassade

    lassade

    Joined:
    Jan 27, 2013
    Posts:
    127
    Thanks, this functions are really helpful.

    The docs about MultiplyVector (http://docs.unity3d.com/ScriptReference/Matrix4x4.MultiplyVector.html) says "When transforming a direction, only the rotation part of the matrix is taken into account." The "rotation part" is actually rotation and scale, because both goes together in 4x4 matrix.

    Now the code goes like this:

    Code (CSharp):
    1. // ...
    2. var meshBounds = mesh.bounds;
    3. var meshTransform = gameObject.transform;
    4. var localToWorldMatrix = meshTransform.localToWorldMatrix;
    5.  
    6. meshBounds.center = localToWorldMatrix.MultiplyPoint3x4 (meshBounds.center);
    7. meshBounds.size = localToWorldMatrix.MultiplyVector (meshBounds.size);
    8.  
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Yeah, that's bad documentation.

    I mean sure... technically it uses only the rotation part of the matrix. But that same portion of the matrix is ALSO the part of the matrix that stores scale/skew information.
     
  5. Darkgaze

    Darkgaze

    Joined:
    Apr 3, 2017
    Posts:
    397
    MelvMay likes this.