<p>Just a quick note: The method outlined below is used across api's. There are &quot;snorm&quot; and &quot;unorm&quot; textures types used soley for encoding floating point numbers in the ranges [-1,1] and [0,1] using an 8 bit representation. Every API I have worked with have specific rules about those encodings but what I outlined below works for putting 8 bit floats in a rgba 16 bit texture and is the general conversion process API specs like vulkan recommend when storing 8 bit floats in these ranges. Also there are specific functions in most shader languages like glsl and hlsl that allow direct conversion between int and float without any type conversions. This is the reason I suggest a unsigned type for texture type rather then a float type.</p>
<p>What API? Also, I recommend using a 16bit unsigned texture type to avoid unwanted type conversions when reading the textures.</p>
<p>The basic idea is to multiply the floating point values by 255 then shift and &quot;or&quot; the two values into a single 16 bit value. That value would go into alpha channel. ie</p>
<pre><code>uint16_t packed = static_cast&lt;uint16_t&gt;(low*255.0f) |
       (static_cast&lt;uint16_t&gt;(high*255.0f)&lt;&lt;8);
</code></pre>
<p>Inside the shader read the value back using a type that will not reinterpret the bits such as an unsigned sampler. Extract the two values as 8 bit values, convert them to floats and divide by 255 to get back to a value between 0 and 1.</p>
<p>HLSL has a 16 bit half type you might be able use, but GLSL doesn't have universal support for 16 bit float types so it is generally better to store them in 16 bit integer texture types. The other channels can be handled in a similar way.</p>
<p>Basically you want the raw value read from the alpha channel so that the bits are exactly what you put in the texture. Reading a float 16 into a float 32 will go through a conversion that will reinterpret the bits.</p>

CC BY-SA 4.0
2024-10-21T17:22:31.670Z
The API is actually the Unreal Engine 5 RHI, so it will vary between DX12, DX11 and the various gen9 console APIs. Unfortunately I can't change the texture format here, it really has to be a `R16G16B16A16_FLOAT`
Max
CC BY-SA 4.0
2024-10-21T18:03:25.230Z
It is doable, but you basically have to hand write a conversion function. flt32 to uint (hlsl has a function for this) then a hand written 32 bit float representation to a 16 bit float representation then finally extract the values.  You might also consider going to a 2 channel 32 bit format. Under unity I think it is called RGfloat or RGint. That would give relatively easy access to all 5 values.
pmw1234
CC BY-SA 4.0
2024-10-19T21:23:44.053Z
2024-10-21T14:20:10.670Z
2024-10-21T14:20:10.670Z
pmw1234
<p>I have two 32bit floats that I calculate in a pixel shader which represent a texture mask. What would be the best way to pack them into the alpha channel of a R16G16B16A16_FLOAT texture without losing too much precision?</p>
<p>I rarely do this type of bit manipulation so I'm not sure how to work around the exponent and the mantissa bits when doing the packing.</p>

CC BY-SA 4.0
2024-10-18T05:03:27.893Z
You want to pack two numbers into 16 bits?
Dmitri Urbanowicz
CC BY-SA 4.0
2024-10-18T07:56:11.577Z
Do the two numbers have a specific range? For example [0, 1]
Thomas
CC BY-SA 4.0
2024-10-18T17:20:34.693Z
@Thomas Yes the range should be in [0, 1] range
Max
CC BY-SA 4.0
2024-10-18T17:24:09.177Z
@DmitriUrbanowicz Yes, I have two 32bit floats that I would like to pack together into 16bits while losing as little precision as possible, I accept the loss of precision for my use case
Max
CC BY-SA 4.0
2024-10-20T07:14:02.110Z
There's no universal best solution for this. Depending on what you do with those numbers, you may get best result with either uniform fixed point representation, or non-linear encoding, or even introducing exponent bits into your 8-bit representation. So what are these numbers exactly in your case?
Dmitri Urbanowicz
CC BY-SA 4.0
2024-10-20T07:23:18.837Z
Or maybe your numbers aren't independent and may benefit from joint representation.
Dmitri Urbanowicz
CC BY-SA 4.0
2024-10-21T17:18:25.293Z
@DmitriUrbanowicz The first number in my use case is actually a depth buffer with some artistic tweaks applied. The second number is a volumetric lighting mask built from a depth buffer.
Max
<shader><fragment-shader>
How to pack two 32bit floats into the alpha channel of a R16G16B16A16_FLOAT texture?
Max
# How to pack two 32bit floats into the alpha channel of a R16G16B16A16_FLOAT texture?

(Asked by: username_0 on 2024-10-17)

I have two 32bit floats that I calculate in a pixel shader which represent a texture mask. What would be the best way to pack them into the alpha channel of a R16G16B16A16\_FLOAT texture without losing too much precision?

I rarely do this type of bit manipulation so I'm not sure how to work around the exponent and the mantissa bits when doing the packing.

**Tags:** shader, fragment-shader

> **Comments:**
> **username_1**: You want to pack two numbers into 16 bits?  
> **username_2**: Do the two numbers have a specific range? For example [0, 1]  
> **username_0**: @username_2 Yes the range should be in [0, 1] range  
> **username_0**: @username_1 Yes, I have two 32bit floats that I would like to pack together into 16bits while losing as little precision as possible, I accept the loss of precision for my use case  
> **username_1**: There's no universal best solution for this. Depending on what you do with those numbers, you may get best result with either uniform fixed point representation, or non-linear encoding, or even introducing exponent bits into your 8-bit representation. So what are these numbers exactly in your case?  
> **username_1**: Or maybe your numbers aren't independent and may benefit from joint representation.  
> **username_0**: @username_1 The first number in my use case is actually a depth buffer with some artistic tweaks applied. The second number is a volumetric lighting mask built from a depth buffer.  

---

## 1 answers

### Answer by username_3 on 2024-10-19. Score: 1

Just a quick note: The method outlined below is used across api's. There are "snorm" and "unorm" textures types used soley for encoding floating point numbers in the ranges [-1,1] and [0,1] using an 8 bit representation. Every API I have worked with have specific rules about those encodings but what I outlined below works for putting 8 bit floats in a rgba 16 bit texture and is the general conversion process API specs like vulkan recommend when storing 8 bit floats in these ranges. Also there are specific functions in most shader languages like glsl and hlsl that allow direct conversion between int and float without any type conversions. This is the reason I suggest a unsigned type for texture type rather then a float type.

What API? Also, I recommend using a 16bit unsigned texture type to avoid unwanted type conversions when reading the textures.

The basic idea is to multiply the floating point values by 255 then shift and "or" the two values into a single 16 bit value. That value would go into alpha channel. ie

```
uint16_t packed = static_cast<uint16_t>(low*255.0f) |
       (static_cast<uint16_t>(high*255.0f)<<8);

```

Inside the shader read the value back using a type that will not reinterpret the bits such as an unsigned sampler. Extract the two values as 8 bit values, convert them to floats and divide by 255 to get back to a value between 0 and 1.

HLSL has a 16 bit half type you might be able use, but GLSL doesn't have universal support for 16 bit float types so it is generally better to store them in 16 bit integer texture types. The other channels can be handled in a similar way.

Basically you want the raw value read from the alpha channel so that the bits are exactly what you put in the texture. Reading a float 16 into a float 32 will go through a conversion that will reinterpret the bits.

> **Comments:**
> **username_0**: The API is actually the Unreal Engine 5 RHI, so it will vary between DX12, DX11 and the various gen9 console APIs. Unfortunately I can't change the texture format here, it really has to be a `R16G16B16A16_FLOAT`  
> **username_3**: It is doable, but you basically have to hand write a conversion function. flt32 to uint (hlsl has a function for this) then a hand written 32 bit float representation to a 16 bit float representation then finally extract the values.  You might also consider going to a 2 channel 32 bit format. Under unity I think it is called RGfloat or RGint. That would give relatively easy access to all 5 values.  

---

