diff --git a/app/glslsandbox.js b/app/glslsandbox.js new file mode 100644 index 0000000..9a9b413 --- /dev/null +++ b/app/glslsandbox.js @@ -0,0 +1,444 @@ +/* https://github.com/mrdoob/glsl-sandbox */ + +if (!window.requestAnimationFrame) { + + window.requestAnimationFrame = (function () { + + return window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback, element) { + + window.setTimeout(callback, 1000 / 60); + + }; + + })(); + +} + +// Get older browsers safely through init code, so users can read the +// message about how to download newer browsers. +if (!Date.now) { + Date.now = function () { + return +new Date(); + }; +} + +var quality = 1, quality_levels = [0.5, 1, 2, 4, 8]; +var toolbar, compileButton, fullscreenButton, compileTimer, errorLines = []; +var canvas, gl, buffer, currentProgram, vertexPosition, screenVertexPosition, + parameters = { startTime: Date.now(), time: 0, mouseX: 0.5, mouseY: 0.5, screenWidth: 0, screenHeight: 0 }, + surface = { centerX: 0, centerY: 0, width: 1, height: 1, isPanning: false, isZooming: false, lastX: 0, lastY: 0 }, + frontTarget, backTarget, screenProgram, getWebGL, resizer = {}, compileOnChangeCode = true; + +function init() { + + console.log('initializing...'); + + if (!document.addEventListener) { + return; + } + + canvas = document.createElement('canvas'); + document.body.appendChild(canvas); + + // Initialise WebGL + + try { + + gl = canvas.getContext('experimental-webgl', { preserveDrawingBuffer: true }); + + } catch (error) { } + + if (gl) { + + // enable dFdx, dFdy, fwidth + gl.getExtension('OES_standard_derivatives'); + + // Create vertex buffer (2 triangles) + + buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([- 1.0, - 1.0, 1.0, - 1.0, - 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, - 1.0, 1.0]), gl.STATIC_DRAW); + + // Create surface buffer (coordinates at screen corners) + + surface.buffer = gl.createBuffer(); + } + + var clientXLast, clientYLast; + + document.addEventListener('mousemove', function (event) { + + var clientX = event.clientX; + var clientY = event.clientY; + + if (clientXLast == clientX && clientYLast == clientY) + return; + + clientXLast = clientX; + clientYLast = clientY; + + parameters.mouseX = clientX / window.innerWidth; + parameters.mouseY = 1 - clientY / window.innerHeight; + + }, false); + + onWindowResize(); + window.addEventListener('resize', onWindowResize, false); + + compile(); + compileScreenProgram(); + +} + +function computeSurfaceCorners() { + + if (gl) { + + surface.width = surface.height * parameters.screenWidth / parameters.screenHeight; + + var halfWidth = surface.width * 0.5, halfHeight = surface.height * 0.5; + + gl.bindBuffer(gl.ARRAY_BUFFER, surface.buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + surface.centerX - halfWidth, surface.centerY - halfHeight, + surface.centerX + halfWidth, surface.centerY - halfHeight, + surface.centerX - halfWidth, surface.centerY + halfHeight, + surface.centerX + halfWidth, surface.centerY - halfHeight, + surface.centerX + halfWidth, surface.centerY + halfHeight, + surface.centerX - halfWidth, surface.centerY + halfHeight]), gl.STATIC_DRAW); + + } + +} + +function resetSurface() { + + surface.centerX = surface.centerY = 0; + surface.height = 1; + computeSurfaceCorners(); + +} + +function compile() { + + console.log('compiling shader...'); + + if (!gl) { + + return; + + } + + var program = gl.createProgram(); + var fragment = document.getElementById('shaderCode').textContent; + var vertex = document.getElementById('surfaceVertexShader').textContent; + + var vs = createShader(vertex, gl.VERTEX_SHADER); + var fs = createShader(fragment, gl.FRAGMENT_SHADER); + + if (vs == null || fs == null) return null; + + gl.attachShader(program, vs); + gl.attachShader(program, fs); + + gl.deleteShader(vs); + gl.deleteShader(fs); + + gl.linkProgram(program); + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + + var error = gl.getProgramInfoLog(program); + + compileButton.title = error; + console.error(error); + + console.error('VALIDATE_STATUS: ' + gl.getProgramParameter(program, gl.VALIDATE_STATUS), 'ERROR: ' + gl.getError()); + compileButton.style.color = '#ff0000'; + compileButton.textContent = 'compiled with errors'; + + return; + + } + + if (currentProgram) { + + gl.deleteProgram(currentProgram); + + } + + currentProgram = program; + + // Cache uniforms + + cacheUniformLocation(program, 'time'); + cacheUniformLocation(program, 'mouse'); + cacheUniformLocation(program, 'resolution'); + cacheUniformLocation(program, 'backbuffer'); + cacheUniformLocation(program, 'surfaceSize'); + + // Load program into GPU + + gl.useProgram(currentProgram); + + // Set up buffers + + surface.positionAttribute = gl.getAttribLocation(currentProgram, "surfacePosAttrib"); + gl.enableVertexAttribArray(surface.positionAttribute); + + vertexPosition = gl.getAttribLocation(currentProgram, "position"); + gl.enableVertexAttribArray(vertexPosition); + + console.log('compilation finished'); + +} + +function compileScreenProgram() { + + console.log('compiling screen program...'); + + if (!gl) { return; } + + var program = gl.createProgram(); + var fragment = document.getElementById('fragmentShader').textContent; + var vertex = document.getElementById('vertexShader').textContent; + + var vs = createShader(vertex, gl.VERTEX_SHADER); + var fs = createShader(fragment, gl.FRAGMENT_SHADER); + + gl.attachShader(program, vs); + gl.attachShader(program, fs); + + gl.deleteShader(vs); + gl.deleteShader(fs); + + gl.linkProgram(program); + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + + console.error('VALIDATE_STATUS: ' + gl.getProgramParameter(program, gl.VALIDATE_STATUS), 'ERROR: ' + gl.getError()); + + return; + + } + + screenProgram = program; + + gl.useProgram(screenProgram); + + cacheUniformLocation(program, 'resolution'); + cacheUniformLocation(program, 'texture'); + + screenVertexPosition = gl.getAttribLocation(screenProgram, "position"); + gl.enableVertexAttribArray(screenVertexPosition); + + console.log('compilation finished'); + +} + +function cacheUniformLocation(program, label) { + + if (program.uniformsCache === undefined) { + + program.uniformsCache = {}; + + } + + program.uniformsCache[label] = gl.getUniformLocation(program, label); + +} + +// + +function createTarget(width, height) { + + var target = {}; + + target.framebuffer = gl.createFramebuffer(); + target.renderbuffer = gl.createRenderbuffer(); + target.texture = gl.createTexture(); + + // set up framebuffer + + gl.bindTexture(gl.TEXTURE_2D, target.texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + 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_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + + gl.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target.texture, 0); + + // set up renderbuffer + + gl.bindRenderbuffer(gl.RENDERBUFFER, target.renderbuffer); + + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, target.renderbuffer); + + // clean up + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + return target; + +} + +function createRenderTargets() { + + frontTarget = createTarget(parameters.screenWidth, parameters.screenHeight); + backTarget = createTarget(parameters.screenWidth, parameters.screenHeight); + +} + +// + +var dummyFunction = function () { }; + + +// + +function htmlEncode(str) { + + return String(str) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(//g, '>'); + +} + +// + +function createShader(src, type) { + + var shader = gl.createShader(type); + var line, lineNum, lineError, index = 0, indexEnd; + + gl.shaderSource(shader, src); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + + var error = gl.getShaderInfoLog(shader); + + // Remove trailing linefeed, for FireFox's benefit. + while ((error.length > 1) && (error.charCodeAt(error.length - 1) < 32)) { + error = error.substring(0, error.length - 1); + } + + console.error(error); + + return null; + + } + + return shader; + +} + +// + +function onWindowResize(event) { + + var isMaxWidth = ((resizer.currentWidth === resizer.maxWidth) || (resizer.currentWidth === resizer.minWidth)), + isMaxHeight = ((resizer.currentHeight === resizer.maxHeight) || (resizer.currentHeight === resizer.minHeight)); + + canvas.width = window.innerWidth / quality; + canvas.height = window.innerHeight / quality; + + canvas.style.width = window.innerWidth + 'px'; + canvas.style.height = window.innerHeight + 'px'; + + parameters.screenWidth = canvas.width; + parameters.screenHeight = canvas.height; + + computeSurfaceCorners(); + + if (gl) { + + gl.viewport(0, 0, canvas.width, canvas.height); + + createRenderTargets(); + + } +} + +// + +function animate() { + + requestAnimationFrame(animate); + render(); + +} + +function render() { + + if (!currentProgram) return; + + parameters.time = Date.now() - parameters.startTime; + + // Set uniforms for custom shader + + gl.useProgram(currentProgram); + + gl.uniform1f(currentProgram.uniformsCache['time'], parameters.time / 1000); + gl.uniform2f(currentProgram.uniformsCache['mouse'], parameters.mouseX, parameters.mouseY); + gl.uniform2f(currentProgram.uniformsCache['resolution'], parameters.screenWidth, parameters.screenHeight); + gl.uniform1i(currentProgram.uniformsCache['backbuffer'], 0); + gl.uniform2f(currentProgram.uniformsCache['surfaceSize'], surface.width, surface.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, surface.buffer); + gl.vertexAttribPointer(surface.positionAttribute, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.vertexAttribPointer(vertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, backTarget.texture); + + // Render custom shader to front buffer + + gl.bindFramebuffer(gl.FRAMEBUFFER, frontTarget.framebuffer); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Set uniforms for screen shader + + gl.useProgram(screenProgram); + + gl.uniform2f(screenProgram.uniformsCache['resolution'], parameters.screenWidth, parameters.screenHeight); + gl.uniform1i(screenProgram.uniformsCache['texture'], 1); + + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.vertexAttribPointer(screenVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, frontTarget.texture); + + // Render front buffer to screen + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + // Swap buffers + + var tmp = frontTarget; + frontTarget = backTarget; + backTarget = tmp; + +} \ No newline at end of file diff --git a/app/index.html b/app/index.html index a356cea..82db6b4 100644 --- a/app/index.html +++ b/app/index.html @@ -17,11 +17,126 @@ + + + + + + + + + + + + + + - -
ΣndPwn³
+ +
ΣndPwn³
\ No newline at end of file