Search Unity

Understanding SurfaceOutput normal

Discussion in 'Shaders' started by Mortom, Oct 27, 2014.

  1. Mortom

    Mortom

    Joined:
    Aug 21, 2014
    Posts:
    20
    I've been trying to output a world normal from my shader however the lighting is always wrong whenever I write to o.normal

    The documentation isn't very clear what is meant by normal with regards to space (object, world, tangent).

    I did see the input structure can use 'worldNormal', and when I debug render this to the albedo output using o.Albedo = IN.worldNormal + 1 * 0.5;, I can see the colours I would expect.

    I want to calculate my own worldNormal and output it, however I just can't make it work.

    So as a test I wrote o.Normal = float3(0.0,0.0,1.0);, the standard Z direction normal for tangent space. The lighting of the object now seems correct. So I'm guessing SurfaceOutput Normal is in tangent space and if the Surface shader writes to it, it is later transformed by the tangent normal somewhere under the hood. Is this correct?

    So if I want to calculate my own world normal I need to convert this back into tangent space? Is this correct?

    My alternative would be to write a vert + fragment shader and do it myself. I've already done this is a few shaders but I wanted to try and use Shaderlab as I believe the shader has more of a chance of compiling across a variety of platforms.
     
    Last edited: Oct 27, 2014
  2. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Yeah, o.Normal should be in tangent space if it's written to (and s.Normal is in tangent space then, too).
    If o.Normal is not written to, s.Normal defaults to object space (essentially, interpolated vertex normals).

    The tangent space normal isn't transformed back into object space inside the shader, instead the view and light directions are transformed into tangent space inside of the vertex shader and fed into the pixel shader. Which is why there are inaccuracies with the lighting when in forward rendering mode.

    IN.worldNormal will give you the world normal, yes, but if you want the world normal from a normal map (i.e. you're writing to o.Normal) you need to append INTERNAL_DATA to the Input struct, that will tell it to get the normal/tangent/binormal vectors from the vertex shader, and it'll use those to convert your tangent space map into world space for IN.worldNormal.

    It's all a bit confusing and expensive... you might well be better of writing it as vert/frag.
     
    michaelhou likes this.