Created
May 23, 2017 07:51
-
-
Save vaalentin/f1c30e8c404f956c30089971b3e8708a to your computer and use it in GitHub Desktop.
Revisions
-
vaalentin created this gist
May 23, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,528 @@ import THREE from 'three' import TrackballControl from 'three.trackball' import { textureLoader } from 'utils' const passThroughMaterial = new THREE.RawShaderMaterial({ uniforms: { uTexture: { type: 't', value: null } }, vertexShader: ` attribute vec3 position; attribute vec2 uv; varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `, fragmentShader: ` precision mediump float; uniform sampler2D uTexture; varying vec2 vUv; void main() { gl_FragColor = texture2D(uTexture, vUv); } ` }) const blurMaterial = new THREE.RawShaderMaterial({ uniforms: { uTexture: { type: 't', value: null }, uResolution: { type: 'v2', value: new THREE.Vector2(0, 0) }, uDirection: { type: 'v2', value: new THREE.Vector2(0, 0) } }, vertexShader: ` attribute vec3 position; attribute vec2 uv; varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `, fragmentShader: ` precision mediump float; vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { vec4 color = vec4(0.0); vec2 off1 = vec2(1.3846153846) * direction; vec2 off2 = vec2(3.2307692308) * direction; color += texture2D(image, uv) * 0.2270270270; color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; return color; } uniform sampler2D uTexture; uniform vec2 uResolution; uniform vec2 uDirection; varying vec2 vUv; void main() { vec4 textureColor = texture2D(uTexture, vUv); gl_FragColor = blur9(uTexture, vUv, uResolution, uDirection); } ` }) const postProcessMaterial = new THREE.RawShaderMaterial({ uniforms: { uTexture: { type: 't', value: null }, uLookupTexture: { type: 't', value: null }, uResolution: { type: 'v2', value: new THREE.Vector2(0, 0) } }, vertexShader: ` attribute vec3 position; attribute vec2 uv; varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `, fragmentShader: ` precision mediump float; #define LUT_FLIP_Y // lookup vec4 lookup(in vec4 textureColor, in sampler2D lookupTable) { #ifndef LUT_NO_CLAMP textureColor = clamp(textureColor, 0.0, 1.0); #endif mediump float blueColor = textureColor.b * 63.0; mediump vec2 quad1; quad1.y = floor(floor(blueColor) / 8.0); quad1.x = floor(blueColor) - (quad1.y * 8.0); mediump vec2 quad2; quad2.y = floor(ceil(blueColor) / 8.0); quad2.x = ceil(blueColor) - (quad2.y * 8.0); highp vec2 texPos1; texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); #ifdef LUT_FLIP_Y texPos1.y = 1.0-texPos1.y; #endif highp vec2 texPos2; texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); #ifdef LUT_FLIP_Y texPos2.y = 1.0-texPos2.y; #endif lowp vec4 newColor1 = texture2D(lookupTable, texPos1); lowp vec4 newColor2 = texture2D(lookupTable, texPos2); lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor)); return newColor; } uniform sampler2D uTexture; uniform sampler2D uLookupTexture; uniform vec2 uResolution; varying vec2 vUv; void main() { vec4 textureColor = texture2D(uTexture, vUv); gl_FragColor = lookup(textureColor, uLookupTexture); } ` }) const zoomBlurMaterial = new THREE.RawShaderMaterial({ uniforms: { uTexture: { type: 't', value: null }, uResolution: { type: 'v2', value: new THREE.Vector2(0, 0) }, uCenter: { type: 'v2', value: new THREE.Vector2(0, 0) }, uRadius: { type: 'f', value: 0 } }, vertexShader: ` attribute vec3 position; attribute vec2 uv; varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `, fragmentShader: ` precision mediump float; uniform sampler2D uTexture; uniform vec2 uResolution; uniform vec2 uCenter; uniform float uRadius; varying vec2 vUv; float random(vec3 scale,float seed) { return fract(sin(dot(gl_FragCoord.xyz+seed, scale)) * 43758.5453 + seed); } void main() { // from https://github.com/spite/Wagner/blob/master/fragment-shaders/zoom-blur-fs.glsl vec4 color = vec4(0.0); float total = 0.0; vec2 toCenter = uCenter - vUv * uResolution; float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for(float i = 0.0; i <= 40.0; i++) { float percent = (i + offset) / 40.0; float weight = 4.0*(percent - percent * percent); vec4 sample = texture2D(uTexture, vUv + toCenter * percent * uRadius / uResolution); sample.rgb *= sample.a; color += sample * weight; total += weight; } gl_FragColor = color / total; } ` }) textureLoader.load('public/images/lookup.png', texture => { texture.generateMipmaps = false texture.minFilter = THREE.LinearFilter; texture.magFilter = THREE.LinearFilter; texture.wrapS = THREE.ClampToEdgeWrapping; texture.wrapT = THREE.ClampToEdgeWrapping; postProcessMaterial.uniforms.uLookupTexture.value = texture }) export default class Scene { /** * @param {HTMLElement} [$container] */ constructor($container, controls) { this._$container = $container const { offsetWidth: width, offsetHeight: height } = this._$container this._scene = this._getScene() this._screenScene = this._getScene() this._camera = this._getCamera(width, height) this._renderer = this._getRenderer(width, height) this._$container.appendChild(this._renderer.domElement) if(controls) { this._controls = this._getControls(this._camera, this._renderer) } this._fbos = this._getFbos(width, height) this._currentFboIndex = 0 this._screenQuad = new THREE.Mesh( new THREE.PlaneBufferGeometry(2, 2), null ) this._blurEnabled = true this._zoomBlurEnabled = true this._postProcessEnabled = true this._blur = false this._blurPasses = 6 this._blurRadius = new THREE.Vector3(0, 0, 0) this._zoomBlur = false blurMaterial.uniforms.uResolution.value.set(width, height) zoomBlurMaterial.uniforms.uResolution.value.set(width, height) zoomBlurMaterial.uniforms.uCenter.value.set(width / 2, height / 2) postProcessMaterial.uniforms.uResolution.value.set(width, height) this._screenScene.add(this._screenQuad) this._frameId = null this._bindMethods() this._addListeners() this._update() window.scene = this } _bindMethods() { this._handleResizeBound = this._handleResize.bind(this) this._updateBound = this._update.bind(this) this._renderBound = this._render.bind(this) } _addListeners() { window.addEventListener('resize', this._handleResizeBound) if(this._controls) { this._controls.addEventListener('change', this._renderBound) } } _removeListeners() { window.removeEventListener('resize', this._handleResizeBound) if(this._controls) { this._controls.removeEventListener('change', this._renderBound) } } /** * @param {float} width * @param {float} height * @returns {Array<THREE.WebGLRenderTarget} */ _getFbos(width, height) { return [ new THREE.WebGLRenderTarget(width, height, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, depthBuffer: true, stencilBuffer: false }), new THREE.WebGLRenderTarget(width, height, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, depthBuffer: true, stencilBuffer: false }) ] } _resetFboIndex() { this._currentFboIndex = 0 } _incrementFboIndex() { this._currentFboIndex = ++this._currentFboIndex === this._fbos.length ? 0 : this._currentFboIndex } /** * @returns {THREE.Scene} */ _getScene() { return new THREE.Scene() } /** * @param {float} [width] * @param {float} [height] * @returns {THREE.Camera} */ _getCamera(width, height) { const camera = new THREE.PerspectiveCamera(45, width / height, 1, 100) camera.position.z = 5 return camera } /** * @param {float} [width] * @param {float} [height] * @returns {THREE.WebGLRenderer} */ _getRenderer(width, height) { const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }) renderer.setSize(width, height) renderer.setClearColor(0x000000, 0) return renderer } /** * @param {THREE.Camera} [camera] * @param {THREE.WebGLRenderer} [renderer] * @returns {TrackballControl} */ _getControls(camera, renderer) { const controls = new TrackballControl(camera) Object.assign(controls, { rotateSpeed: 1, zoomSpeed: 1, panSpeed: .8, noZoom: false, noPan: false, staticMoving: false, dynamicDampingFactor : .3 }) return controls } _handleResize() { const { offsetWidth: width, offsetHeight: height } = this._$container this._camera.aspect = width / height this._camera.updateProjectionMatrix() this._renderer.setSize(width, height) for(let renderTarget of this._fbos) { renderTarget.setSize(width, height) } blurMaterial.uniforms.uResolution.value.set(width, height) zoomBlurMaterial.uniforms.uResolution.value.set(width, height) zoomBlurMaterial.uniforms.uCenter.value.set(width / 2, height / 2) postProcessMaterial.uniforms.uResolution.value.set(width, height) } _update() { this._frameId = requestAnimationFrame(this._updateBound) if(this._controls) { this._controls.update() } this._render() } _render() { // no need for fbos // render directly to screen if(!this._blurEnabled && !this._zoomBlurEnabled && !this._postProcessEnabled) { this._renderer.render(this._scene, this._camera) return } this._resetFboIndex() // first, render scene to texture this._renderer.render(this._scene, this._camera, this._fbos[this._currentFboIndex]) // blur if(this._blurEnabled && this._blur) { this._screenQuad.material = blurMaterial for(let i = this._blurPasses - 1; i >= 0; --i) { const blurRadiusX = (this._blurRadius.x * (i + 1)) / this._blurPasses const blurRadiusY = (this._blurRadius.y * (i + 1)) / this._blurPasses // horizontal blurMaterial.uniforms.uDirection.value.set(blurRadiusX, 0) blurMaterial.uniforms.uTexture.value = this._fbos[this._currentFboIndex].texture this._incrementFboIndex() this._renderer.render(this._screenScene, this._camera, this._fbos[this._currentFboIndex]) // vertical blurMaterial.uniforms.uDirection.value.set(0, blurRadiusY) blurMaterial.uniforms.uTexture.value = this._fbos[this._currentFboIndex].texture if(i !== 0) { // next pass this._incrementFboIndex() this._renderer.render(this._screenScene, this._camera, this._fbos[this._currentFboIndex]) } } } // zoom blur if(this._zoomBlurEnabled && this._zoomBlur) { this._screenQuad.material = zoomBlurMaterial zoomBlurMaterial.uniforms.uTexture.value = this._fbos[this._currentFboIndex].texture zoomBlurMaterial.uniforms.uRadius.value = this._blurRadius.z this._incrementFboIndex() this._renderer.render(this._screenScene, this._camera, this._fbos[this._currentFboIndex]) } // post process if(this._postProcessEnabled) { this._screenQuad.material = postProcessMaterial postProcessMaterial.uniforms.uTexture.value = this._fbos[this._currentFboIndex].texture this._renderer.render(this._screenScene, this._camera) } else { this._screenQuad.material = passThroughMaterial passThroughMaterial.uniforms.uTexture.value = this._fbos[this._currentFboIndex].texture this._renderer.render(this._screenScene, this._camera) } } /** * @param {THREE.Object3D|Array<THREE.Object3D>} [children] */ add(...children) { for(let i = 0; i < children.length; ++i) { this._scene.add(children[i]) } } /** * @param {THREE.Object3D|Array<THREE.Object3D>} [children] */ remove(...children) { for(let i = 0; i < children.length; ++i) { this._scene.remove(children[i]) } } /** * @param {float} blurX */ setBlurRadiusX(blurX) { this._blurRadius.x = blurX this._blur = this._blurRadius.x !== 0 || this._blurRadius.y !== 0 } /** * @param {float} blurY */ setBlurRadiusY(blurY) { this._blurRadius.y = blurY this._blur = this._blurRadius.x !== 0 || this._blurRadius.y !== 0 } /** * @param {float} blurZ */ setBlurRadiusZ(blurZ) { this._blurRadius.z = blurZ this._zoomBlur = this._blurRadius !== 0 } /** * @param {float} fov */ setFov(fov) { this._camera.fov = fov this._camera.updateProjectionMatrix() } dispose() { this._removeListeners() cancelAnimationFrame(this._frameId) } }