Search Unity

Cannot get worldnormal in surf shader

Discussion in 'Shaders' started by komatii, Jul 25, 2011.

  1. komatii

    komatii

    Joined:
    Sep 8, 2010
    Posts:
    58
    I'm using this function
    worlNorm = WorldNormalVector( IN, float3( 0, 0, 1 ) )
    however all I get from it is the value in the float3() that is the 2nd input to the WorldNormalVector function.
    The lighting on the object is correct, which means the normal vectors have to be correct.

    komatii
     
  2. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,880
    from the documents;
    float3 worldNormal - will contain world normal vector if surface shader does not write to o.Normal.
    float3 worldNormal; INTERNAL_DATA - will contain world normal vector if surface shader writes to o.Normal. To get the normal vector based on per-pixel normal map, use WorldNormalVector (IN, o.Normal).

    so you just put it in your struct and access it with o.worldNormal if you dont write to o.Normal, or WorldNormalVector (IN, o.Normal) if you write to it.
     
    GameDevMig likes this.
  3. komatii

    komatii

    Joined:
    Sep 8, 2010
    Posts:
    58
    I can't use float3 worldNormal in the input struct because it tells me it has run out of interpolators. Even though my input struct is only using two texture coords. i.e. it says this results in too many interpolators for SM3.0

    struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
    float3 worldNormal;
    };

    which seems ridiculous, given that even if I don't explicitly declare "float3 worldNormal" it still has the worldNormal, as if declaring it now means there are two interpolators providing worldNormal
     
  4. komatii

    komatii

    Joined:
    Sep 8, 2010
    Posts:
    58
    Also, I've tried not writing to o.Normal, and tried to use o.worldNormal and o.WorldNormal to get the world normal in the surface shader, but it tells me that "expression left of ."worldNormal" is not a struct or array" - no problem with o.Albedo, o.Gloss, etc.

    When I use WorldNormalVector( IN, float3(x,y,z) ) I just get whatever the value of float3 is, regardless of whether I set o.Normal, before the WorldNormalVector function, after it, or not at all. Lighting is correct, so it definitely has the normal vector available in an interpolator [and presumably the tangent too]
     
  5. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    As was mentioned, you need INTERNAL_DATA as well as float3 worldNormal;

    Code (csharp):
    1.  
    2. struct Input {
    3.     float2 uv_MainTex;
    4.     float2 uv_BumpMap;
    5.     float3 worldNormal;
    6.     INTERNAL_DATA
    7. };
    8.  
    9. void surf (Input IN, inout SurfaceOutput o) {
    10.     o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5;
    11.     o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    12.     float3 worldNormal = WorldNormalVector (IN, o.Normal);
    13. }
    14.  
     
  6. sleep

    sleep

    Joined:
    Aug 7, 2013
    Posts:
    8
    Using the above code, I can't get IN.worldNormal to work unless I comment out the o.Normal = ... line.

    i.e. This works:
    float3 worldNormal = WorldNormalVector (IN, o.Normal);

    This doesn't work unless I comment out o.Normal = ...
    float3 worldNormal = IN.worldNormal;

    Yet the docs say IN.worldNormal; INTERNAL_DATA will contain the worldNormal "if surface shader writes to o.Normal".

    What I need is the good 'ole vanilla Graphics 101 per-pixel interpolated vertex normal without any normal maps applied. But I also want the normal map applied to the regular o.Normal as usual. Is this impossible?

    <rant>
    The documentation on Surface Shaders is pretty terrible. Unity docs authors, when describing precise technical topics, please use precise technical language. "will contain world normal vector if surface shader writes to o.Normal" is obtuse and misleading and simply doesn't adequately describe the actual behaviour.
    </rant>
     
    Last edited: Feb 4, 2014
  7. brilliantgames

    brilliantgames

    Joined:
    Jan 7, 2012
    Posts:
    1,937
    ^ This.
     
  8. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Yes, that's possible. Assign o.Normal as usual, but don't use that to get the world normal - use a flat normal instead.

    Code (csharp):
    1. struct Input {
    2.     float2 uv_MainTex;
    3.     float2 uv_BumpMap;
    4.     float3 worldNormal;
    5.     INTERNAL_DATA
    6. };
    7.  
    8. void surf (Input IN, inout SurfaceOutput o) {
    9.     o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5;
    10.     o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    11.     float3 worldNormal = WorldNormalVector (IN, float3(0,0,1));
    12. }
    Or you could just write a custom vertex shader and pass through the world normal to the surf function from there. Which will be cheaper to do.

    Code (csharp):
    1. #pragma surface surf SurfName vertex:vert
    2.  
    3. struct Input {
    4.     float2 uv_MainTex;
    5.     float2 uv_BumpMap;
    6.     float3 wNormal;
    7. };
    8.  
    9. void vert (inout appdata_full v, out Input data)
    10. {
    11.     UNITY_INITIALIZE_OUTPUT(Input,v);
    12.     data.wNormal = mul((float3x3)_Object2World, v.normal));
    13. }
    14.  
    15. void surf (Input IN, inout SurfaceOutput o) {
    16.     o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5;
    17.     o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    18.     // your interpolated world normal is now IN.wNormal
    19. }
     
  9. frogsbo

    frogsbo

    Joined:
    Jan 16, 2014
    Posts:
    79
    When i try this line, the shading goes uniformly grey, i dont understand why:

    o.Normal = IN.worldNormal;