Sign in to follow this  

Encode float to RG or RGB

Recommended Posts

Ceniza    105
I was quite interested in the idea of encoding a depth value [0, 1] to either RG (16 bits) or RGB (24 bits) using GLSL. I found a couple of posts about it, and there seems to be a rather accepted algorithm as well (even though it encodes in the range [0, 1)). After analyzing the algorithms, I found that none of them was fully using the range provided by RG, RGB or RGBA. The most common check is that an encoded value of all 1s is decoded as a number bigger than 1. In other words, the maximum RG/RGB/RGBA encoded value and a bunch more are unused.

There was one solution that followed the right path, but failed in the implementation.

For reference, here are the links I found in the process:

Implementing this with integer logic is rather straight forward:
[source lang="cpp"]// Encode:
R = value &0xff;
G = (value >> 8) & 0xff;
B = (value >> 16) & 0xff;

// Decode:
value = (B << 16) | (G << 8) | R;[/source]
The trick turns then into converting that to floating point logic that can be used in GLSL. Well, here is the solution to encode it in 16 bits:
[source lang="cpp"]// Encode:
const float max16int = 256.0 * 256.0 - 1.0;
value *= max16int;
vec2 result = floor(value / vec2(256.0, 1.0));
result.g -= result.r * 256.0;
result /= 255.0;

// Decode
const float max16int = 256.0 * 256.0 - 1.0;
float result = 255.0 * dot(value, vec2(256.0, 1.0)) / max16int;[/source]
The same idea can be used to encode the value in 24 bits:
[source lang="cpp"]// Encode:
const float max24int = 256.0 * 256.0 * 256.0 - 1.0;
value *= max24int;
vec3 result = floor(value / vec3(256.0 * 256.0, 256.0, 1.0)); -= result.rg * 256.0;
result /= 255.0;

// Decode:
const float max24int = 256.0 * 256.0 * 256.0 - 1.0;
float result = 255.0 * dot(value, vec3(256.0 * 256.0, 256.0, 1.0)) / max24int;[/source]
Find attached the results of the same scene where the encoded depth is used as the fragment color. One uses RG, the other RGB.
Notice how the plane color seems to repeat in a color band fashion. The farthest red is (255, 3, 0), the next one is (254, 3, 0), then (253, 3, 0). The algorithms also encode 1 as all 1s, so no values are wasted.

As a bonus, I include a greenscale version of the scene that uses a very simple sort of encoding that roughly makes the input value linear (again, just roughly), using the following code:
[source lang="cpp"]result = pow(value, 128.0);[/source]
[b]NOTE:[/b] This way of encoding values does not play nicely with filtering. As an example consider the RG encoding of 0.5 (which cannot be represented exactly) and that of linearly interpolating between 0 and 1 (byte values are used for clarity):

0.5: (127, 255)
0.0: (0, 0)
1.0: (255, 255)
0.5 (interpolated): (127, 127)

The real encoding of 0.5 and the interpolation of it are close, yet off (by 128 in G). If "close" is good enough, then you may use interpolation.

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this