I've been having nightmares at work this week trying to get Unity's Standard shader, and my own custom car paint shader modded from it, to look good on Android.

The most obvious problem was fractured specular highlights, which I identified as a float precision issue, and solved by changing all the directional math to use float3 instead of half3.

It now looks fine on GLES3, but on GLES2, which we want to support, the glossy reflections coming from reflection probes look terrible, with noisy matte areas all over the place.

I finally tracked this down to the cube map texture lookup itself - different blur levels are stored in different MIP levels, and a textureCubeLod lookup samples the reflection at the right smoothness level. Trouble is, GLES2 doesn't natively support lod texture lookups, so it relies on an extension. If the extension is not found, and it seems most Android devices don't have it, it falls back to a regular texture sample, with the lod parameter becoming a bias. This explains the matte areas - sharp features are causing the tex lookup to be kicked down the mip chain to the rougher maps, and the bias applied from there. From certain angles, a single bit of noise in the normal map is enough to cause the effect.

What I want to know is, does any workaround exist for this? My first thought was to 'bias the bias' using dfdx, dfdy, but unfortunately these are also GLES3 only. I may just have to kill glossy reflections altogether on older droids otherwise.


1 Answer 1


One way would be to break out the mip levels of the cube into a bunch of separate textures, then implement the mip selection and trilinear filtering yourself, by branching based on the LOD value.

That doesn't sound fast, though, given you're targeting older devices. You could speed it up by forgoing trilinear filtering and just using the nearest-neighbor mip level. Gloss values would be effectively quantized to the number of mip levels, but maybe it won't look too bad.

  • $\begingroup$ Thanks Nathan, I was thinking along those lines myself. Unfortunately the cubemaps are engine-supplied per renderer, and Unity's graphics engine is a bit of a black box in a lot of ways. I may just have to bake out my own cubemap per-scene somehow and fall back to that on GLES2. $\endgroup$
    – russ
    Jan 15, 2018 at 4:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.