Skip to content

Instantly share code, notes, and snippets.

@philogb
Created September 16, 2012 06:45
Show Gist options
  • Save philogb/3731312 to your computer and use it in GitHub Desktop.
Save philogb/3731312 to your computer and use it in GitHub Desktop.
window.addEventListener('load', function() {
var section = document.querySelector('div.main'),
args = document.querySelector('div.arguments'),
controls = document.querySelector('.controls'),
image = args.querySelector('img'),
video = args.querySelector('video'),
log = $('log'),
useBloomX = true,
useBloomY = true,
useBlending = true;
//check support
if (!supportsWebGL()) {
log.innerHTML = '<p class=\'error\'>Your browser doesn\'t seem to support WebGL. More info <a href=\'http://get.webgl.org/\'>here</a>.</p>';
return;
}
//add events
$('useBloomX').addEventListener('change', function() {
useBloomX = this.checked;
});
$('useBloomY').addEventListener('change', function() {
useBloomY = this.checked;
});
$('useBlending').addEventListener('change', function() {
useBlending = this.checked;
});
//get context
var canvas = $('webgl-canvas'),
gl = getWebGLContext(canvas),
useVideo;
createProgramsFromURIs(gl, {
programs: [{
name: 'bloom',
vsURI: 'shaders/simple.vs',
fsURI: 'shaders/bloom.fs'
}, {
name: 'blend',
vsURI: 'shaders/simple.vs',
fsURI: 'shaders/blend.fs'
}],
onComplete: function(programs) {
//try adding video input, if not fallback to image
useVideo = setupCamera();
if (!useVideo) {
image.style.display = '';
render(programs);
} else {
controls.style.display = '';
video.addEventListener('loadeddata', render.bind(null, programs));
}
}
});
function setupCamera(callback) {
var getUserMediaKey = ['getUserMedia', 'webkitGetUserMedia', 'mozGetUserMedia'],
urlKey = ['URL', 'webkitURL', 'mozURL'],
found = false,
videoHandler = function(localMediaStream) {
video.style.display = '';
video.src = window[urlKey[i]].createObjectURL(localMediaStream);
video.play();
},
videoHandler2 = function(stream) {
video.style.display = '';
video.src = stream;
video.play();
},
errorHandler = function() {
log.innerHTML = 'An error occurred while loading the camera. Please refresh and try again.';
},
key;
for (var i = 0, l = getUserMediaKey.length; i < l; ++i) {
key = getUserMediaKey[i];
if (key in navigator) {
if (i > 0) {
navigator[key]({ video: true }, videoHandler, errorHandler);
} else {
navigator[key]({ video: true }, videoHandler2, errorHandler);
}
found = true;
break;
}
}
return found;
}
function render(programs) {
var buffer = gl.createBuffer(),
vertices = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1],
inputTexture = createTexture(gl, useVideo ? video : image),
bloomXFramebuffer = createFramebuffer(gl, useVideo ? video : image),
bloomYFramebuffer = createFramebuffer(gl, useVideo ? video : image),
x, y;
//set position attribute data
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
//set image as texture and render only once
if (!useVideo) {
log.innerHTML = 'Your browser doesn\'t seem to support getUserMedia, using HTMLImage.';
section.style.width = image.offsetWidth + 'px';
section.style.height = image.offsetHeight + 'px';
postProcess({
gl: gl,
programs: programs,
quad: buffer,
input: image,
inputTexture: inputTexture,
bloomXFramebuffer: bloomXFramebuffer,
bloomYFramebuffer: bloomYFramebuffer
});
return;
}
window.requestAnimationFrame(function loop() {
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
section.style.width = canvas.width + 'px';
section.style.height = canvas.height + 'px';
gl.viewport(0, 0, canvas.width, canvas.height);
postProcess({
gl: gl,
programs: programs,
quad: buffer,
input: video,
inputTexture: inputTexture,
bloomXFramebuffer: bloomXFramebuffer,
bloomYFramebuffer: bloomYFramebuffer
});
//request next frame to render
window.requestAnimationFrame(loop);
});
}
function postProcess(opt) {
var gl = opt.gl,
programs = opt.programs,
quad = opt.quad,
input = opt.input,
inputTexture = opt.inputTexture,
bloomXFramebuffer = opt.bloomXFramebuffer,
bloomYFramebuffer = opt.bloomYFramebuffer,
canvas = gl.canvas,
widthLocation,
heightLocation,
samplerLocation,
samplerLocation0,
samplerLocation1,
positionLocation,
useBlendingLocation,
currentProgram;
//1.- first pass
currentProgram = programs.bloom,
gl.bindFramebuffer(gl.FRAMEBUFFER, bloomXFramebuffer.buffer);
gl.useProgram(currentProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, quad);
//set uniform data
widthLocation = gl.getUniformLocation(currentProgram, 'width');
heightLocation = gl.getUniformLocation(currentProgram, 'height');
blurXLocation = gl.getUniformLocation(currentProgram, 'blurX');
blurYLocation = gl.getUniformLocation(currentProgram, 'blurY');
samplerLocation = gl.getUniformLocation(currentProgram, 'sampler0');
positionLocation = gl.getAttribLocation(currentProgram, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.uniform1f(widthLocation, canvas.width);
gl.uniform1f(heightLocation, canvas.height);
gl.uniform1f(blurXLocation, useBloomX ? 1 : 0);
gl.uniform1f(blurYLocation, 0);
gl.uniform1i(samplerLocation, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, inputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, input);
gl.drawArrays(gl.TRIANGLES, 0, 6);
//send result to bloomX framebuffer
gl.bindTexture(gl.TEXTURE_2D, bloomXFramebuffer.texture);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
//2.- second pass
gl.bindFramebuffer(gl.FRAMEBUFFER, bloomYFramebuffer.buffer);
gl.uniform1f(blurXLocation, 0);
gl.uniform1f(blurYLocation, useBloomY ? 1 : 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, bloomXFramebuffer.texture);
gl.drawArrays(gl.TRIANGLES, 0, 6);
//send result to bloomY framebuffer
gl.bindTexture(gl.TEXTURE_2D, bloomYFramebuffer.texture);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
//3.- blend texture with current output, add tone mapping and send to screen
currentProgram = programs.blend;
gl.useProgram(currentProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, quad);
//set uniform data
widthLocation = gl.getUniformLocation(currentProgram, 'width');
heightLocation = gl.getUniformLocation(currentProgram, 'height');
samplerLocation0 = gl.getUniformLocation(currentProgram, 'sampler0');
samplerLocation1 = gl.getUniformLocation(currentProgram, 'sampler1');
useBlendingLocation = gl.getUniformLocation(currentProgram, 'useBlending');
positionLocation = gl.getAttribLocation(currentProgram, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.uniform1f(widthLocation, canvas.width);
gl.uniform1f(heightLocation, canvas.height);
gl.uniform1i(samplerLocation0, 0);
gl.uniform1i(samplerLocation1, 1);
gl.uniform1i(useBlendingLocation, useBlending ? 1 : 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, bloomYFramebuffer.texture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, inputTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, input);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
function createTexture(gl, size) {
var texture = gl.createTexture();
//set properties for the texture
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size.offsetWidth, size.offsetHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
return texture;
}
function createFramebuffer(gl, size) {
var buffer = gl.createFramebuffer();
//bind framebuffer to texture
gl.bindFramebuffer(gl.FRAMEBUFFER, buffer);
var texture = createTexture(gl, size);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
return {
texture: texture,
buffer: buffer
};
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment