Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

world normals and surface shaders

Discussion in 'Shaders' started by snakeylake, Oct 25, 2010.

  1. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    Hi, I haven't figured out how to get world normals into surface shaders. If I try to propagate them through manually, I run out of texcoords.

    I'm fairly certain that a tangent to world matrix is available in the surface shader ( for getting world reflection vector ), but it is abstracted away with INTERNAL_DATA macro.

    Is there any reason that the tangent to world matrix isn't available for us to use in the surface shader?

    thanks,
    -s
     
  2. DerWoDaSo

    DerWoDaSo

    Joined:
    May 25, 2009
    Posts:
    131
    Hey snakeylake,

    Have you found a way to access these parameters? I am looking for the same right now... :(
    Thanks,

    Jan
     
  3. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    So you want to transform normals coming from a normalmap into world space?

    Yeah, internally this is used for reflective shaders. The only reason why it's not exposed in surface shaders... no one asked for it, and I haven't thought about it! ;)
     
  4. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    Aras, that would be great!
     
  5. tokyob

    tokyob

    Joined:
    May 17, 2010
    Posts:
    37
    I vote for, :)
    that would be great (i was searching for it :p)
     
  6. DerWoDaSo

    DerWoDaSo

    Joined:
    May 25, 2009
    Posts:
    131
    That would be great...

    The viewDir and lightDir is in tangent space anyway, right? It would be great if the World2Tangent or Object2Tangent matrix (Pre-3.0 -> TANGENT_SPACE_ROTATION) would be exposed too... this way arbitrary vectors can be used in a custom lighting function. In my current case this would be a vector to a certain world space position...

    [Edit]
    Sorry... I should check this, before posting. The TANGENT_SPACE_ROTATION still seems to be available! :rolleyes:
     
    Last edited: Dec 14, 2010
  7. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    They are in "some space". When you use normal maps, they are in tangent space. When you don't, they are in world space (I think...), and the interpolated per-vertex normals are in that space as well.

    That is, all lighting related vectors (normal, light, view) are in the same space. But the space itself might be different based on the situation.
     
  8. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    I would really like this. Whenever I use INTERNAL_DATA I feel like there is some magic going on behind the scenes that I don't have control over. It also adds a lot of interpolators into the vert -> fragment, which means that it makes it possible to run out of them easily. This wouldn't be so bad an issue if you had control over which compiled shader permutations added INTERNAL_DATA, but currently we don't and it's included for some permutations and not in others.

    If using INTERNAL_DATA / TBN exposing it to be used in all shader permutations would make it then possible to access per pixel world normal and other useful shader properties. I'm going to assume that if you are using deferred and per pixel normals that special permutation of the shader is compiled that has INTERNAL_DATA implicitly defined, unlike the manual definition that is required for things like world reflection.

    The other thing I would love would be a flag to apply texture matrix application in the pixel shader instead of in the vertex shader. Doing it in the vertex shader requires each sampler to have an interpolated output, this really pushes the number of interpolators up and makes it easy to go over the limitations of SM2.0/3.0. If the mesh UV was passed through and then the texture matrix was applied in the pixel shader this would help with the issue of too many interpolators.

    Just some things I have been experiencing when developing the shader editor that you might like to know about.
     
  9. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Built-in variable for world space normal ("worldNormal") is coming in next release soon-ish. This will just use interpolated per-vertex normal when no bumpmapping is used, and will compute it via hidden tangent-to-world matrix in INTERNAL_DATA when bumpmapping is used. Just like worldRefl does now.
     
  10. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Awesome! That'll be very useful for simple cubemap diffuse lighting. Thanks Aras!
     
  11. Deleted User

    Deleted User

    Guest

    That's good news for me, as I'm in the same boat as unisip. My artist wants good ambient lighting for dynamic objects to go along with our RNM lighting for static objects. So I eagerly await the next release! :)

    Though I have a quick question. Will we be able to use worldRefl in conjunction with worldNormal? I'd hate to have to give up one for the other.
     
    Last edited by a moderator: Dec 17, 2010
  12. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Yes, I specifically added tests for that to our testing suite (for both bumpmapped non bumpmapped cases).
     
  13. tokyob

    tokyob

    Joined:
    May 17, 2010
    Posts:
    37
    hello,
    i did some kind of car paint using "surface shader's magic" like INTERNAL_DATA
    it looks like its' ok :



    but if i do a UNIFORM SCALE (in the editor with the scale tool) it's look like the normal are changing (like scaled too) :


    look at the color change on the truck....

    look's weird.... so i try non-uniform scale... (first z, then x and y ... looks like order don't really matter) and it works...


    looks like there is no problem... :

    so my shader don't support" Uniform scale" i don't really understand why...
    I wonder if it is some kind of Unity bug, or if I did something wrong...
    any idea ?

    thanks
     

    Attached Files:

  14. DerWoDaSo

    DerWoDaSo

    Joined:
    May 25, 2009
    Posts:
    131
    Nice shader, but strange problem...
    I guess you use a custom lighting function? Have you tried to normalize the normal before using the shader to see if the problem still occurs? If that doesn't help try the same with the other input vectors like worldRefl, lightDir,...
     
  15. cybernoid

    cybernoid

    Joined:
    Nov 15, 2010
    Posts:
    27
    I am just trying to figure out a work around until you make that fix by calculating the world space normal from worldRefl and viewDir but I am completely confused by what space viewDir is in presently!

    Would you care to suggest the correct work around?
     
  16. tokyob

    tokyob

    Joined:
    May 17, 2010
    Posts:
    37
    hello, thank's DerWoDaSo, that's it, i normalized the normals and now every things working nicely now :)
    thank you again!
     
  17. DerWoDaSo

    DerWoDaSo

    Joined:
    May 25, 2009
    Posts:
    131
    @tokyob: Your welcome, glad it works now!

    Maybe adding a custom vertex program and calculating the world normal there? This requires to create a struct for the appdata (adding the worldNormal parameter). Just making it up from my memory...

    Code (csharp):
    1.  
    2. void vert (inout appdata_custom v) {
    3.    half3 v.worldNormal = mul(object2world(vertex), v.normal);
    4. }
    I am not sure about the Object2World syntax though! ;)
     
  18. cybernoid

    cybernoid

    Joined:
    Nov 15, 2010
    Posts:
    27
  19. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    The original problem that I had was that once I defined INTERNAL_DATA, I didn't have enough texcoords to pass through worldNormal from the vertex shader...

    At some point, I realized that when you define INTERNAL_DATA, IN.worldRefl is the world view vector ( when calculating world reflection it converts tangent space normal to world space normal and reflects it with world view ).

    If you normalize IN.worldView, you can calculate world normal like this once you've calculated world reflection.
    float3 worldNormal = normalize( worldRefl - IN.worldRefl );

    This is wasted instructions as we already calculated worldNormal inside of the macro when calculating world reflection vector... but it works ( right now anyway :)
     
  20. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    Hey guys,

    I've been hassling and googling, but I still can't figure out how one would get the world normal when creating custom lighting functions?

    I've been looking for a tangent to world space matrix, but alas, no luck.

    Any suggestions?
     
  21. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    my previous response in code form

    IN.worldRefl = normalize( IN.worldRefl );
    float3 worldRefl = WorldReflectionVector (IN, o.Normal);
    float3 worldNormal = normalize( worldRefl - IN.worldRefl );
     
  22. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    I am writing a new lighting function, all I have access to is SurfaceOutput and light dir, no IN.
     
  23. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    You don't have to use SurfaceOutput. You can use you own custom output struct for your lighting functions.
     
  24. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Ok, I'm not sure I'm following you here... When I try what you describe, I don't seem to get a world space normal.
    In fact, why would IN.worldRefl contain the worldView vector? If I'm not mistaken, worldRefl - IN.worldRefl = 0 no matter what, unless I'm missing something.

    Can you clarify? Or (even better) post a full example of a shader using this trick?

    That would be greatly appreciated !
     
  25. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    My code above is definitely a hack as I mentioned in one of my previous posts.
    When you define INTERNAL_DATA, IN.worldRefl contains world view ( when you don't define INTERNAL_DATA, it contains world reflection )
    The WorldReflectionVector macro needs the world view vector to calculate the reflection.

    The code snippet that I pasted should just work assuming it compiles. My only guess as to why it wouldn't work would be if INTERNAL_DATA weren't defined... but I wouldn't imagine that it would compile in that case.
     
  26. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Ok, I definitely must be missing something, then.
    Below is the test shader I'm using : it simply outputs worldNormal as a color on the geometry, for debugging purposes. It compiles fine, but the object desperately renders black. Any idea?

    Shader "DiffuseCM" {
    Properties {
    }
    SubShader {
    LOD 200

    CGPROGRAM
    #pragma surface surf Lambert

    struct Input {
    INTERNAL_DATA
    float3 worldRefl;
    };


    void surf (Input IN, inout SurfaceOutput o) {

    //trick to compute world space normal
    IN.worldRefl = normalize( IN.worldRefl );
    float3 worldRefl = WorldReflectionVector (IN, o.Normal);
    float3 worldNormal = normalize( worldRefl - IN.worldRefl);

    o.Albedo = 0;
    o.Emission = worldNormal ;
    }
    ENDCG
    }
     
  27. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    ok nevermind, i figured it out --> when not using a normal map, i just need to init o.Normal to (0,0,1,0). I thought that was done by default (sometimes it's hard to guess what that magic surface shader code does for you or not). Thanks for the tip, snakeylake, it seems to work great :)
     
  28. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    Glad to hear you figured it out. I've seen that bug too. Sometimes it will set the normal map for you and sometimes it won't... seemingly at random ( though it is consistent for a set of code, I have the same results with the shader you've included here).

    I guess we should submit a bug.
     
  29. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Hi all,

    I'm trying to use the newly added worldNormal in my shader.
    Here is a simple example: it's supposed to add a cubemap effect addressed by the world normal (a diffuse cubemap, basically, that is independent of the viewpoint).
    However, I can't seem to make it work. I have two problems:
    1. the worldNormal doesn't seem to work
    2. the shader says it requires tangent space --> how can I tell it to use vertex normals and don't worry about tangent space?

    Here is the shader code:

    Shader "Judiva/CM/Diffuse" {
    Properties {
    _Cube ("Diffuse Cubemap", Cube) = "_Skybox"
    }
    SubShader {
    LOD 200
    Tags { "RenderType"="Opaque" }

    CGPROGRAM
    #pragma surface surf Lambert


    samplerCUBE _Cube;

    struct Input {
    float3 worldNormal;
    INTERNAL_DATA
    };

    void surf (Input IN, inout SurfaceOutput o) {

    o.Normal = float4(0,0,1,0);

    half4 dif = texCUBE (_Cube, IN.worldNormal);

    o.Albedo = 0;
    o.Emission = dif.rgb ;
    }
    ENDCG
    }

    FallBack "Diffuse"
    }
     
  30. snakeylake

    snakeylake

    Joined:
    Aug 20, 2010
    Posts:
    19
    if you don't need to modify the normal remove INTERNAL_DATA

    If you define internal data you have to do it like this
    half4 dif = texCUBE (_Cube, WorldNormalVector (IN, o.Normal));
     
    RenanRL likes this.
  31. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Thanks again for the prompt help (as usual), Snakeylake, it works like a charm :)
     
  32. Mchasse

    Mchasse

    Joined:
    Jul 12, 2012
    Posts:
    5
    Code (csharp):
    1.  
    2.         struct Input
    3.         {
    4.             fixed2 uv_MainTex;
    5.             float3 worldNormal;
    6.         };
    7.        
    8.         void surf (Input IN, inout SurfaceOutput o)
    9.         {
    10.             o.Albedo = texCUBE (_Cube, IN.worldNormal).rgb;
    11.         }
    12.  
    I'm wondering why does the mesh UV matters and interfere with how the cubemap is displayed.
     
  33. spalmer

    spalmer

    Joined:
    Jun 3, 2014
    Posts:
    6
    Aras, can you just give us access to this hidden basis via the TANGENT_SPACE_ROTATION macro (even in surf or fragment shaders) or something already and be done with it? This is ridiculous trying to reverse engineer INTERNAL_DATA to get access to something so widely needed.