Using WebGL Shader Language (GLSL) for arbitrary vector mathematics in JavaScript
As far as I can see from the spec WebGL supports framebuffer objects and read-back operations. This is sufficient for you to transform the data and get it back into client space. Here is a sequence of operations:
- Create FBO with attachment render buffers that you need to store the result; bind it
- Upload all input data into textures (the same size).
- Create the GLSL processing shader that will do the calculus inside the fragment part, reading the input from textures and writing the output into destination renderbuffers; bind it
- Draw a quad; read back the render buffers via
glReadPixels
.
Getting floats out of a shader in the browser is actually pretty easy, the constraint being 1 float per pixel though.
We convert 4 ints to 1 float (r: int, g: int, b: int, a: int) -> (rgba: float).
Thanks IEEE
float random(vec2 seed) { return fract(cos(mod(123456780., 1024. * dot(seed / time, vec2(23.1406926327792690, 2.6651441426902251))))); }float shift_right(float v, float amt) { v = floor(v) + 0.5; return floor(v / exp2(amt)); }float shift_left(float v, float amt) { return floor(v * exp2(amt) + 0.5); }float mask_last(float v, float bits) { return mod(v, shift_left(1.0, bits)); }float extract_bits(float num, float from, float to) { from = floor(from + 0.5); to = floor(to + 0.5); return mask_last(shift_right(num, from), to - from); }vec4 encode_float(float val) { if (val == 0.0) return vec4(0, 0, 0, 0); float sign = val > 0.0 ? 0.0 : 1.0; val = abs(val); float exponent = floor(log2(val)); float biased_exponent = exponent + 127.0; float fraction = ((val / exp2(exponent)) - 1.0) * 8388608.0; float t = biased_exponent / 2.0; float last_bit_of_biased_exponent = fract(t) * 2.0; float remaining_bits_of_biased_exponent = floor(t); float byte4 = extract_bits(fraction, 0.0, 8.0) / 255.0; float byte3 = extract_bits(fraction, 8.0, 16.0) / 255.0; float byte2 = (last_bit_of_biased_exponent * 128.0 + extract_bits(fraction, 16.0, 23.0)) / 255.0; float byte1 = (sign * 128.0 + remaining_bits_of_biased_exponent) / 255.0; return vec4(byte4, byte3, byte2, byte1); }
Usage:
Shader:
outputcolor = encode_float(420.420f);
JavaScript:
// convert output to floatsoutput = new Float32Array(output.buffer);