Normal Viewer Shaders Confusion ???

Hey Guys I’ve been hacking together a normal viewer replacement shader. I was able to use a lot of posts on the forum to put it together.
It appears to work, but there are still a few things I’m confused about.

Mainly I just want to confirm or rectify my understanding of the process of the shader below.

struct v2f {
                float3 worldPos : TEXCOORD0;
                // these three vectors will hold a 3x3 rotation matrix
                // that transforms from tangent to world space
                half3 tspace0 : TEXCOORD1; // tangent.x, bitangent.x, normal.x
                half3 tspace1 : TEXCOORD2; // tangent.y, bitangent.y, normal.y
                half3 tspace2 : TEXCOORD3; // tangent.z, bitangent.z, normal.z
                float2 uv : TEXCOORD4;
                float4 pos : SV_POSITION;
            };

            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL, float4 tangent : TANGENT, float2 uv : TEXCOORD0)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.worldPos = mul(unity_ObjectToWorld, vertex).xyz;
                o.uv = TRANSFORM_TEX(uv, _MainTex);
                half3 wNormal = UnityObjectToWorldNormal(normal);
                half3 wTangent = UnityObjectToWorldDir(tangent.xyz);
                half tangentSign = tangent.w * unity_WorldTransformParams.w;
                half3 wBitangent = cross(wNormal, wTangent) * tangentSign;
                o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x);
                o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y);
                o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv));
                half3 worldNormal;
                worldNormal.x = dot(i.tspace0, tnormal);
                worldNormal.y = dot(i.tspace1, tnormal);
                worldNormal.z = dot(i.tspace2, tnormal);

                fixed4 c = 0;
                c.rgb = normalize(mul((float3x3)UNITY_MATRIX_V, worldNormal))/2+0.5;
                return c;
            }

My understanding so far is that I am first transforming the combined mesh normal and normal map in the frag function into worldnormals. Then from there I am transforming them into view space using UNITY_MATRIX_V.

So finally it outputs an objects normals in the viewports view space.

Please correct me if anything I said is wrong.

1 Like

Looks correct to me, though you’re unnecessary computing and passing the world space position in the vertex shader to the fragment shader.

Is something in particular not behaving as you would expect from the above code?

Well it works when replacing the standard shader, but if I’m replacing the basic unlit shader there’s wierd behavior towards the poles of a sphere primitive. I’ll post a picture when I get to my computer.

The left is the standard, and the right is the unity default unlit shader.

3298694--255754--DeleteME.PNG

Anybody know why this behavior may be happening?

I wonder if it might be a bug, like the mesh’s tangents aren’t being passed along. What happens if you try rendering just the v.tangent without doing anything to convert it? I wonder if the unlit sphere ends up rendering black.

The left is standard the right is unlit

They appear to come out the same.

Try the normal, then try the normal map. Make sure they’re all working as expected.

1 Like

Thanks for the help I figured out that it was being caused by not assigning a normal map to it. Now I simply added a shader feature _NormalMap, and then returned just the viewspace normal from the mesh if no normal map is defined.

Left is standard, right is unlit.
3299826--255882--upload_2017-11-25_2-28-38.png

1 Like