Last active
October 15, 2017 04:56
-
-
Save adrianseeley/10583034 to your computer and use it in GitHub Desktop.
Burst JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
Each pixel is read as 4 bytes (traditionally representing red, green, blue and alpha | |
color channels). But since we have 4 bytes, we can interpret them in any way 4 bytes | |
can be interpreted, like as a single precision floating point number for example: | |
http://en.wikipedia.org/wiki/Single-precision_floating-point_format | |
In this way we can use each pixel to represent a floating point number with a total | |
precision of 24 bits (equivalent to log10(224) ≈ 7.225 decimal digits): | |
1234567 -> no problems | |
12345678 -> loss of precision | |
1.234567 -> no problems | |
1.2345678 -> loss of precision | |
--> | |
<!DOCTYPE html> | |
<html> | |
<script src='webgl-utils.js'></script> | |
<script> | |
var vertex_shader_source = 'attribute vec2 aVertexPosition; varying vec2 vTexCoord; void main() { vTexCoord = aVertexPosition; gl_Position = vec4(aVertexPosition, 0, 1); }'; | |
var fragment_shader_source_header = 'precision mediump float; varying vec2 vTexCoord;'; | |
var fragment_shader_source_random = 'uniform float uTime; float random (vec2 seed) { return fract(cos(mod(123456780., 1024. * dot(seed / uTime, vec2(23.1406926327792690, 2.6651441426902251))))); }'; | |
var fragment_shader_source_encode_float = '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); }'; | |
var fragment_shader_source_uniform_wrap = 'uniform float {{name}};'; | |
var fragment_shader_source_body = 'void main() { gl_FragColor = encode_float(i); }'; | |
function GenerateGPUFunction (Uniforms, CellCount, FloatsOut) { | |
// create a canvas | |
var canvas = document.createElement('canvas'); | |
// determine canvas size by number of cells needed | |
CellCount = Math.ceil(Math.sqrt(CellCount)); | |
canvas.width = CellCount; | |
canvas.height = CellCount; | |
// get gl context from canvas (note that we preserve the drawing buffer to maintain pixel readability, might work without) | |
gl = canvas.getContext('experimental-webgl', {preserveDrawingBuffer: true}); | |
// create full quad to cover canvas | |
var quad = screenQuad(); | |
// create the vertex shader source | |
var vs = vertex_shader_source; | |
// create the fragment shader source | |
// fragment shader header | |
var fs = fragment_shader_source_header; | |
// add uniforms | |
for (var u = 0; u < Uniforms.length; u++) | |
fs += fragment_shader_source_uniform_wrap.split('{{name}}').join(Uniforms[u]); | |
// add random function | |
fs += fragment_shader_source_random; | |
// add encode float function | |
fs += fragment_shader_source_encode_float; | |
// add the main function | |
fs += fragment_shader_source_body; | |
// create the program | |
var program = createProgram(vs, fs); | |
// shake and bake | |
return function GeneratedGPUFunction (UniformValues, Cells) { | |
// load program | |
gl.useProgram(program); | |
// get gpu vertex position location | |
program.aVertexPosition = gl.getAttribLocation (program, 'aVertexPosition'); | |
// get time uniform location | |
program.uTime = gl.getUniformLocation(program, 'uTime'); | |
// create object to hold user uniforms | |
program.UserUniforms = {}; | |
// get locations for all user uniforms | |
for (var u = 0; u < Uniforms.length; u++) | |
program.UserUniforms[Uniforms[u]] = gl.getUniformLocation(program, Uniforms[u]); | |
// enable vertex attributes | |
gl.enableVertexAttribArray(program.vertexPosArray); | |
// load verticies | |
gl.vertexAttribPointer(program.aVertexPosition, quad.itemSize, gl.FLOAT, false, 0, 0); | |
// load time uniform | |
gl.uniform1f(program.uTime, new Date().getTime()); | |
// load all user uniform values | |
for (var u = 0; u < Uniforms.length; u++) | |
gl.uniform1f(program.UserUniforms[Uniforms[u]], UniformValues[u]); | |
// draw | |
gl.drawArrays(gl.TRIANGLE_STRIP, 0, quad.numItems); | |
// create buffer to hold pixels | |
PixelsOutBuffer = new Uint8Array(canvas.width * canvas.height * 4); | |
// read pixels into buffer | |
gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, PixelsOutBuffer); | |
// if the output is floats | |
if (FloatsOut) | |
// convert output to floats | |
return new Float32Array(PixelsOutBuffer.buffer) | |
else | |
// return outputs as bytes | |
return PixelsOutBuffer; | |
}; | |
}; | |
var gpufn = GenerateGPUFunction(['i'], 16, true); | |
for (var i = 0; i < 100; i++) | |
console.log(gpufn([i], null)); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment