Your normal map looks fine in the viewport. Then the camera moves and the highlight shimmers. That is specular aliasing, and it is fixable.
What Causes It
Specular aliasing is a sampling problem. Your pixel shader evaluates the BRDF at one fixed point on screen per pixel. When a surface has high-frequency normals, the specular contribution can vary wildly across a tiny area. When the camera or object moves, that sample point shifts, and the highlight flickers. The smoother your material, the worse it is; a tight specular lobe amplifies every small normal deviation.
There are two distinct sources of this aliasing.
Normal map aliasing is what most people think of first. When a normal map mips down, a standard box filter averages the normals but the roughness map has no idea; it still holds whatever the artist painted. The result at distance is flat normals combined with very high gloss: mirror-like shimmering. Sledgehammer's Danny Chan framed it cleanly: normals and gloss both represent geometric information at different scales, and independently mipmapping them breaks their relationship.
Geometric specular aliasing happens even without normal maps. Dense meshes have normals interpolated across triangles, and near silhouettes that interpolation can overshoot, producing normals that point "behind" the surface, causing bright specular sparks. Vlachos at Valve flagged this in the GDC 2015 VR talk: the camera never stops moving in VR, so any temporal instability becomes immediately visible.
Why It Gets Worse at Grazing Angles
At steep viewing angles, anisotropic texture filtering samples a higher-resolution mip, one that hasn't had the roughness correction applied yet. The aliasing creeps back in exactly at the Fresnel peak. Ready at Dawn noted this explicitly: their technique breaks down at grazing incidence, where GGX deviates from the Gaussian assumption their maths relies on. They worked around it by forcing trilinear (not anisotropic) filtering on the roughness map, accepting some over-smoothing as the lesser evil.
A related problem: when normal map texels point away from the camera (n·v ≤ 0), the geometry term creates a sharp discontinuity. Ready at Dawn found it better to shade those pixels as front-facing rather than clamp to zero; the grazing falloff still looks correct, with no hard edge.
The Fix
There is no single universal solution, but the approaches cluster into a few families.
Gloss-from-normal-variance (offline precompute) is the pragmatic sweet spot for most shipped games. The core idea, from Toksvig (2004), is that the length of an averaged normal encodes how much variance the individual normals had. A flat normal map averages to a unit vector; a noisy one averages to something shorter. You map that shortening to a roughness increase at mip generation time, so each lower mip has roughness baked in that accounts for all the normals under that texel.
Sledgehammer (COD: WWII) built this specifically for GGX. They importance-sampled the GGX NDF across 255 gloss steps to generate a lookup table mapping gloss to "shortened normal length," then used that table during mip generation. Normal direction and gloss are stored separately (two-channel normal + one-channel gloss) since floating-point shortened normals were too memory-intensive.
Ready at Dawn (The Order: 1886) did the same thing but more rigorously, using a von Mises-Fisher distribution to fit an NDF per texel and convolving it with the BRDF in the frequency domain. The effective roughness formula is α' = sqrt(α² + 1/(2κ)) where κ is the vMF concentration. In practice the results are very similar to Toksvig. Both approaches have zero runtime cost. All the work is in the offline compositing pipeline.
Valve (Advanced VR Rendering, GDC 2015) tackled both problems. For normal maps, they stored a 2D anisotropic roughness value per mip (std dev of tangent normals in X and Y), which fits neatly into a DXT5 RGBA texture. For geometric aliasing on dense meshes, they compute a roughness floor from ddx/ddy of the interpolated vertex normal; if the normal changes fast across a pixel, roughness goes up to match. For silhouette sparkling specifically, they interpolate the vertex normal twice (standard and centroid) and pick the centroid version whenever the standard normal has over-interpolated past unit length.
LEAN / CLEAN mapping stores the full covariance of the normal distribution per texel, giving true anisotropic NDF filtering for any BRDF. Quality is excellent but memory and precision requirements are high; both Sledgehammer and Ready at Dawn evaluated it and passed.
Practical Takeaway
For most projects: generate roughness mips to account for normal variance. It's a pipeline change, not a shader change, and eliminates the worst of the shimmering at zero runtime cost. Pair it with the ddx/ddy geometric roughness term to cover dense meshes and silhouettes.
Artstyle is a real factor; a stylized game can tolerate more aggressive roughness floors than a photorealistic one. For VR, treat it as non-optional: aliasing that's mildly annoying on a monitor becomes actively unpleasant when the camera never stops moving and pixels-per-degree are already low.
Referenced Papers
- Alex Vlachos, Valve: Advanced VR Rendering, GDC 2015
- Danny Chan, Sledgehammer Games: Material Advances in Call of Duty: WWII, SIGGRAPH 2018
- David Neubelt & Matt Pettineo, Ready at Dawn: Crafting a Next-Gen Material Pipeline for The Order: 1886, SIGGRAPH 2013
- Michael Toksvig: Mipmapping Normal Maps, 2004
© 2026 Stefan Groenewoud - All views are my own, not those of my employer.

