how to encode the depth value in a 32bit RGBA texture

Started by
8 comments, last by zhugel_007 15 years, 4 months ago
Hi, I would like to save depth value in to a texture. Since on some graphic card, floating point texture doesn't support alpha blend, I am planning to write the depth value into a regular 32bit RGBA texture. Since one channel only has 8bit which is too less, I want to use the lower N bit in each channel (RGB, not A, I need A for blending.) to encode the depth value. (N<8) for example, I could use 5bit from each channel to save 15bit depth value. I've tried some methods, but no one seems to be correct. I was wondering what should be the correct way to do this? Thanks a lot!
Advertisement
I actually haven't tried this out, but you should be able to get the lower N bits to store N bits at a time of a given value. If the value you are trying to store is called 'Depth', we'll assume N = 5, and we'll say that the R channel is going to store bits 0-4 of Depth, G channel will store 5-9, and B will store 10-14.

To find the value to store in R, take (frac( Depth / 2^5 ) * 2^5). Then, similarly for B you would take (frac( Depth / 2^10 ) * 2^5), and for G it would be (frac( Depth / 2^15 ) * 2^5). That should properly parse out the bits that you are looking for.

If you are going to use the top few bits of each channel, make sure that you only add values that will affect the upper few bits and ADD the values to the depth values we mentioned above.

Have you tried something similar to this before?
Hi Jason Z,

Thanks for your reply.

Could you please explain more about how to get back the floating point value and the theory behind this method?

Thank you very much!
Well to do what you explained you simply use bitwise operations.
So say your depth value is N*3 bits and we'll call it d.
Then just store it as follows:
mask = (pow(2, N)-1)
r = d & mask
g = (d << N) & mask
b = (d << (N*2)) & mask

To get d back do:
d = r & mask
d = d >> N
d = d+(g & mask)
d = d >> N
d = d+(b & mask)

-CProgrammer
Quote:Original post by CProgrammer
Well to do what you explained you simply use bitwise operations.
So say your depth value is N*3 bits and we'll call it d.
Then just store it as follows:
mask = (pow(2, N)-1)
r = d & mask
g = (d << N) & mask
b = (d << (N*2)) & mask

To get d back do:
d = r & mask
d = d >> N
d = d+(g & mask)
d = d >> N
d = d+(b & mask)

-CProgrammer

That would work ok if he is using integer values, but that is frequently not the case in a pixel shader.

However, the concept is fairly close to what CProgrammer describes - the only real difference is how to get at the bits you want. In the technique I mentioned above, taking the fractional remainder after dividing by the bit range that you are interested in, then multiplying by the range that you can store, you are effectively doing the same thing that CProgrammer did. The only thing I forgot to mention is that you need to put the value into the [0,1] range to store in the render target anyways, so you would only need to do: frac( Depth / 2^x ) where x is determined by which channel you are working on.

To get the depth value back, you would take the compressed R value (which represents the lowest 5 bits, and is stored in the range of [0,1]) then multiply it by 2^5, and then multiply it by the range offset. Since this is the lowest 5 bits, then you don't need to multiply by an offset (2^0 = 1).

Then do the same for G - multiply by 2^5, then multiply by an offset to put the value into the correct range. In this case it would be to multiply by 2^5 to put this number into the range for bits 5-9.

Finally for B, multiply by 2^5 and then 2^10 to put the values into the range of bits 10-14. The reconstructed depth value is simply the sum of these three products.

Once again, I haven't tried this myself, but the theory should be sound - you may lose a little precision here and there in converting between integer and floating point, but it should be a pretty good representation of what you are trying to do.
Hi Jason,

I have tried your method, it seems doesn't work.

here is the shader to encode the depth value:

float scalar = 32.0f;
float3 OutColor;
OutColor.x = frac( DepthValue / scalar ) * scalar;
OutColor.y = frac( DepthValue / (scalar * scalar) ) * scalar;
OutColor.z = (frac( DepthValue / (scalar * scalar * scalar) ) * scalar);

here is the shader to get the value back:

float4 f= tex2D(Tex, In.texCoord);
float scalar = 32.0f;
float depth = f.x *scalar + f.y*scalar*scalar + f.z*scalar*scalar*scalar;

Did i do something wrong?

What i am confused is the floating point value in the shader. Will the bit shifting work for floating point value?

Thanks!
Hello,

The only thing that I see is that the storage doesn't need to be multplied after taking the fraction. I mentioned it in my second post, but forgot it in the first post (sorry...).

What this will do is put the values into the [0,1] range, which is required to store the value in the RGB channels. The reconstruction that you showed is what I had envisioned, so hopefully that works for you!
Thanks Jason for your answer.

But it doesn't work. :(

I guess maybe it is because the result is too small to be saved in the 8bit texture?

OutColor.x = frac( DepthValue / scalar );
OutColor.y = frac( DepthValue / (scalar * scalar) ;
OutColor.z = frac( DepthValue / (scalar * scalar * scalar) ) ;

What is the range of the value that you are trying to store? Is it generated from a linear range, like the view space, or from a projected range?

I am assuming that you have an integer distance - something like a D32 value. Can you try to take a PIX reading (or the OGL equivalent if needed) of the resulting render target so that I can check out what you are getting? Unfortunately I don't have the time to check it out until at least Thursday, but I would be interested to see what you are getting out...
Thanks Jason for your help :)

I was actually using Rendermonkey to test a shader. I will try to find out where is the problem.

Thanks again!

This topic is closed to new replies.

Advertisement