Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

World space normal?

Discussion in 'Shaders' started by Arnage, Aug 29, 2010.

  1. Arnage

    Arnage

    Joined:
    Aug 29, 2010
    Posts:
    3
    I'm trying to convert the vertex normals to world space, however for some reason the methods that work fine for position don't seem to work with normals.

    Here's an example of a position conversion I found here on the forum:

    Code (csharp):
    1. float3 worldPosition = mul ((float4x4)_Object2World, v.vertex );
    If that works, I'd expect the following to work fine too:

    Code (csharp):
    1. float3 worldNormal = mul ((float4x4)_Object2World, v.normal );
    I've tried other methods but I just can't get it to compile for some reason. Any clues why that doesn't work and how you're supposed to do it?
     
  2. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    Normals are vectors and vectors behave differently than points (this is why transform has both TransformPoint and TransformDirection functions).

    You will need to use a matrix that doesn't contain the translation component to transform vectors. Since you're not doing projection, this would be the same matrix but without the 4th column and 4th row (so it's a 3x3 matrix). I don't know if casting the matrix to float3x3 does this for you, but you could at least try that first.
     
  3. Arnage

    Arnage

    Joined:
    Aug 29, 2010
    Posts:
    3
    Of course, thanks for the quick reply and making me feel stupid, it's all good now :D
     
  4. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,183
    It does :D

    Note: I Think SCALED_NORMAL is a unity 3 macro.
    Code (csharp):
    1.  
    2. mul((float3x3)_Object2World, SCALED_NORMAL);
    3.  
     
    PrimalCoder likes this.
  5. Patapom

    Patapom

    Joined:
    Aug 19, 2009
    Posts:
    155
    Actually, the best methods yet is to write this for points :
    Code (csharp):
    1. float3 worldPosition = mul( _Object2World, float4( v.vertex, 1.0 ) ).xyz;
    and this for vectors :
    Code (csharp):
    1. float3 worldNormal = mul( _Object2World, float4( v.normal, 0.0 ) ).xyz;

    • When you append 1 at the end of a float3 like (X,Y,Z,1) this means the multiplication with a matrix will add the translation part of the matrix during the operation : so a point gets rotated, scaled and translated.


    • When you append 0 at the end of a float3 like (X,Y,Z,0) this only rotates and scales the vector, it doesn't translate it. Which is what we want for normals. And anyway it makes no sense to translate a vector : only points can be translated.
     
  6. Patapom

    Patapom

    Joined:
    Aug 19, 2009
    Posts:
    155
    Anyway, when dealing with homogeneous coordinates (i.e. the fourth W component of a float4), try to always make the distinction between points and vectors and know what you are manipulating :

    • You can add 2 vectors : this yields a new vector
    • You can subtract 2 vectors : this yields a new vector
    • You can add a point and a vector : this yields a new point (i.e. a translated point)
    • You can subtract 2 points : this yields a vector (i.e. the vector going from the first point to the second one)
    • You CANNOT add 2 points together
    This is basic geometry but the distinction between a vector (X,Y,Z,0) and a point (X,Y,Z,1) is all important here.
     
    Lipoly likes this.
  7. richierich1204

    richierich1204

    Joined:
    May 20, 2010
    Posts:
    45
    I love you guys for this thread! Aside from helping to fix my current shader issue, my world now makes sense. :)
     
  8. peacefulshade

    peacefulshade

    Joined:
    Jul 25, 2012
    Posts:
    9
    In case someone still has a problem using

    float3 worldNormal = mul( _Object2World, float4( v.normal, 0.0 ) ).xyz;

    it works if the transformation of the object is not scaled, if you apply scale in your transforms use this:

    float3 worldNormal = mul( float4( v.normal, 0.0 ), _World2Object ).xyz;

    This is because object to world matrices containing scaling mess up the normals:

    Notice that the normal in the triangle of the right is no longer perpendicular to it's surface.

    There's a really nice explanation of this in "Introduction to 3D Game Programming with DirectX 9.0c a shader approach" by Frank D. Luna, pages 246 - 247. If you read it, you'll notice that the correct matrix is the inverse transpose of the _Object2World matrix, that's why the multiplication here:

    float3 worldNormal = mul( float4( v.normal, 0.0 ), _World2Object ).xyz;

    Has the normal as the first parameter and the matrix as the second.
     
    Last edited: May 6, 2016
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,243
    There's a Unity function for this that's in UnityCG.cginc:
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);

    It's basically just doing the equivalent of your code but broken out to:
    return normalize(_World2Object[0].xyz * norm.x + _World2Object[1].xyz * norm.y + _World2Object[2].xyz * norm.z);

    Note: mul( v.normal, (float3x3)_World2Object ) also works and is more efficient than your example, and should result in identical code to Unity's function. Doing a float4(xyz, 0.0) results in identical results, but means the shader might spend time multiplying things by zero that you don't even want.*

    * Most shader compilers while likely optimize this away as it knows it's a zero, but not all.
     
    Last edited: May 6, 2016
  10. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    385
    Man you are super cool :D You are everywhere and that's awesome
     
    rduret and SenseEater like this.