Since the release of material variants, the normal map light calculation has been incorrect.
Here’s an example of a normal map:
Now here’s what 4 of them look like side by side (all with different 90 degree rotations on the y-axis). We can see clearly that the final lighting is incorrect.
There has been a report from Zomebody about the issue, but so far this hasn’t been addressed in quite some time.
I really want this bug to be solved, so I’m gonna share my findings looking at the decompiled shader code to show exactly what is causing the issue. For this, I’m using renderdoc (https://renderdoc.org/)
To simplify matters considerably, here is the problem shader code which is responsible for the incorrect lighting you see in the image. This is also being done before any post processing, so ideally all colors should match perfectly by this point, but they do not.
After doing some reading, there’s a strange piece of math that caught my eye (comments added by me)
// ??? multiply normal map uvs by RefractionBias_FadeDistance_GlowFactor_Free ???
r2.xy = r2.xy * r0.xx;
// ??? use red channel multiplied by fog, multiply by 0.2 and add 1 ????
r0.x = r2.x * 0.200000003 + 1;
The shader is taking the y value of RefractionBias_FadeDistance_GlowFactor_Free (most likely the the fade distance), multiplying it by the clamped red channel of the normal map, and then adding some arbitrary numbers to it. This seems very out of place, why is it reading only the red channel of the normal map? And why is this happening before the normals are converted from tangent space to world/view space?
Using renderdoc, I began reading and modifying the assembly code to get a better idea. Just looking at the base calculated color (albedo + vertex colors) multiplied by this r0.x value gave a very strange result that seems unintentional.
If we were to remove this multiplication to the red channel of the normal map and just give a constant value, it fixes the issue entirely. Inspecting the raw color values of the image also confirms that they are the same.
And here’s what it looks like after all the post processing is done for extra confirmation:
Conclusion:
I am aware that this may not easily solve the problem as there are other factors at play that are out of my control. Such as not knowing what the uncompiled code looks like nor what the original intention of the math was.
But I really hope these findings can help fast track this bug being fixed because it drives me absolutely nuts that the normal map light calculation is incorrect in production, and I would love for nothing more than to see it fixed.
Additional Contents:
Studio Place:
roblox_normal_map_test.rbxl (49.3 KB)
RenderDoc Capture (zipped to fit size restrictions):
roblox_normal_map_capture_closer_four.zip (5.4 MB)