3

I have this code sample from threejs example [http://threejs.org/examples/#webgl_animation_cloth] where a float value is converted to vec4. I have seen this logic on few other forums but no explanation.

  1. Could someone explain what this logic is doing and the use of 256 ?.I understand the bitwise masking and shifting .

I have seen this link too Packing float into vec4 - how does this code work?

  1. It says the vec4 will be stored in a 32 bit RGBA8 buffer finally.
    Since we are passing the depth value into a color buffer , how will opengl know what to do with this ?

  2. Also since vec4 has 4 components , each of 4 bytes making it 16 bytes which makes it 16 * 8 bits , how does this fit into 32 bit RGBA8 ?

         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;
           }
    
        void main() {
            vec4 pixel = texture2D( texture, vUV );
            if ( pixel.a < 0.5 ) discard;
            gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );
        }
    
Community
  • 1
  • 1
Chandan
  • 1,486
  • 2
  • 15
  • 24

2 Answers2

2

Along with @WaclawJasper's answer this code is packing 'depth', a float value (32bits), into 4 8bit values.

gl_FragData[ 0 ] presents an single pixel in a texture, in this case a texture with 8bits per channel (32bits total). If I write

gl_FragData[0] = vec4(0.25, 0.5, 0.75, 1.0);

The texture being written to (assuming it's an 8bit texture) will actually get the values

r = Math.floor(0.25 * 255) = 63;
g = Math.floor(0.5  * 255) = 127;
b = Math.floor(0.75 * 255) = 191;
a = Math.floor(1.0  * 255) = 255;

The actual formula from the spec is effectively

unsignedIntValue = floor(clamp(floatValue, 0., 1.) * (pow(2., numberOfBits) - 1.));

So even though pack_depth returns a vec4 which is floats and gl_FragData is defined as vec4 it will eventually be converted when written to whatever WebGL is currently writing into (the canvas, a renderbuffer, a texture).

If it was writing into a floating point texture pack_depth would be unnecessary. We can infer it's writing to a 8bit RGBA texture because of what pack_depth is doing.

Why is this need at all? Because in WebGL support for floating point textures is optional. So if the user's machine does not support floating point textures and you need floating point like data (like a depth buffer for shadow maps) then packing the data into 8bit textures is one solution.

gman
  • 100,619
  • 31
  • 269
  • 393
1
  1. 256 is the number of different values a 8 bit data can represent. Let us stop working in binary for a second and work in the familiar decimal. If we have 2 channels that can only store 1 digit each (0-9), how do we pack something like 45? Obviously, we pack 5 into one digit and 40/10 or 4 into another digit. Then in our unpack function we just do the reverse: 4*10 + 5 = 45. 256 is something similar to the 10 in our decimal example.

  2. Opengl doesnt, but your own application code can make sense of it.

  3. Im not sure if I understand this part correctly. But the code your shown is packing one float into 8*4 bit RGBA.

WacławJasper
  • 3,284
  • 14
  • 19
  • On my question 3, My understanding is that the vec4 has 4 components : Eg. (1.4, 4.6, 3.8, 44.6) . Since each component is a float , each one takes 4 bytes. Since there are 4 components , vec 4 will need 4 * 4 = 16 bytes totally . This comes to 16 * 8 = 128 bits . But since the RGBA is a 32bit space , how will a 128 bit fit into a 32 bit space ? . I am sure my understanding is fundamentally wrong somewhere here. Please bear with me as I am new to graphics – Chandan Dec 28 '15 at 22:04
  • You are not fitting 128 bit into 32 bit. You are packing >one< float into a vec4. – WacławJasper Dec 29 '15 at 01:25