Skip to content

Instantly share code, notes, and snippets.

@adrianseeley
Last active October 15, 2017 04:56
Show Gist options
  • Save adrianseeley/10583034 to your computer and use it in GitHub Desktop.
Save adrianseeley/10583034 to your computer and use it in GitHub Desktop.
Burst JS
<!--
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