Float / Z-Buffer problem

Started by
8 comments, last by Evil Steve 17 years, 6 months ago
Hello, I've got a little problem with calculating z-values. I use glTranslatef, so I'll need a float value in the end. My idea was to calculate an int value up to 23 bit first, and then write that value right into the float. I didn't expect it would work even when the value gets HIGHER than 23 bit! That's very strange and at first I was happy that it worked, but in the end it did NOT work properly. Here is a little test program I wrote that shows the full range between 0.0 and 1.0, and you can see that all numbers are valid and also unique.

void test() 
{ 
    float f = 0; 
    float previous = 0; 
    int err = 0; 

    for(int i=0; i<0xFFFFFFFF; i++) 
    { 
        __asm 
        { 
            mov eax, i 
            mov f, eax 
        } 

        if (previous > f) 
        { 
            printf("the previous number was greater than this one\n"); 
            err++; 
        } 

        if (f > 1.0) 
        { 
            printf("\nreached 1.0\n"); 
            break; 
        } 

        if (i % 50000 == 0) 
            printf("%8X - %1.58f\n", i, f); 

        previous = f; 
    } 

    printf("\n\n%i errors.\n\n", err); 

    exit(0); 
}
If you run it, you'll see that the first numbers are REALLY small, but they should be valid floats, right? Then the program stops at 0x3F7FF370 because that would be the point where the float 1.0 would be topped. Now I put that "logic" (pushing the int-bytes right into the float) into my game and put the resulting float number into the glTranslatef function, but sadly it seems that he assumes those small float values to be ZERO. I can't understand neither of those cases, though the first case (bigger number than 23 bit but working though) would be cooler to be reality ;) What's wrong there? And is there a working way to calculate floats using a < 23 bit number?
Advertisement
I've been thinking and testing about this now a little bit...

Could it be that the depth buffer simply can't handle floats that enter some microscopic range?
I'm not exactly sure what your question is, but typically floats that are very close to each other are considered the same. Usually you use some sort of epsilon value that signifies the amount of floating point error you're willing to tolerate, and you use this to compare floats. So typically a very small float value is considered to be 0 if the value is smaller than the specified epsilon, which is usually ok because of the minute difference.

What's the problem with small values equaling 0?
Well I use glTranslatef in an orthogonal projection. Now I want to use the z-value as a depth buffer value.

My float values are correct now, and they are unique and can't be assumed to be the same, as I'm setting the bit-pattern for the mantissa by myself. So I use that float which is extremely small, but depth sorting won't work.

Then I multiplicate the float with a very huge number (at least 20 zeros or something) and then suddenly it works, when my float numbers are in the range of 0,0000002 or something.

But as I said, I set the float completely manually, so normally there shouldn't be values which could be regarded as "the same" by the machine, as they are completely different.
I might be a little out of my depth here (excuse the pun), since I'm a software renderer guy not really a HW renderer guy, but...

Are you using a z-buffer or a 1/z-buffer? IIRC the later can give nicer results.

Does your depth buffer use 32-bit or 16-bit values?


Also, why resort to using asm when you could simply use a union, or do a reinterpret_cast?
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Boy are you sure you are not using denormalized numbers, and OpenGL could work with them?

I would put 1 to the XMM0 register and simply increase it as needed. Or you might like to set the exponent to verify if there are not problems with denormalized numbers.

BTW 0xFFFFFFFF is max size of int. If you need 16 bits do 0xFFFF. And you also might like to use int32, or something like that to be sure your int is 32 bits.
Quote:Original post by ZeHa
Could it be that the depth buffer simply can't handle floats that enter some microscopic range?


Do you have a 32-bit, floating point depth buffer? IIRC, depth buffers tend to be 16- or 24- bit fixed point - extremely small floating point numbers like yours will thus alias to zero.

Also, there is no need to use assembly language for this:
float f = 0; int i = 0;__asm {     mov eax, i     mov f, eax }
the conversion can be performed in a platform-independent way by doing:
f = *reinterpret_cast<float *>(&i);
Well I used a union now, it's right that the asm way is unneccessary, but I wasn't used to unions until now ;)

A friend of mine explained me how to build up a correct float and I tried it, I used mantissa values between 0 and 7FFFFF and an exponent of 1 (resulting in -126). Now the numbers should be dead small.

The problem now is, when I use those floats for the depth buffer, they are regarded as if they were zero. If I shift the comma by multiplicating, until they are in a range approx. 0,000002 suddenly it works and my polygons are perfectly sorted.
IIRC, depth buffers tend to be 16- or 24- bit fixed point

Well I thought if I'm able to define the near/far range in glOrtho, then I'll also be able to use the full range with 32 bit floating point numbers.

But if that's not the case, how would the 16/24 bit be mapped to the individually set near/far values?
Quote:Original post by ZeHa
Well I thought if I'm able to define the near/far range in glOrtho, then I'll also be able to use the full range with 32 bit floating point numbers.
Hardware Z-buffers use 24 or 32 bits, yes. They tend not to store it in standard IEEE floating point format though, because of the precision issues. Typically, there's something like 75% of the Z-range used to represent the first 20% of the viewing frustum, which means you don't get visible artifacts close up. Further away they tend not to matter. This is also the reason you shouldn't have a near clip of 0.000001f or something else tiny; it'll change the way Z-values are allocated.

Incidently, this tends to be why you can't lock Z-buffers unless you explicitly tell the driver that you want to, and it also tends to be very expensive.

This topic is closed to new replies.

Advertisement