Basic said:
Reverend said:
For example, in a shader that says something like 1/square(magnitude(LightPosition-TexelPosition)), unless your light and texel are real close together, that subtract can easily have many bits of lsb error, and squaring that quantity then doubles the number of error bits.
You got it the wrong way. It's when the light and the texel are close to each other relative to the distance to origo you've got problems.
My bad, you're right. This case actually occurs, for example, when a player is in a small room with the lightsource, and the room is far away from the origin of the world.
And squaring it doesn't double the number of error bits, but it can double the error (meaning adding one error bit).
Yup, you're right.
But it's bad (wrt performance and precision) to do the calc that way. It's better to write square(magnitude(X)) as dot(X,X), or to do the latter in an inline function magnitude2(X).
The approaches are pretty similar. The problem occurs just as much in 1D as in 3D, for example (a-b)^2 where a and b are both large numbers that are almost equal.
The subtraction lose as many bits of precision as is needed to store the ratio between light to tex distance, and light/tex to origo distance. Ie, if light-tex distance is one unit, and they are 32 units away from origo, then you'll lose 5 bits. If you lose to much precision (which certainly is possible if you're not careful), you can gain it back by moving part of the subtraction to VS (using its higher precision). The VS can make sure that PS get a local coordinate system for (lightPos-texPos).
Yes, you can definitely reduce the amount of error by arranging calculations as carefully as possible, and moving certain things into the vertex shader (or doing them on the CPU in double-precision and passing the final results down to a VS). This all requires more programming effort of course. It also limits the generality of what you can set up. When you are writing a single pixel shader, you can look at the overall algorithm and manage its precision carefully.
But for example if you're writing a bunch of shader components that can be combined together to form pixel shaders (for example, a specular lighting module, a spherical harmonic module, attenuation modules, etc), you can't be so sure about how much precision will be lost as data is passed between the different routines, given that they can be plugged together arbitrarily, by artists. This is the essence of what engines are meant to do, not to provide a single shader or single feature, but a bunch of shaders that the content creators can piece together to achieve the effect they want.
I'm sure for all that I've written thus far, there is a spirit of the arguments for FP24 (or other hardcoded hardware limitations in general) being always something like "for all the shaders we can think of, this isn't a problem. If you think there's a problem, send us a shader and we'll show you how to work around our limitations with it.". The flaw in that logic is that it assumes isolated pieces of shader code matter, but what really matters is the set of all possible shaders an engine can generate. If you look at Max or Maya's lighting models and material systems, they're all along these lines, not a single shader with a few knobs you can twiddle with, but general frameworks for combining arbitrary other shader functionality.