var scene = document.getElementById("scene"); var gl = scene.getContext("webgl"); var createProgram = function (vsh, fsh) { var program = gl.createProgram(); gl.attachShader(program, vsh); gl.attachShader(program, fsh); gl.linkProgram(program); var status = gl.getProgramParameter(program, gl.LINK_STATUS); if (!status) { var error = gl.getProgramInfoLog(program); console.error("Failed to link shader program: " + error); gl.deleteProgram(program); gl.deleteShader(vsh); gl.deleteShader(fsh); return null; } return program; }; var createShader = function (type, src) { var shader = gl.createShader(type); gl.shaderSource(shader, src); gl.compileShader(shader); var status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!status) { var error = gl.getShaderInfoLog(shader); console.error("Failed to compile shader: " + error); gl.deleteShader(shader); return null; } return shader; }; var createTexture = function (width, height) { var tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); return tex; }; var line_vsh_src = "" + "attribute vec2 a_pos;" + "attribute vec4 a_color;" + "varying vec4 v_color;" + "void main(){" + " gl_Position = vec4(a_pos, 0.0, 1.0);" + " v_color = a_color;" + "}"; var line_fsh_src = "" + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec4 v_color;" + "void main(){" + " gl_FragColor = v_color;" + "}"; var line_vsh = createShader(gl.VERTEX_SHADER, line_vsh_src); var line_fsh = createShader(gl.FRAGMENT_SHADER, line_fsh_src); var line_program = createProgram(line_vsh, line_fsh); gl.useProgram(line_program); var line_a_pos = gl.getAttribLocation(line_program, "a_pos"); var line_a_color = gl.getAttribLocation(line_program, "a_color"); var canvas_vsh_src = "" + "attribute vec2 a_pos;" + "attribute vec2 a_texcoord;" + "varying vec2 v_texcoord;" + "void main(){" + " gl_Position = vec4(a_pos, 0.0, 1.0);" + " v_texcoord = a_texcoord;" + "}"; var canvas_fsh_src = "" + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "uniform sampler2D u_tex;" + "varying vec2 v_texcoord;" + "void main(){" + " gl_FragColor = texture2D(u_tex, v_texcoord);" + "}"; var canvas_vsh = createShader(gl.VERTEX_SHADER, canvas_vsh_src); var canvas_fsh = createShader(gl.FRAGMENT_SHADER, canvas_fsh_src); var canvas_program = createProgram(canvas_vsh, canvas_fsh); gl.useProgram(canvas_program); var canvas_a_pos = gl.getAttribLocation(canvas_program, "a_pos"); var canvas_a_texcoord = gl.getAttribLocation(canvas_program, "a_texcoord"); var canvas_u_tex = gl.getUniformLocation(canvas_program, "u_tex"); var canvas_vertices = new Float32Array([ -1, -1, 0, 0, // BL 1, -1, 1, 0, // BR 1, 1, 1, 1, // TR -1, 1, 0, 1 // TL ]); var canvas_indices = new Uint8Array([ 0, 1, 2, 2, 3, 0 ]); var canvas_vbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, canvas_vbo); gl.bufferData(gl.ARRAY_BUFFER, canvas_vertices, gl.STATIC_DRAW); var canvas_ebo = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, canvas_ebo); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, canvas_indices, gl.STATIC_DRAW); var line_blur_fsh_src = "" + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "uniform sampler2D u_tex;" + "uniform vec2 u_offset;" + "varying vec2 v_texcoord;" + "vec3 GaussianBlur(sampler2D tex, vec2 centreUV, vec2 pixelOffset) {" + " vec3 colOut = vec3(0, 0, 0);" + " const int stepCount = 2;" + " float gWeights[stepCount];" + " gWeights[0] = 0.64908;" + " gWeights[1] = 0.15092;" + " float gOffsets[stepCount];" + " gOffsets[0] = 0.53805;" + " gOffsets[1] = 2.06278;" + " for (int i = 0; i < stepCount; i++) {" + " vec2 texCoordOffset = gOffsets[i] * pixelOffset;" + " vec3 col = texture2D(tex, centreUV + texCoordOffset).xyz +" + " texture2D(tex, centreUV - texCoordOffset).xyz;" + " colOut += gWeights[i] * col;" + " }" + " return colOut;" + "}" + "void main(){" + " gl_FragColor = /*texture2D(u_tex, v_texcoord);*/vec4(GaussianBlur(u_tex, v_texcoord, u_offset), 1.0);" + "}"; var line_blur_fsh = createShader(gl.FRAGMENT_SHADER, line_blur_fsh_src); var line_blur_program = createProgram(canvas_vsh, line_blur_fsh); gl.useProgram(line_blur_program); var line_blur_a_pos = gl.getAttribLocation(line_blur_program, "a_pos"); var line_blur_a_texcoord = gl.getAttribLocation(line_blur_program, "a_texcoord"); var line_blur_u_tex = gl.getUniformLocation(line_blur_program, "u_tex"); var line_blur_u_offset = gl.getUniformLocation(line_blur_program, "u_offset"); var line_tex_size = 256; var blur_tex_size = 64; var line_tex = createTexture(line_tex_size, line_tex_size); var line_blur_h_tex = createTexture(blur_tex_size, blur_tex_size); var line_blur_tex = createTexture(blur_tex_size, blur_tex_size); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, line_tex); var line_framebuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, line_framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, line_tex, 0); gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, line_blur_h_tex); gl.activeTexture(gl.TEXTURE2); gl.bindTexture(gl.TEXTURE_2D, line_blur_tex); var line_blur_framebuffer = gl.createFramebuffer(); var line_points = [ ], count = 0, max = 15; var line_vbo = gl.createBuffer(); var map = function (v, l, h, f, t) { return (v - l) / (h - l) * (t - f) + f; }; scene.onmousemove = function (e) { var x = map(e.offsetX, 0, 512, -1, 1); var y = map(e.offsetY, 512, 0, -1, 1); line_points.push(x, y, Math.random(), Math.random(), Math.random(), 1); if (count < max) { count++; } else { line_points.shift(); line_points.shift(); line_points.shift(); line_points.shift(); line_points.shift(); line_points.shift(); } }; gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.BLEND); gl.lineWidth(1.5); requestAnimationFrame(function loop() { // draw lines on line framebuffer gl.bindFramebuffer(gl.FRAMEBUFFER, line_framebuffer); gl.clear(gl.COLOR_BUFFER_BIT); var line_points_float = new Float32Array(line_points); gl.bindBuffer(gl.ARRAY_BUFFER, line_vbo); gl.bufferData(gl.ARRAY_BUFFER, line_points_float, gl.DYNAMIC_DRAW); gl.vertexAttribPointer(line_a_pos, 2, gl.FLOAT, false, line_points_float.BYTES_PER_ELEMENT * 6, 0); gl.enableVertexAttribArray(line_a_pos); gl.vertexAttribPointer(line_a_color, 4, gl.FLOAT, false, line_points_float.BYTES_PER_ELEMENT * 6, line_points_float.BYTES_PER_ELEMENT * 2); gl.enableVertexAttribArray(line_a_color); gl.viewport(0, 0, line_tex_size, line_tex_size); gl.useProgram(line_program); gl.drawArrays(gl.LINE_STRIP, 0, line_points_float.length / 6); // draw blur lines on line blur framebuffer gl.bindFramebuffer(gl.FRAMEBUFFER, line_blur_framebuffer); gl.bindBuffer(gl.ARRAY_BUFFER, canvas_vbo); gl.vertexAttribPointer(line_blur_a_pos, 2, gl.FLOAT, false, canvas_vertices.BYTES_PER_ELEMENT * 4, 0); gl.enableVertexAttribArray(line_blur_a_pos); gl.vertexAttribPointer(line_blur_a_texcoord, 2, gl.FLOAT, false, canvas_vertices.BYTES_PER_ELEMENT * 4, canvas_vertices.BYTES_PER_ELEMENT * 2); gl.enableVertexAttribArray(line_blur_a_texcoord); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, canvas_ebo); gl.viewport(0, 0, blur_tex_size, blur_tex_size); gl.useProgram(line_blur_program); // draw h-blur lines on line blur framebuffer gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, line_blur_h_tex, 0); gl.uniform1i(line_blur_u_tex, 0); gl.uniform2f(line_blur_u_offset, 0.5 / blur_tex_size, 0); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); // draw v-blur lines on line blur framebuffer gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, line_blur_tex, 0); gl.uniform1i(line_blur_u_tex, 1); gl.uniform2f(line_blur_u_offset, 0, 0.5 / blur_tex_size); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); // draw canvas gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clear(gl.COLOR_BUFFER_BIT); gl.bindBuffer(gl.ARRAY_BUFFER, canvas_vbo); gl.vertexAttribPointer(canvas_a_pos, 2, gl.FLOAT, false, canvas_vertices.BYTES_PER_ELEMENT * 4, 0); gl.enableVertexAttribArray(canvas_a_pos); gl.vertexAttribPointer(canvas_a_texcoord, 2, gl.FLOAT, false, canvas_vertices.BYTES_PER_ELEMENT * 4, canvas_vertices.BYTES_PER_ELEMENT * 2); gl.enableVertexAttribArray(canvas_a_texcoord); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, canvas_ebo); gl.viewport(0, 0, 512, 512); gl.useProgram(canvas_program); gl.uniform1i(canvas_u_tex, 0); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); gl.blendFunc(gl.ONE, gl.ONE); gl.uniform1i(canvas_u_tex, 2); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); gl.blendFunc(gl.ONE, gl.ZERO); requestAnimationFrame(loop); });