Here’s a problem.
You’ve got a mesh. You apply specular shader. You enable antialiasing in quality settings.
And suddenly a white pixel dots appear on some random edges when you zoom out from object.
But this is not the problem itself. This is just a prehistory.
And now, here’s the actual problem: this is a known issue. And it’s known since Unity 3!
Many people confirmed it. And it happens regardless of using normal maps or even specular maps at all! You can see it in my screenshots above.
It. Just. Happens. With no relation to mipmaps, normal-mapping or filtering.
In previous Unity versions (3 and 4) there was a dirty hack which fixed it. This hack is more related to some voodoo magic rather than predictable behavior. It’s about adding this line in the end of your surface function:
o.Normal = fixed3(0,0,1);
i have no idea why it worked (and looks like nobody knows) but it worked.
Until now.
Since Unity 5 was released this fix stoped working.
So… I faced this issue. Again. With the only possible way to fix it: to find where it comes from.
You may think this is just another topic with some random guy raging about some bug, but it’s not the case.
I’m here to share the solution.
After several hours of trial and error, I finally found where it comes from. These white dots are caused by normal, which becomes non-normalized after transforming from tangential space (in which it’s described in surface function) to the space in which lighting function works (I don’t remember if it’s object space or world space).
So, in short, even if your normal is normalized in surface function, it may and will become non-normalized when it’s passed to lighting function.
So the only “nice and clean” solution is writing your own lighting function, in which you normalize incoming normal first.
For those of you who’s not so familiar with writing your own lighting functions… here it is:
#pragma surface surf NormalizedBlinnPhong halfasview
fixed4 LightingNormalizedBlinnPhong (SurfaceOutput s, fixed3 lightDir, fixed3 halfDir, fixed atten)
{
// TODO: conditional normalization using ifdef
fixed3 nN = normalize(s.Normal);
fixed diff = max( 0, dot(nN, lightDir) );
fixed nh = max( 0, dot(nN, halfDir) );
fixed spec = pow(nh, s.Specular*128) * s.Gloss;
fixed4 c;
c.rgb = _LightColor0.rgb * (s.Albedo * diff + spec) * atten;
UNITY_OPAQUE_ALPHA(c.a);
return c;
}
Yes, you’re welcome.
It’s based on MobileBlinnPhong from “Mobile/Bumped Specular” and, obviously, this is old-style (pre-U5) surface function (with no realtime GI support).
Feel free to comment if this helped and if I found the right place where subj comes from.
The big question is still there, however. It’s:
Why do normals become un-normalized when AA is enabled in Quality Settings?
Hoping to get answer from UT in the comments below.