Recently, I found my model has normal map seam when rendering in Unity with our custom shader.
And I try to use the Unity built-in shader to render it. The result shows that there's no seams. How strange? Soon I found that because of all UV of my models are symmetry. It means that half of my UV are flipped. Hence, the binormal of the half model has been reversed. (tangent basis is calculated by normal and UV,
http://www.3dkingdoms.com/weekly/weekly.php?a=37). In Maya, I never notice that, because it auto-detects the UV winding direction. So, I just need to do the same thing in my shader.
So after researching, I got the method how to detect the UV winding. Here's the formula:
tangent[a].w = (Dot(Cross(n, t), tan2[a]) < 0.0F) ? -1.0F : 1.0F;
here tangent is stored by float4, the w stores the UV winding direction, 1, or -1.
And tan2 would be the binormal, n is the normal, and t is the actual orthogonalized tangent.
Although this equation answer my question, it didn't solve my problem. It's kind impossible to calculated the
orthogonalized tangent in a shader. But it give me a hint. Would Unity has calculated the UV winding for us and stored it in tangent.w?
With an easy debug shader to show the tangent.w, I got my answer:
So the actual key is:
I just need to write a branch to see if I need to flip the binormal when calculating the tangent space.
float3x3 rotation = float3x3(v.tangent.xyz, binormal.xyz, v.normal.xyz);
if(v.tangent.w > 0)
rotation = float3x3(v.tangent.xyz, -binormal.xyz, v.normal.xyz);
So, here is the result without the UV winding detection:
And this is the correct rendering with UV winding detection
Note: Why we don't want to use the built-int shader in Unity? Just because even the shader is simple as bump spec one which really has a lot of junk inside. And the most important: they hide those expensive calculations in compiled functions.