interestingly, the last week i wrestled with lots of template magic to provide four base types:
* fixed_point_t<typename int_t, unsigned fractional_bits> + some basic operations, like casting float-to-fixed (and vice versa), fixed-to-fixed (and vice versa
), and int-to-fixed (...), (the fix-fix-cast operations use round-to-even)
* vector_t, point_t, normal_t with each having parameters <typename scalar_t>
Now I can switch easily between spheres (+ co.) that use integer centers and double radius, or float center and fixed-point radius, etc. etc. .
The implementation will (at least in the beginning) be as simple as (further examining sphere example) calculating the difference between sphere-center and ray-zero, that difference (with vector
oint-point, as in PBRT) will then be cast to vector<float|double>, and the canonical ray/sphere-intersection is done.
Example:
sphere.center ::= ray.position ::= vector<fixed_point<int64_t, 16> >
ray.direction ::= vector<float>
vector<float> diff = vector_cast<vector<float> > (sphere.center-ray.position);
...
So basically like you mentioned. Best thing it runs already, both with float and int64_t as the scalar-type for points.
The cool thing now is, all the redshift-code can just use point_t, which is a typedef of point_t<int64_t>, and the other types (vector, normal) are typedefed so they use float. This all will yield no problems, as point_t only supports addition and subtraction:
vector = point [+-] point
point = point [+-] vector
(operands can be exchanged)
Of course there could be problems if I really typedef normal_t to be of integer-type, but that would anyways not what is intended.
As for single precision: In the beginning of picogen I switched to double because you already suffer upon intersection with a sphere with radius=100,000.0f (I got very ugly artifacts, like with hardware zbuffering). Clearly, these where also epsilon problems, but then I did not want to wrestle with epsilons being dependent on the magnitude (which would have been needed).