Reputation: 53335
I am trying to study shadow mapping in WebGL. I see same piece of shader code copied in various libraries and examples that achieve this. However nowhere did I find the explanation of how it works.
The idea is to save a depth value (a single float) into the color buffer (vec4). There is a pack function that saves float to vec4 and unpack function that retrieves the float from vec4.
vec4 pack_depth(const in float depth)
{
const vec4 bit_shift = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);
const vec4 bit_mask = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);
vec4 res = fract(depth * bit_shift);
res -= res.xxyz * bit_mask;
return res;
}
float unpack_depth(const in vec4 rgba_depth)
{
const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);
float depth = dot(rgba_depth, bit_shift);
return depth;
}
I would have imagined that packing a float into vec4 should be a trivial problem, just copy it into one of the 4 slots of vec4 and leave others unused. That's why the bit shifting logic in above code is puzzling to me.
Can anyone shed some light?
Upvotes: 23
Views: 11556
Reputation: 43662
If you're interested into the nitty-gritty details of how these routines work I suggest you to read my blog post. I'm adding some details here on how that code works and to address some possible use cases.
As you probably figured out, that code is encoding a normalized float value to a vec4
. OpenGL ES 2.0 or WebGL (at the time of writing), might make use of those pack/unpack routines to provide 32 bit precision floating points via RGBA8
textures (more on this in the spec).
Even with the extension posted by Mikael (OES_texture_float
) it might be necessary (for debugging purposes for instance) to dump full 32 bit precision normalized floating points and as described in the spec readPixels
is currently limited by the following
Only two combinations of format and type are accepted. The first is format RGBA and type UNSIGNED_BYTE. The second is an implementation-chosen format.
Upvotes: 5
Reputation: 579
In addition to the answer above, you might be interested in the floating point texture extension described here:
http://www.khronos.org/registry/webgl/extensions/OES_texture_float/
Note that there are hardware/software setups out there where this extension doesn't exist/run, but if it do it sure is a good extension. My experience is that it's fast as well. If you use this, you can use the remaining three channels to store other information, such as color from a projected texture.
Upvotes: 4
Reputation: 473407
It's not storing a GLSL float
in a GLSL vec4
. What it's doing is storing a value in a vec4
which, when written to an RGBA8 framebuffer (32-bit value) can be read as a vec4
and then reconstituted into the same float
that was given previously.
If you did what you suggest, just writing the floating-point value to the red channel of the framebuffer, you'd only get 8 bits of accuracy. With this method, you get all 32-bits working for you.
Upvotes: 17