Use of Low-Precision floats in Mobile Shaders breaks materials! (Affects Default Engine Materials)

I’ve had a couple of threads going on the forums and on Answerhub for a few months now which in-detail describes a lot of the problems I’ve been having with materials on the Android mobile platform. In short, materials drawn on the device look nothing like they actually should. Some band and block up and some nodes don’t seem to work at all.

I have finally traced the cause of the problem down the the use of extremely low-precision floats in mobile shaders. In my case, this makes some very simple materials absolutely impossible to achieve on mobile, and this also affects some of the default engine materials. The Default Skydome for instance renders a sun-disc as part of the Shader. If you deploy to a mobile device, you’ll notice the sun disc is no longer there. This is because the ‘radius’ value of the sun disc is so small (0.0003f) that the device just sets it to ‘zero’, and therefore it never draws! Heres the simple material setup I was using to test this (on a skydome mesh).

Long story short, the engine is doing all those extra instructions for no result. If you make the sun radius large enough, it will start to draw (though it will band due to precision issues). If you make it even larger, it’ll start to draw smoothly. I haven’t found any documentation on this nor advice on how to avoid it, but most importantly how to switch this off if possible. Certain shaders or materials in my game need the full precision of floating point in order to draw correctly. It seems as though the range of floats on Android devices is heavily quantized (possibly at the hardware level?), which causes huge problems with many nodes.

This issue can be easily tested in the default engine. Create a new project with the default map and deploy to the device. You’ll notice that there is NO sun-disc on the skydome regardless of what you deploy to. If you then change the material and make the sun-disc size something larger (say 0.01), and deploy to device, the sun disc will render (but of course, will be enormous).

At the opposite end of the spectrum, values that are too large also seem to get quantized and get “a bit screwy” too. In this case I’m creating a soft-edge atmosphere effect using an inverted sphere. I’m generating the ‘Normals’ for the soft-edge using the world-position of the pixels and normalizing it (essentially giving me pixel-perfect spherical normals for a smooth surface). Unfortunately when deployed to a device, the atmosphere becomes solid.

This is for a low-poly sphere with a 6,800 unit radius. If I swap the world-position method and use vertex/pixel normals, everything draws fine but there are a lot of banding artifacts due to the low-precision of the surface normals, and my only option is to massively increase the polycount to avoid it.

So in short, what I need is a way to be able to force full-precision math on some materials for my game, or otherwise face a lot of horrifically ugly artefacts. If I have to make an engine change for this that’s fine, but I really need it!

I use the Custom node to write my own full precision HLSL code. Also, if you are normalizing large values, you should fist shrink the number ( like multiply by 1/1024 ) so that you don’t lose all the precision during the normalize process.

Hey TheJamsh,

I’ve entered a feature request for the ability to force full-precision math on materials. You can track this request here: Unreal Engine Issues and Bug Tracker (UE-34055)

Have a great day!

Noice. Thankyou!

[USER=“160394”]Sean L[/USER] The issue URL no longer exists. Was that ever implemented?

I came here because of a spherical projection problem that was solved by @elecorn suggestion of dividing vectors before normalizing (thanks for that!!). But if we had a setting to enable precision in mobile shaders it would solve a lot of other issues, like the ones demonstrated by @TheJamsh.

This has since been fixed I believe, in that there is now an option to force a shader to use high precision on mobile. I believe it’s in the Mobile settings of the material itself, but if not it might be a global project-wide setting. I’d expect there to be some performance regressions though!

NB: I got around this issue at the time by just adding a flat quad with a circle mask. Not the most elegant solution but it did the job!

It’s in the material settings. Next time I’ll read those one by one. lol
Thanks a lot for the heads up, @TheJamsh!

Fortunately I got around mine without activating full precision too. It’s interesting the math/computing hacks people come up with!

Keep in mind that everything that is calculated at vertex shader uses high precision by default. So most of the time you can get away without forcing full precision by calculating most of the stuff at vertex shader. Also with carefully trying to stay in half float range you can get away even in pixel shader. In your example normalize is the prolem. Absolute world position using centimeters so values are usually quite big. Max half float is 65504. Normalize is doing division by lenght of vector. So its xyz / x^2 + y^2 + z^2. So if sqrt of x,y or z is over sqrt(65504) ~= 256 which is just 2.56meters you get overflow. Just by premultiplying vector before normalization can get you in proper range and everything is fine again.

Awesome explanation, @anonymous_user_fbe2d247. Thanks a lot! I’ll keep that vertex full precision info in mind. I already do a lot of stuff in vertex to save on processing power, even Fresnel.
I suppose you are exactly right about the problematic distance. In the projected objects that seemed to be the start distance of the projection problems.