Is it possible to use both an object's world normal and a normal map in a shader?

I’m working on a surface shader right now that uses the object’s world normal to calculate whether or not it should be covered by snow:

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = tex2D(_MetallicGlossMap, IN.uv_MainTex).r * _Metallic;
            o.Smoothness = tex2D(_MetallicGlossMap, IN.uv_MainTex).a *_Glossiness;
            if (dot(o.Normal, _SnowAngle.xyz) >= _SnowSize - 0.4) { // if dot product result is higher than snow amount, we turn it into snow
                float snowAmount = saturate((dot(o.Normal, _SnowAngle.xyz) - (_SnowSize - 0.4))/_SnowTimes);
                float4 snowColor = (lerp(_SnowColor, _TColor, snowAmount)); // blend base snow with top snow based on position
               
                o.Albedo = (snowColor * snowAmount + c.rgb * (1 - snowAmount))*tex2D(_SnowTex, IN.uv_MainTex);
                o.Metallic = _SnowGloss;
                o.Smoothness = _SnowMetallic;
            }
            //o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Alpha = c.a;
        }

This works fine, but if I un-comment the line setting the normal, the shader uses the normal map instead of the mesh’s world normals even though that code happens after the snow calculation.

I’m wondering if there’s a way to access the mesh’s world normals in a surface shader so I can use it independently from the final, rendered normals that use the normal map.

Thanks!

The documentation has you covered here:

Except, it’s wrong. If you write to o.Normal there’s a long time bug that the IN.worldNormal isn’t set properly. But the above documentation still has a clue. It has the example of WorldNormalVector (IN, o.Normal). You’re using that above, but the example assumes you’ve set o.Normal before hand. It’s transforming the tangent space normal vector you get from a normal map and UnpackNormal into world space.

Alternative if you want to just get the vertex world normal you can do this:
IN.worldNormal = WorldNormalVector(IN, float3(0,0,1));

1 Like