├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── demo.frag ├── demo.js ├── demo.vert ├── index.glsl └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | index.html -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Matt DesLauriers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # glsl-hash-blur 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | [![grain](http://i.imgur.com/uTGaC5t.jpg?1)](http://stack.gl/glsl-hash-blur/) 6 | 7 | A fast blur effect that uses a random hash to compute the sample offset. This gives a snowy/grainy feel to the blur. When requiring, you must specify a `sample` function and the `iterations` (a `const` or `#define` integer). 8 | 9 | ```glsl 10 | vec3 tex(vec2 uv); 11 | 12 | #pragma glslify: blur = require('glsl-hash-blur', sample=tex, iterations=20) 13 | 14 | vec3 tex(vec2 uv) { 15 | return texture2D(iChannel0, uv).rgb; 16 | } 17 | 18 | void main() { 19 | float aspect = resolution.x / resolution.y; 20 | gl_FragColor.rgb = blur(vUv, radius, aspect); 21 | } 22 | ``` 23 | 24 | See [demo.frag](demo.frag) for an implementation of a vignette blur. 25 | 26 | The effect was inspired by David Hoskins' [ShaderToy](https://www.shadertoy.com/view/XdjSRw). 27 | 28 | **Note:** Use `highp` precision for best results on mobile and other low-end devices. 29 | 30 | ## Demos 31 | 32 | - [applied to a texture](http://stack.gl/glsl-hash-blur/) 33 | - [editable glslbin demo](http://glslb.in/s/c1a93844) 34 | 35 | ## Usage 36 | 37 | [![NPM](https://nodei.co/npm/glsl-hash-blur.png)](https://www.npmjs.com/package/glsl-hash-blur) 38 | 39 | #### `blur = require('glsl-hash-blur', sample=S, iterations=I)` 40 | 41 | Requires the module with your desired sampling function `S` and iteration count `I`. The sample function has the following signature: 42 | 43 | ```glsl 44 | vec3 sample(in vec2 uv); 45 | ``` 46 | 47 | #### `vec3 blur(vec2 uv, float radius[, float aspect[, float offset]])` 48 | 49 | Using the sample function provided above, this will create a blur using the specified UV coordinates and `radius` strength. The radius is typically multiplied by texel size, e.g. `1.0 / resolution.x`. 50 | 51 | The `aspect` (defaults to 1.0) is recommended to produce a more accurate blur; e.g. `resolution.x / resolution.y`. 52 | 53 | The `offset` is optional (defaults to 0.0). It offsets the uvs during randomization, which can produce a "jitter" effect like moving film noise. 54 | 55 | ## Running From Source 56 | 57 | To build/run the demo from source: 58 | 59 | ```sh 60 | git clone https://github.com/stackgl/glsl-hash-blur.git 61 | cd glsl-hash-blur 62 | npm install 63 | ``` 64 | 65 | Then run the following to start development: 66 | 67 | ```sh 68 | npm run start 69 | ``` 70 | 71 | And open [http://localhost:9966/](). Changes to the file will trigger a LiveReload event on the page. 72 | 73 | To run the production build: 74 | 75 | ```sh 76 | npm run build 77 | ``` 78 | 79 | ## License 80 | 81 | MIT, see [LICENSE.md](http://github.com/stackgl/glsl-hash-blur/blob/master/LICENSE.md) for details. 82 | -------------------------------------------------------------------------------- /demo.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | uniform vec3 iResolution; 4 | uniform sampler2D iChannel0; 5 | uniform float iGlobalTime; 6 | 7 | #define ITERATIONS 13 8 | 9 | vec3 sample(vec2 uv); 10 | #pragma glslify: blur = require('./', sample=sample, iterations=ITERATIONS) 11 | 12 | vec3 sample(vec2 uv) { 13 | return texture2D(iChannel0, uv).rgb; 14 | } 15 | 16 | void main() { 17 | vec2 uv = vec2(gl_FragCoord.xy / iResolution.xy); 18 | uv.y = 1.0 - uv.y; 19 | 20 | float texelSize = 1.0 / iResolution.x; 21 | float aspect = iResolution.x / iResolution.y; 22 | 23 | //animate strength 24 | float anim = sin(iGlobalTime)/2.0+0.5; 25 | float strength = 40.0 * anim * texelSize; 26 | 27 | //vignette blur 28 | float radius = 1.0 - length(uv - 0.5); 29 | radius = smoothstep(0.7, 0.0, radius) * strength; 30 | 31 | //jitter the noise but not every frame 32 | float tick = floor(fract(iGlobalTime)*20.0); 33 | float jitter = mod(tick * 382.0231, 21.321); 34 | 35 | //Apply the blur effect... 36 | //We do this on every fragment, but you 37 | //might get a performance boost by only 38 | //blurring fragments where radius > 0 39 | vec3 color = blur(uv, radius, aspect, jitter); 40 | gl_FragColor = vec4(color, 1.0); 41 | } -------------------------------------------------------------------------------- /demo.js: -------------------------------------------------------------------------------- 1 | var triangle = require('a-big-triangle') 2 | var createShader = require('gl-shader') 3 | var glslify = require('glslify') 4 | var loop = require('raf-loop') 5 | 6 | var gl = require('webgl-context')({ 7 | width: 512, 8 | height: 512 9 | }) 10 | 11 | document.body.appendChild(gl.canvas) 12 | 13 | var time = 0 14 | var image = require('baboon-image').transpose(1, 0, 2) 15 | var texture = require('gl-texture2d')(gl, image) 16 | texture.wrapS = texture.wrapT = gl.REPEAT 17 | 18 | var shader = createShader(gl, glslify('./demo.vert'), glslify('./demo.frag')) 19 | shader.bind() 20 | shader.uniforms.iResolution = [gl.drawingBufferWidth, gl.drawingBufferHeight, 0] 21 | shader.uniforms.iGlobalTime = time 22 | shader.uniforms.iChannel0 = 0 23 | start() 24 | 25 | function start() { 26 | loop(render).start() 27 | 28 | function render(dt) { 29 | var width = gl.drawingBufferWidth 30 | var height = gl.drawingBufferHeight 31 | gl.viewport(0, 0, width, height) 32 | 33 | texture.bind() 34 | shader.bind() 35 | shader.uniforms.iGlobalTime = (time += dt) / 1000 36 | triangle(gl) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /demo.vert: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | attribute vec2 position; 4 | 5 | void main() { 6 | gl_Position = vec4(position, 1, 1); 7 | } -------------------------------------------------------------------------------- /index.glsl: -------------------------------------------------------------------------------- 1 | #pragma glslify: random = require('glsl-random') 2 | 3 | #ifndef TAU 4 | #define TAU 6.28318530718 5 | #endif 6 | 7 | //Use last part of hash function to generate new random radius and angle 8 | vec2 mult(inout vec2 r) { 9 | r = fract(r * vec2(12.9898,78.233)); 10 | return sqrt(r.x + .001) * vec2(sin(r.y * TAU), cos(r.y * TAU)); 11 | } 12 | 13 | vec3 blur(vec2 uv, float radius, float aspect, float offset) { 14 | vec2 circle = vec2(radius); 15 | circle.x *= aspect; 16 | vec2 rnd = vec2(random(vec2(uv + offset))); 17 | 18 | vec3 acc = vec3(0.0); 19 | for (int i = 0; i < iterations; i++) { 20 | acc += sample(uv + circle * mult(rnd)).xyz; 21 | } 22 | return acc / float(iterations); 23 | } 24 | 25 | vec3 blur(vec2 uv, float radius, float aspect) { 26 | return blur(uv, radius, aspect, 0.0); 27 | } 28 | 29 | vec3 blur(vec2 uv, float radius) { 30 | return blur(uv, radius, 1.0); 31 | } 32 | 33 | #pragma glslify: export(blur) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glsl-hash-blur", 3 | "version": "1.0.3", 4 | "description": "a blur effect based on random hash offset", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Matt DesLauriers", 9 | "email": "dave.des@gmail.com", 10 | "url": "https://github.com/mattdesl" 11 | }, 12 | "dependencies": { 13 | "glsl-random": "0.0.5" 14 | }, 15 | "devDependencies": { 16 | "a-big-triangle": "^1.0.0", 17 | "baboon-image": "^1.0.0", 18 | "browserify": "^9.0.8", 19 | "budo": "^3.0.4", 20 | "garnish": "^2.1.3", 21 | "gl-shader": "^4.0.1", 22 | "gl-texture2d": "^2.0.8", 23 | "glslify": "^2.1.1", 24 | "raf-loop": "^1.0.1", 25 | "uglifyjs": "^2.4.10", 26 | "watchify": "^3.1.0", 27 | "webgl-context": "^2.1.2" 28 | }, 29 | "scripts": { 30 | "start": "budo demo.js:bundle.js --live -v -t glslify | garnish", 31 | "build": "browserify demo.js -t glslify | uglifyjs -cm > bundle.js" 32 | }, 33 | "keywords": [ 34 | "hash", 35 | "blur", 36 | "blurred", 37 | "blurs", 38 | "blurring", 39 | "glsl", 40 | "glslify", 41 | "glslbin", 42 | "stackgl", 43 | "hashed", 44 | "random", 45 | "gaussian", 46 | "sparkle" 47 | ], 48 | "repository": { 49 | "type": "git", 50 | "url": "git://github.com/stackgl/glsl-hash-blur.git" 51 | }, 52 | "homepage": "https://github.com/stackgl/glsl-hash-blur", 53 | "bugs": { 54 | "url": "https://github.com/stackgl/glsl-hash-blur/issues" 55 | } 56 | } 57 | --------------------------------------------------------------------------------