7

I have Float32Array textures which can be displayed through WebGL correctly. However, when I tried to convert them into Uint16Array, the problem occurs.

enter image description here

Here is my conversion part.

var _floatToHalfFloat = function(input, offset) {
    var largestHalf = Math.pow(2, 30-15) * (1 + 1023/1024);
    var m = new ArrayBuffer(4);
    var n = new Float32Array(m);
    var o = new Uint32Array(m);
    var f = 0.0;
    for (var i = input.length - 1 - offset; i >= 0;i--) {
        n[0] = input[i];
        f = o[0];
        // fast conversion of half
        // ref : ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf

        if (isNaN(input[i])) {
            input[i] = 0x7fff;
        } else if(n === Infinity || n > largestHalf) {
            input[i] = 0x7c00;
        } else if(n === -Infinity || n < -largestHalf) {
            input[i] = 0xfc00;
        } else if(n === 0) {
            input[i] = 0;
        } else {
            input[i] = ((f>>16)&0x8000)|((((f&0x7f800000)-0x38000000)>>13)&0x7c00)|((f>>13)&0x03ff);
        }
    }
    return new Uint16Array(input);
};
0

1 Answer 1

1

We can see saturated colors (full red, green and/or blue) in the converted image when reaching black color in the original image. I think the function doesn't work very well near 0.

I have done a quick implementation of wikipedia explanation of norm of the float 16 bits.

<html>
<head>
<script>
var _floatToHalfFloat = #### YOUR FUNCTION HERE CUT ####

var _halfFloatToFloat = function(hf) {
  var m = new ArrayBuffer(2);
  var n = new Uint16Array(m);
  n[0] = hf;
  var sign = n[0] & 0x8000;
  var exp = (n[0] >> 10) & 0x1F;
  var mant = n[0]& 0x03FF;
  document.getElementById('sign').innerHTML += sign+" - ";
  document.getElementById('exp').innerHTML += exp+" - ";
  document.getElementById('mant').innerHTML += mant+" - ";
  if (exp == 0x1F) {
    return 1.0 * Math.pow(-1, sign) * Infinity;
  } else if (exp == 0) {
    return Math.pow(-1, sign) *
           Math.pow(2, -14) *
           (mant / Math.pow(2, 10));
  } else {
    return Math.pow(-1, sign) *
           Math.pow(2, exp-15) *
           (1+(mant / Math.pow(2, 10)));
  }
};

document.addEventListener("DOMContentLoaded", function(event) {
  var input = new Float32Array(8);
  input[0] = 2.5;
  input[1] = 0.25;
  input[2] = 0.025;
  input[3] = 0.025;
  input[4] = 0.0025;
  input[5] = 0.00025;
  input[6] = 0.000025;
  input[7] = 0.0;

  var i, s = "Value before = ";
  for (i = 0; i < input.length; i++) 
    s += input[i] + " - ";
  document.getElementById('res1').innerHTML = s;
  var output = _floatToHalfFloat(input, 0);
  s = "Value after = ";
  for (i = 0; i < output.length; i++) 
    s += _halfFloatToFloat(output[i]) + " - ";
  document.getElementById('res2').innerHTML = s;
});
</script>
</head>
<body>
  <span id="res1">result</span></br>
  <span id="res2">result</span></br>
  </br></br></br>
  <span id="sign">signs =</span></br>
  <span id="exp">exponents =</span></br>
  <span id="mant">mantissas =</span></br>
</body>
</html>

The test results are shown below :

Value before = 2.5 - 0.25 - 0.02500000037252903 - 0.02500000037252903 - 0.0024999999441206455 - 0.0002500000118743628 - 0.00002499999936844688 - 0 -
Value after = 2.5 - 0.25 - 0.024993896484375 - 0.024993896484375 - 0.002498626708984375 - 0.0002498626708984375 - Infinity - 2 -



signs =0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -
exponents =16 - 13 - 9 - 9 - 6 - 3 - 31 - 16 -
mantissas =256 - 0 - 614 - 614 - 286 - 24 - 653 - 0 - 

This shows that the 2 last information are not coherent. 0.000025 is transformed into Infinity (rather than 0?) and 0 itself is transformed to 2. This doesn't appear to be correct. When you want to code a zero "wikipedia says" your mantissa AND your exponent should be zero. In the code you provided the mantissa is zero but the exponent is 16 which leads to 2 (2^(16-15)).

After tweaking a bit your function it appears that all cases are treated as normal one. This is due to a bug in your if statements. So instead of having :

} else if(n === 0) {
    input[i] = 0;
}

You want probably do something like that :

} else if(n[0] === 0) {
    input[i] = 0;
}

And the same for all uses of n variable. But you still have the underflow problem.So may be you can find acceptable to do :

} else if(Math.abs(n[0]) < 0.0001) {
    input[i] = 0;
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.