diff --git a/README.md b/README.md index fa792fb..e822f3b 100644 --- a/README.md +++ b/README.md @@ -88,10 +88,6 @@ Unpacks a packed `vec4` into a single floating point value. ## Credits -This routine was originally written by @ultraist. You can find his blog here: http://ultraist.hatenablog.com/ +Originally based on a routine by @ultraist. You can find his blog here: http://ultraist.hatenablog.com/ -The post describing this code was published here: - -[WebGL GPGPUでfloatの結果を得る](http://ultraist.hatenablog.com/entry/20110608/1307539319) - -The npm entry and glslify packaging are currently maintained by Mikola Lysenko. \ No newline at end of file +Newer version rewritten by Mikola Lysenko. MIT License (c) 2014 \ No newline at end of file diff --git a/example/example.js b/example/example.js index f7aa213..13f7e1a 100644 --- a/example/example.js +++ b/example/example.js @@ -26,12 +26,16 @@ void main() {\ inline: true })(gl) -function render() { - var num = Math.random() +var FLOAT = new Float32Array(1) +var BYTE = new Uint8Array(FLOAT.buffer) + +function render(num) { + //Convert to float + FLOAT[0] = num //Draw shader shader.bind() - shader.uniforms.f = num + shader.uniforms.f = FLOAT[0] triangle(gl) //Read back the float @@ -39,6 +43,21 @@ function render() { gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buffer) var unpacked = unpackFloat(buffer[0], buffer[1], buffer[2], buffer[3]) + FLOAT[0] = num + console.log('in bits:', BYTE[3], BYTE[2], BYTE[1], BYTE[0]) + console.log('out bits: ', buffer[0], buffer[1], buffer[2], buffer[3]) + //Log output to console - console.log("expected:", num, "got:", unpacked) -} \ No newline at end of file + if(FLOAT[0] === unpacked) { + console.log('ok') + } else { + console.log('fail, expected:', FLOAT[0], 'got:', unpacked) + } +} + +for(var i=-300; i<300; ++i) { + render(i) +} +for(var j=0; j<100; ++j) { + render(Math.random()) +} diff --git a/index.glsl b/index.glsl index 4cd62ce..951f851 100644 --- a/index.glsl +++ b/index.glsl @@ -1,28 +1,42 @@ -highp vec4 nvjs_encode_float(highp float v) { - highp vec4 c = vec4(0.0, 0.0, 0.0, 0.0); - if (v < 0.0) { - c[0] += 64.0; - v = -v; +#define FLOAT_MAX 3.40282347e+38 +#define FLOAT_MIN 1.17549435e-38 + +lowp vec4 encode_float(highp float v) { + highp float av = abs(v); + + //Handle special cases + if(av < FLOAT_MIN) { + return vec4(0.0, 0.0, 0.0, 0.0); + } else if(v > FLOAT_MAX) { + return vec4(127.0, 64.0, 0.0, 0.0) / 255.0; + } else if(v < -FLOAT_MAX) { + return vec4(128.0, 64.0, 0.0, 0.0) / 255.0; } - highp float f = 0.0; - highp float e = ceil(log2(v)); - highp float m = v * exp2(-e); - if (e < 0.0) { - e = -e; - c[0] += 128.0; - } - c[0] += e; - m *= 255.0; - f = floor(m); - c[1] = f; - m -= f; - m *= 255.0; - f = floor(m); - c[2] = f; - m -= f; - m *= 255.0; - c[3] = floor(m); - return c * 0.003921569; + + highp vec4 c = vec4(0,0,0,0); + + //Compute exponent and mantissa + highp float e = floor(log2(av)); + + //Compute higher order byte + highp float ebias = e + 127.0; + c[0] = floor(ebias / 2.0); + ebias -= c[0] * 2.0; + c[0] += 128.0 * step(0.0, -v); //Compute sign bit + + //Unpack mantissa + highp float m = av * pow(2.0, -e) - 1.0; + c[1] = floor(128.0 * m); + m -= c[1] / 128.0; + c[2] = floor(32768.0 * m); + m -= c[2] / 32768.0; + c[3] = floor(8388608.0 * m); + + //Add last bit of exponent back + c[1] += floor(ebias) * 128.0; + + //Scale back to range + return c / 255.0; } -#pragma glslify: export(nvjs_encode_float) \ No newline at end of file +#pragma glslify: export(encode_float) \ No newline at end of file diff --git a/index.js b/index.js index 6948c0a..2ff7048 100644 --- a/index.js +++ b/index.js @@ -1,56 +1,12 @@ -//Adapted from here: http://ultraist.hatenablog.com/entry/20110608/1307539319 module.exports = decodeFloat -var exp2_table = [ - 2.168404E-19, 4.336809E-19, 8.673617E-19, 1.734723E-18, - 3.469447E-18, 6.938894E-18, 1.387779E-17, 2.775558E-17, - 5.551115E-17, 1.110223E-16, 2.220446E-16, 4.440892E-16, - 8.881784E-16, 1.776357E-15, 3.552714E-15, 7.105427E-15, - 1.421085E-14, 2.842171E-14, 5.684342E-14, 1.136868E-13, - 2.273737E-13, 4.547474E-13, 9.094947E-13, 1.818989E-12, - 3.637979E-12, 7.275958E-12, 1.455192E-11, 2.910383E-11, - 5.820766E-11, 1.164153E-10, 2.328306E-10, 4.656613E-10, - 9.313226E-10, 1.862645E-09, 3.725290E-09, 7.450581E-09, - 1.490116E-08, 2.980232E-08, 5.960464E-08, 1.192093E-07, - 2.384186E-07, 4.768372E-07, 9.536743E-07, 1.907349E-06, - 3.814697E-06, 7.629395E-06, 1.525879E-05, 3.051758E-05, - 6.103516E-05, 1.220703E-04, 2.441406E-04, 4.882812E-04, - 9.765625E-04, 1.953125E-03, 3.906250E-03, 7.812500E-03, - 1.562500E-02, 3.125000E-02, 6.250000E-02, 1.250000E-01, - 2.500000E-01, 5.000000E-01, 1.000000E+00, 2.000000E+00, - 4.000000E+00, 8.000000E+00, 1.600000E+01, 3.200000E+01, - 6.400000E+01, 1.280000E+02, 2.560000E+02, 5.120000E+02, - 1.024000E+03, 2.048000E+03, 4.096000E+03, 8.192000E+03, - 1.638400E+04, 3.276800E+04, 6.553600E+04, 1.310720E+05, - 2.621440E+05, 5.242880E+05, 1.048576E+06, 2.097152E+06, - 4.194304E+06, 8.388608E+06, 1.677722E+07, 3.355443E+07, - 6.710886E+07, 1.342177E+08, 2.684355E+08, 5.368709E+08, - 1.073742E+09, 2.147484E+09, 4.294967E+09, 8.589935E+09, - 1.717987E+10, 3.435974E+10, 6.871948E+10, 1.374390E+11, - 2.748779E+11, 5.497558E+11, 1.099512E+12, 2.199023E+12, - 4.398047E+12, 8.796093E+12, 1.759219E+13, 3.518437E+13, - 7.036874E+13, 1.407375E+14, 2.814750E+14, 5.629500E+14, - 1.125900E+15, 2.251800E+15, 4.503600E+15, 9.007199E+15, - 1.801440E+16, 3.602880E+16, 7.205759E+16, 1.441152E+17, - 2.882304E+17, 5.764608E+17, 1.152922E+18, 2.305843E+18 -] +var UINT8_VIEW = new Uint8Array(4) +var FLOAT_VIEW = new Float32Array(UINT8_VIEW.buffer) function decodeFloat(x, y, z, w) { - var m = y * 3.921569E-03 - + z * 1.537870E-05 - + w * 6.030863E-08 - var e = x - var i_sign = 0 - if (e & 0x80) { - i_sign = 1 - e &= ~0x80 - } - if (e & 0x40) { - m = -m - e &= ~0x40 - } - if (i_sign) { - e = -e - } - return m * exp2_table[e + 62] -} \ No newline at end of file + UINT8_VIEW[0] = w + UINT8_VIEW[1] = z + UINT8_VIEW[2] = y + UINT8_VIEW[3] = x + return FLOAT_VIEW[0] +}