Packing a float into a texture

Started by
11 comments, last by HippyNerd 18 years, 10 months ago
I know there are probably a couple of ways to do this. But is there a stardard way people use to pack a floating point into a texture. I would like to extract it as a float in a fragment shader. If its a 32 bit int I can cleanly take 8 bits and store them in the RGBA channel, then in my fragment shader do something like vec4 texVal = glTexture1d(tex,value.u); float number = dot(texVal,vec4(1,256,2^16,2^24) (except I'll also have to account for normalisation of the texture values to [-1,1]..gah) Just wondering if there was a way of doing this with a float, or even if I can use some other channel (such as a depth texture). To store the values. (I know above if I can figure out how to pack an int, I can convert my float to an int in the main application and convert it back in the frag shader...however all these conversions will start slowing things down) Thanks,
Advertisement
Well, I know that in D3D you can just make a floating-point texture and then render to that. Can you not do that in OGL?
Well that's exactly what I'd like. So far I havn't been able to find anything in the specs though :-/
It looks like extensiosn such as ATI_TEXTURE_FLOAT allow 32 bit values to be stored in textures, without being clamped.

Not sure how to access these from the other end via GLSL though. Working on that now...
This is the way I do it in my engine. I don't know if there is a better way, but it works very well.

I pack a floating point number into 24 bits (3 channels) of a ARGB texture because I need to keep the alpha for depth testing. If you'd wish to use all 32bits you'd use a similar method, using the extra 8 bits for higher accuracy in the integer or fractional parts, or of course you could just use a 32bit floating point texture. But we are talking about packing here. :)

Here is the packing code:

float3 PackDepth24(float depth){	//	// Calculate the integer and fractional parts of the depth	//	float depthInteger = floor(depth);		float depthFraction = frac(depth);		//	// Calculate the upper and lower 8 bits	//	float depthUpper = floor(depthInteger / 256.0f);		float depthLower = depthInteger - (depthUpper * 256.0f);	//	// Pack the values in a float3 which will be converted to 8bit RGB later	//	return float3(depthUpper / 256.0f, depthLower / 256.0f, depthFraction);}


The above provides 16 bits for the integer part and 8 bits for the fractional part. This obviously allows for a floating point number in the range of 0-65535 with 8 bits of accuracy for the fraction.

Unpacking is performed like so:

float UnpackDepth24(float3 depth){	//	// Unpacking multiplier	//	const float3 unpack = {65536.0f, 256.0f, 1.0f};		//	// Unpack the depth	//	return dot(unpack, depth);}


In my case I didnt need such a large range in the integer part, I only needed from 0-1000. By limiting your range, you can give yourself greater accuracy in the fractional part.

To do this, simply shift upwards like you would for an integer.

The packing code now becomes:

float3 PackDepth24(float depth){	//	// Shift the depth up 6 bits (eg. multiply by 64) so that we get 6 more bits of the fraction	//	float shiftedDepth = depth * 64.0f;		//	// Calculate the integer and fractional parts of the depth	//		float depthInteger = floor(shiftedDepth);		float depthFraction = frac(shiftedDepth);		//	// Calculate the upper and lower 8 bits	//	float depthUpper = floor(depthInteger / 256.0f);		float depthLower = depthInteger - (depthUpper * 256.0f);	//	// Pack the values in a float3 which will be converted to 8bit RGB later	//	return float3(depthUpper / 256.0f, depthLower / 256.0f, depthFraction);}


And the unpacking becomes:

float UnpackDepth24(float3 depth){	//	// Unpacking multiplying	//	const float3 unpack = {65536.0f, 256.0f, 1.0f};		//	// Unpack the sample	//	float unpackedShadowSample = dot(unpack, depth);		//	// Unshift the sample by 6 bits and return	//	return unpackedShadowSample / 64.0f;}


So now you have a floating point number with an integer part that ranges from 0-1024 and 14 bits of accuracy in the fractional part.

There may be another way that preserves the entire sign, mantissa and exponent, but this is the method that best suited me.

Hope that helps.
Thanks heaps Buttza, i'm implementing a method simmilar to your idea now. I hadn't really thought about separating out the fractional part like you have. This is cool.
That looks awfully like fixed-point math to me [smile]

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

superpig, it is. :P The beauty of it is it uses the most bits where they count.

I would still like to see a version which fully preserves a 32, 24 or 16bit floating point number. If its even possible.

I was thinking you may be able to use 'frexp' to get the mantissa and the exponent, then like above, allowing 16bits for the mantissa, 8 bits for the exponent and i guess another 8 bits to store the sign. Though it still isn't a true 32bit float because your wasting 7bits.
What's wrong with just using an fp texture? I haven't had any problems accessing those from GLSL.

If you need to render to an fp texture, you might want to try the RenderTexture library, which you can get from gpgpu.org--it makes the whole process pretty easy by encapsulating all the driver-specific stuff like the fp formats.

(Sorry in advance if I misunderstood the question. ;)

Edit: I guess if you're using hardware that doesn't support fp textures you can't do that, but I'm pretty sure any card you can use GLSL with supports them.
Orin Tresnjak | Graphics ProgrammerBethesda Game StudiosStandard Disclaimer: My posts represent my opinions and not those of Bethesda/Zenimax, etc.
You can render to floating point textures in OpenGL. Atleast on NVIDIA hardware, since ATI doesn't have any support for FBO (pbuffers does not counts).

GL_ARB_texture_float
GL_EXT_framebuffer_object
http://oss.sgi.com/projects/ogl-sample/registry/
[size="1"]Perl - Made by Idiots, Java - Made for Idiots, C++ - Envied by Idiots | http://sunray.cplusplus.se

This topic is closed to new replies.

Advertisement