Digging this up. This is how I use 16-bit floating point textures
function createDataTexture16F(gl, width, height) {
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.texStorage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, width, height)
return texture
}
function updateDataTexture16F(gl, texture, width, height, data32) {
const data = float32ToHalfFloat(data32)
gl.bindTexture(gl.TEXTURE_2D, texture)
if (data.length !== width * height * 4) throw new Error('Incorrect data length')
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, width, height, 0, gl.RGBA, gl.HALF_FLOAT, data)
}
float32ToHalfFloat
packs the data into Uint16Array as there's no Float16Array in JavaScript. data32
can be any array, either [] or Float32Array
var toHalfFn = (function() {
const floatView = new Float32Array(1)
const int32View = new Int32Array(floatView.buffer)
return function toHalf(val) {
floatView[0] = val
var x = int32View[0]
var bits = (x >> 16) & 0x8000
var m = (x >> 12) & 0x07ff
var e = (x >> 23) & 0xff
if (e < 103) {
return bits
}
if (e > 142) {
bits |= 0x7c00
bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff)
return bits
}
if (e < 113) {
m |= 0x0800
bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1)
return bits
}
bits |= ((e - 112) << 10) | (m >> 1)
bits += m & 1
return bits
}
}())
export const toHalf = toHalfFn
function float32ToHalfFloat(arr) {
const data = new Uint16Array(arr.length)
for (var i = 0, il = arr.length; i < il; i ++) {
data[i] = toHalf(arr[i])
}
return data
}