I'm working on a webgl pixel shader which is writing to a 16 bit floating point buffer (each color channel r,g,b,a is a 16 bit floating point number).

I'm trying to figure out if there are any decent ways to pack the following info into those 16 bits per color channel:

  • normal (x,y,z where z is always negative)
  • uv coordinates (x,y between 0 and 1)
  • material index (an integer. 0 to 3 would be plenty)
  • distance (a positive float)

I could live without distance if needed but the others are required.

I know that I can store just x,y of the normal and calculate z as needed since its always negative (the normal is in screenspace).

That still leaves 5 values to store in four 16 bit floats: normal.xy, uv.xy, material index.

Does anyone have any good methods for making this happen? It would be nice to have the distance too, otherwise I am limited to directional lighting instead of getting point lights as well.

This is in shadertoy if that context changes any answers.

I'll be sampling this buffer with nearest neighbor sampling, so you don't have to worry about it being stored in a way that interpolates well.



1 Answer 1


Assuming you are not going to filter the buffer using HW texture filtering: since you only need 4 values for the material index, pack it in the sign bits of uv x and y.

  • $\begingroup$ Ah cool neat idea. And yeah, I'm just doing nearest neighbor sampling. I'll edit the question to reflect that. $\endgroup$
    – Alan Wolfe
    Feb 19, 2016 at 5:14
  • 1
    $\begingroup$ Given you are storing 0-1 values where, probably, having high precision near 0 is of little value, the exponents of the f16 may be wasted space. Something to think about if you are looking to tighten things up as much as possible. Bit packing can be especially useful in vertex data before any interpolation happens; in one case, I store texture array coordinates in 4 bytes and use the alpha channel to add extra bits to x & y; this gives me texel precise addressing for up to a 4k x 4k x 256 texture. $\endgroup$ Feb 19, 2016 at 5:29
  • $\begingroup$ Good to know! In my case unfortunately all I have is a full screen pixel shader, I don't have geometry. But, on the plus side, the buffer can store values outside of 0 to 1. It's a full 16 bit floating point number which is nice. $\endgroup$
    – Alan Wolfe
    Feb 19, 2016 at 5:33
  • 1
    $\begingroup$ Basically, the exponent scales an otherwise fixed point value (the mantissa) with the size of the value. If you don't benefit from that, then fixed point numbers (ints or GL "norms") utilize the bits better. It may not apply in your case for any number of reasons (does WebGL support integers? I'm out of date). I do use mod and div to unpack when I need to (also, before GLSL supported ints). $\endgroup$ Feb 19, 2016 at 13:28
  • 1
    $\begingroup$ I can rant on floats, so I won't go there. Fixed point can be a better choice (ask if more precision near 0 is useful - with positional values fp is often more of a convenience; in a linear color space, fp matches our perceptual system beautifully). Just something to consider when concerned with bandwidth. $\endgroup$ Feb 19, 2016 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.