Spaces:
Running
Running
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author MPanknin / http://www.redplant.de/ | |
| * @author takahiro / https://github.com/takahirox | |
| * | |
| * WebGLDeferredRenderer supports two types of Deferred Renderings. | |
| * One is Classic Deferred Rendering and the other one is | |
| * Light Pre-Pass (Deferred Lighting). | |
| * Classic Deferred Rendering is default. You can use Light Pre-Pass | |
| * by calling .enableLightPrePass( true ) method. | |
| * | |
| * Dependencies | |
| * - THREE.CopyShader | |
| * - THREE.RenderPass | |
| * - THREE.ShaderPass | |
| * - THREE.EffectComposer | |
| * - THREE.FXAAShader | |
| * | |
| * TODO | |
| * - reuse existing glsl | |
| * - shadow | |
| * - optimization | |
| * - MRT (when it's available on Three.js) | |
| * - AmbientLight | |
| * - HemisphereLight | |
| * - PointLight (distance < 0) | |
| * - morphNormals | |
| * - BumpMap | |
| * - ToneMap | |
| * - envMap | |
| * - wrapAround | |
| * - addEffect | |
| */ | |
| THREE.WebGLDeferredRenderer = function ( parameters ) { | |
| parameters = parameters || {}; | |
| // private properties | |
| var _this = this; | |
| var _context; | |
| var _state; | |
| var _width, _height; | |
| // for Classic Deferred Rendering | |
| var _compColor; | |
| var _passColor, _passForward, _passCopy; | |
| // for Light Pre-Pass | |
| var _compReconstruction; | |
| var _passReconstruction; | |
| // for Common | |
| var _compNormalDepth, _compLight, _compFinal; | |
| var _passNormalDepth, _passLight, _passLightFullscreen, _passFinal, _passFXAA; | |
| var _depthTexture; | |
| var _currentCamera; | |
| var _lightScene, _lightFullscreenScene; | |
| var _antialias = false; | |
| var _hasTransparentObject = false; | |
| var _lightPrePass = false; | |
| var _cacheKeepAlive = false; | |
| var _tmpMaterial = new THREE.ShaderMaterial( { visible: false } ); | |
| var _tmpVector3 = new THREE.Vector3(); | |
| // scene/material/light cache for deferred rendering. | |
| // save them at the creation and release | |
| // if they're unused removeThresholdCount frames | |
| // unless _cacheKeepAlive is true. | |
| // scene.uuid -> lightScene, lightFullscreenScene | |
| var _lightScenesCache = {}; | |
| var _lightFullscreenScenesCache = {}; | |
| // object.material.uuid -> deferredMaterial or | |
| // object.material[ n ].uuid -> deferredMaterial | |
| var _normalDepthMaterialsCache = {}; | |
| var _normalDepthShininessMaterialsCache = {}; | |
| var _colorMaterialsCache = {}; | |
| var _reconstructionMaterialsCache = {}; | |
| // originalLight.uuid -> deferredLight | |
| var _deferredLightsCache = {}; | |
| // deferredLight.uuid -> deferredLightMaterial | |
| var _classicDeferredLightMaterialsCache = {}; | |
| var _lightPrePassMaterialsCache = {}; | |
| var _removeThresholdCount = 60; | |
| // deferredMaterials.uuid -> object.material or | |
| // deferredMaterials.uuid -> object.material[ n ] | |
| // save before render and release after render. | |
| var _originalMaterialsTable = {}; | |
| // object.uuid -> originalOnBeforeRender | |
| // save before render and release after render. | |
| var _originalOnBeforeRendersTable = {}; | |
| // object.material.uuid -> object.material.visible or | |
| // object.material[ i ].uuid -> object.material[ i ].visible or | |
| // save before render and release after render. | |
| var _originalVisibleTable = {}; | |
| // external properties | |
| this.renderer = undefined; | |
| this.domElement = undefined; | |
| this.forwardRendering = false; // for debug | |
| // private methods | |
| function init( parameters ) { | |
| _this.renderer = parameters.renderer !== undefined ? parameters.renderer : new THREE.WebGLRenderer(); | |
| _this.domElement = _this.renderer.domElement; | |
| _context = _this.renderer.context; | |
| _state = _this.renderer.state; | |
| _width = parameters.width !== undefined ? parameters.width : _this.renderer.getSize( new THREE.Vector2() ).width; | |
| _height = parameters.height !== undefined ? parameters.height : _this.renderer.getSize( new THREE.Vector2() ).height; | |
| var antialias = parameters.antialias !== undefined ? parameters.antialias : false; | |
| if ( parameters.cacheKeepAlive !== undefined ) _cacheKeepAlive = parameters.cacheKeepAlive; | |
| initDepthTexture(); | |
| initPassNormalDepth(); | |
| initPassColor(); | |
| initPassLight(); | |
| initPassReconstruction(); | |
| initPassFinal(); | |
| _this.setSize( _width, _height ); | |
| _this.setAntialias( antialias ); | |
| _this.enableLightPrePass( false ); | |
| } | |
| function initDepthTexture() { | |
| _depthTexture = new THREE.DepthTexture( | |
| _width, | |
| _height, | |
| THREE.UnsignedInt248Type, | |
| undefined, | |
| undefined, | |
| undefined, | |
| undefined, | |
| undefined, | |
| undefined, | |
| THREE.DepthStencilFormat | |
| ); | |
| } | |
| function initPassNormalDepth() { | |
| _passNormalDepth = new THREE.RenderPass(); | |
| _passNormalDepth.clear = true; | |
| var rt = new THREE.WebGLRenderTarget( _width, _height, { | |
| minFilter: THREE.NearestFilter, | |
| magFilter: THREE.NearestFilter, | |
| format: THREE.RGBAFormat, | |
| type: THREE.FloatType, | |
| stencilBuffer: true, | |
| depthTexture: _depthTexture | |
| } ); | |
| rt.texture.generateMipamps = false; | |
| _compNormalDepth = new THREE.EffectComposer( _this.renderer, rt ); | |
| _compNormalDepth.renderToScreen = false; | |
| _compNormalDepth.addPass( _passNormalDepth ); | |
| } | |
| function initPassColor() { | |
| _passColor = new THREE.RenderPass(); | |
| _passColor.clear = true; | |
| var rt = new THREE.WebGLRenderTarget( _width, _height, { | |
| minFilter: THREE.NearestFilter, | |
| magFilter: THREE.NearestFilter, | |
| format: THREE.RGBAFormat, | |
| type: THREE.FloatType, | |
| depthTexture: _depthTexture | |
| } ); | |
| rt.texture.generateMipamps = false; | |
| _compColor = new THREE.EffectComposer( _this.renderer, rt ); | |
| _compColor.renderToScreen = false; | |
| _compColor.addPass( _passColor ); | |
| } | |
| function initPassLight() { | |
| _passLightFullscreen = new THREE.RenderPass(); | |
| _passLightFullscreen.clear = true; | |
| _passLightFullscreen.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); | |
| _passLight = new THREE.RenderPass(); | |
| _passLight.clear = false; | |
| var rt = new THREE.WebGLRenderTarget( _width, _height, { | |
| minFilter: THREE.NearestFilter, | |
| magFilter: THREE.NearestFilter, | |
| format: THREE.RGBAFormat, | |
| type: THREE.FloatType, | |
| depthTexture: _depthTexture | |
| } ); | |
| rt.texture.generateMipamps = false; | |
| _compLight = new THREE.EffectComposer( _this.renderer, rt ); | |
| _compLight.renderToScreen = false; | |
| _compLight.addPass( _passLightFullscreen ); | |
| _compLight.addPass( _passLight ); | |
| } | |
| function initPassReconstruction() { | |
| _passReconstruction = new THREE.RenderPass(); | |
| _passReconstruction.clear = true; | |
| var rt = new THREE.WebGLRenderTarget( _width, _height, { | |
| minFilter: THREE.NearestFilter, | |
| magFilter: THREE.NearestFilter, | |
| format: THREE.RGBAFormat, | |
| type: THREE.FloatType, | |
| depthTexture: _depthTexture | |
| } ); | |
| rt.texture.generateMipamps = false; | |
| _compReconstruction = new THREE.EffectComposer( _this.renderer, rt ); | |
| _compReconstruction.renderToScreen = false; | |
| _compReconstruction.addPass( _passReconstruction ); | |
| } | |
| function initPassFinal() { | |
| _passFinal = new THREE.ShaderPass( THREE.ShaderDeferred[ 'final' ] ); | |
| _passFinal.clear = true; | |
| _passFinal.uniforms.samplerResult.value = _compLight.renderTarget2.texture; | |
| _passFinal.material.blending = THREE.NoBlending; | |
| _passFinal.material.depthWrite = false; | |
| _passFinal.material.depthTest = false; | |
| _passForward = new THREE.RenderPass(); | |
| _passForward.clear = false; | |
| _passCopy = new THREE.ShaderPass( THREE.CopyShader ); | |
| _passFXAA = new THREE.ShaderPass( THREE.FXAAShader ); | |
| var rt = new THREE.WebGLRenderTarget( _width, _height, { | |
| minFilter: THREE.NearestFilter, | |
| magFilter: THREE.LinearFilter, | |
| format: THREE.RGBFormat, | |
| type: THREE.UnsignedByteType, | |
| depthTexture: _depthTexture | |
| } ); | |
| rt.texture.generateMipamps = false; | |
| _compFinal = new THREE.EffectComposer( _this.renderer, rt ); | |
| _compFinal.addPass( _passFinal ); | |
| _compFinal.addPass( _passForward ); | |
| _compFinal.addPass( _passCopy ); | |
| _compFinal.addPass( _passFXAA ); | |
| } | |
| function initLightScene( scene ) { | |
| var lightSceneData = _lightScenesCache[ scene.uuid ]; | |
| var lightFullscreenSceneData = _lightFullscreenScenesCache[ scene.uuid ]; | |
| if ( lightSceneData === undefined ) { | |
| var s = new THREE.Scene(); | |
| s.userData.lights = {}; | |
| lightSceneData = createCacheData(); | |
| lightSceneData.scene = s; | |
| _lightScenesCache[ scene.uuid ] = lightSceneData; | |
| } | |
| if ( lightFullscreenSceneData === undefined ) { | |
| var s = new THREE.Scene(); | |
| s.userData.lights = {}; | |
| var emissiveLight = createDeferredEmissiveLight(); | |
| s.userData.emissiveLight = emissiveLight; | |
| s.add( emissiveLight ); | |
| lightFullscreenSceneData = createCacheData(); | |
| lightFullscreenSceneData.scene = s; | |
| _lightFullscreenScenesCache[ scene.uuid ] = lightFullscreenSceneData; | |
| } | |
| lightSceneData.used = true; | |
| lightFullscreenSceneData.used = true; | |
| var lightScene = lightSceneData.scene; | |
| var lightFullscreenScene = lightFullscreenSceneData.scene; | |
| // emissiveLight is only for Classic Deferred Rendering | |
| lightFullscreenScene.userData.emissiveLight.visible = ! _lightPrePass; | |
| _lightScene = lightScene; | |
| _lightFullscreenScene = lightFullscreenScene; | |
| } | |
| function getMaterialFromCacheOrCreate( originalMaterial, cache, createFunc, updateFunc ) { | |
| var data = cache[ originalMaterial.uuid ]; | |
| if ( data === undefined ) { | |
| data = createCacheData(); | |
| data.material = createFunc( originalMaterial ); | |
| cache[ originalMaterial.uuid ] = data; | |
| } | |
| data.used = true; | |
| updateFunc( data.material, originalMaterial ); | |
| _originalMaterialsTable[ data.material.uuid ] = originalMaterial; | |
| return data.material; | |
| } | |
| function overrideMaterialAndOnBeforeRender( object, getMaterialFunc, onBeforeRender ) { | |
| if ( object.material === undefined ) return; | |
| if ( Array.isArray( object.material ) ) { | |
| for ( var i = 0, il = object.material.length; i < il; i ++ ) { | |
| object.material[ i ] = getMaterialFunc( object.material[ i ] ); | |
| } | |
| } else { | |
| object.material = getMaterialFunc( object.material ); | |
| } | |
| object.onBeforeRender = onBeforeRender; | |
| } | |
| function restoreOriginalMaterial( object ) { | |
| if ( object.material === undefined ) return; | |
| if ( Array.isArray( object.material ) ) { | |
| for ( var i = 0, il = object.material.length; i < il; i ++ ) { | |
| object.material[ i ] = _originalMaterialsTable[ object.material[ i ].uuid ]; | |
| } | |
| } else { | |
| object.material = _originalMaterialsTable[ object.material.uuid ]; | |
| } | |
| } | |
| function setMaterialNormalDepth( object ) { | |
| overrideMaterialAndOnBeforeRender( object, getNormalDepthMaterial, updateDeferredNormalDepthUniforms ); | |
| } | |
| function getNormalDepthMaterial( originalMaterial ) { | |
| return getMaterialFromCacheOrCreate( | |
| originalMaterial, | |
| ( _lightPrePass ) ? _normalDepthShininessMaterialsCache : _normalDepthMaterialsCache, | |
| createDeferredNormalDepthMaterial, | |
| updateDeferredNormalDepthMaterial | |
| ); | |
| } | |
| function createDeferredNormalDepthMaterial( originalMaterial ) { | |
| var shader = ( _lightPrePass ) ? THREE.ShaderDeferred[ 'normalDepthShininess' ] : THREE.ShaderDeferred[ 'normalDepth' ]; | |
| return new THREE.ShaderMaterial( { | |
| uniforms: Object.assign( {}, shader.uniforms ), | |
| fragmentShader: shader.fragmentShader, | |
| vertexShader: shader.vertexShader, | |
| blending: THREE.NoBlending | |
| } ); | |
| } | |
| function updateDeferredNormalDepthMaterial( material, originalMaterial ) { | |
| if ( originalMaterial.skinning !== undefined ) material.skinning = originalMaterial.skinning; | |
| if ( originalMaterial.morphTargets !== undefined ) material.morphTargets = originalMaterial.morphTargets; | |
| if ( originalMaterial.visible === true ) { | |
| material.visible = ! originalMaterial.transparent; | |
| } else { | |
| material.visible = false; | |
| } | |
| } | |
| function updateDeferredNormalDepthUniforms( renderer, scene, camera, geometry, material, group ) { | |
| if ( ! _lightPrePass ) return; | |
| var originalMaterial = _originalMaterialsTable[ material.uuid ]; | |
| if ( originalMaterial === undefined || originalMaterial.shininess === undefined ) return; | |
| material.uniforms.shininess.value = originalMaterial.shininess; | |
| } | |
| function setMaterialColor( object ) { | |
| overrideMaterialAndOnBeforeRender( object, getColorMaterial, updateDeferredColorUniforms ); | |
| } | |
| function getColorMaterial( originalMaterial ) { | |
| return getMaterialFromCacheOrCreate( | |
| originalMaterial, | |
| _colorMaterialsCache, | |
| createDeferredColorMaterial, | |
| updateDeferredColorMaterial | |
| ); | |
| } | |
| function createDeferredColorMaterial( originalMaterial ) { | |
| var shader = THREE.ShaderDeferred[ 'color' ]; | |
| var material = new THREE.ShaderMaterial( { | |
| uniforms: Object.assign( {}, shader.uniforms ), | |
| fragmentShader: shader.fragmentShader, | |
| vertexShader: shader.vertexShader, | |
| blending: THREE.NoBlending | |
| } ); | |
| if ( originalMaterial.map !== undefined ) material.map = originalMaterial.map; | |
| return material; | |
| } | |
| function updateDeferredColorMaterial( material, originalMaterial ) { | |
| if ( originalMaterial.map !== undefined ) material.map = originalMaterial.map; | |
| if ( originalMaterial.skinning !== undefined ) material.skinning = originalMaterial.skinning; | |
| if ( originalMaterial.morphTargets !== undefined ) material.morphTargets = originalMaterial.morphTargets; | |
| if ( originalMaterial.visible === true ) { | |
| material.visible = ! originalMaterial.transparent; | |
| } else { | |
| material.visible = false; | |
| } | |
| } | |
| function updateDeferredColorUniforms( renderer, scene, camera, geometry, material, group ) { | |
| var originalMaterial = _originalMaterialsTable[ material.uuid ]; | |
| var uniforms = material.uniforms; | |
| var diffuse, emissive; | |
| if ( originalMaterial.isMeshBasicMaterial === true ) { | |
| emissive = originalMaterial.color; | |
| } else { | |
| diffuse = originalMaterial.color; | |
| emissive = originalMaterial.emissive; | |
| } | |
| var specular = originalMaterial.specular; | |
| var shininess = originalMaterial.shininess; | |
| var map = originalMaterial.map; | |
| if ( diffuse !== undefined ) uniforms.diffuse.value.copy( diffuse ); | |
| if ( emissive !== undefined ) uniforms.emissive.value.copy( emissive ); | |
| if ( specular !== undefined ) uniforms.specular.value.copy( specular ); | |
| if ( shininess !== undefined && uniforms.shininess !== undefined ) uniforms.shininess.value = shininess; | |
| if ( map !== undefined ) uniforms.map.value = map; | |
| } | |
| function setMaterialReconstruction( object ) { | |
| overrideMaterialAndOnBeforeRender( object, getReconstructionMaterial, updateDeferredReconstructionUniforms ); | |
| } | |
| function getReconstructionMaterial( originalMaterial ) { | |
| if ( originalMaterial.transparent === true ) { | |
| _originalMaterialsTable[ originalMaterial.uuid ] = originalMaterial; | |
| return originalMaterial; | |
| } | |
| return getMaterialFromCacheOrCreate( | |
| originalMaterial, | |
| _reconstructionMaterialsCache, | |
| createDeferredReconstructionMaterial, | |
| updateDeferredReconstructionMaterial | |
| ); | |
| } | |
| function createDeferredReconstructionMaterial( originalMaterial ) { | |
| var shader = THREE.ShaderDeferred[ 'reconstruction' ]; | |
| var material = new THREE.ShaderMaterial( { | |
| uniforms: Object.assign( {}, shader.uniforms ), | |
| fragmentShader: shader.fragmentShader, | |
| vertexShader: shader.vertexShader, | |
| blending: THREE.NoBlending | |
| } ); | |
| if ( originalMaterial.map !== undefined ) material.map = originalMaterial.map; | |
| return material; | |
| } | |
| function updateDeferredReconstructionMaterial( material, originalMaterial ) { | |
| updateDeferredColorMaterial( material, originalMaterial ); | |
| } | |
| function updateDeferredReconstructionUniforms( renderer, scene, camera, geometry, material, group ) { | |
| if ( material.transparent === true ) { | |
| // 'this' is object here because this method is set as object.onBefore() | |
| var onBeforeRender = _originalOnBeforeRendersTable[ this.uuid ]; | |
| if ( onBeforeRender ) { | |
| onBeforeRender.call( this, renderer, scene, camera, geometry, material, group ); | |
| } | |
| return; | |
| } | |
| updateDeferredColorUniforms( renderer, scene, camera, geometry, material, group ); | |
| material.uniforms.samplerLight.value = _compLight.renderTarget2.texture; | |
| } | |
| function setVisibleForForwardRendering( object ) { | |
| if ( object.material === undefined ) return; | |
| if ( Array.isArray( object.material ) ) { | |
| for ( var i = 0, il = object.material.length; i < il; i ++ ) { | |
| if ( _originalVisibleTable[ object.material[ i ].uuid ] === undefined ) { | |
| _originalVisibleTable[ object.material[ i ].uuid ] = object.material[ i ].visible; | |
| object.material[ i ].visible = object.material[ i ].transparent && object.material[ i ].visible; | |
| } | |
| } | |
| } else { | |
| if ( _originalVisibleTable[ object.material.uuid ] === undefined ) { | |
| _originalVisibleTable[ object.material.uuid ] = object.material.visible; | |
| object.material.visible = object.material.transparent && object.material.visible; | |
| } | |
| } | |
| } | |
| function restoreVisible( object ) { | |
| if ( object.material === undefined ) return; | |
| if ( Array.isArray( object.material ) ) { | |
| for ( var i = 0, il = object.material.length; i < il; i ++ ) { | |
| object.material[ i ].visible = _originalVisibleTable[ object.material[ i ].uuid ]; | |
| } | |
| } else { | |
| object.material.visible = _originalVisibleTable[ object.material.uuid ]; | |
| } | |
| } | |
| function createDeferredEmissiveLight() { | |
| var shader = THREE.ShaderDeferred[ 'emissiveLight' ]; | |
| var material = new THREE.ShaderMaterial( { | |
| uniforms: Object.assign( {}, shader.uniforms ), | |
| vertexShader: shader.vertexShader, | |
| fragmentShader: shader.fragmentShader, | |
| blending: THREE.NoBlending, | |
| depthWrite: false | |
| } ); | |
| var geometry = new THREE.PlaneBufferGeometry( 2, 2 ); | |
| var mesh = new THREE.Mesh( geometry, material ); | |
| mesh.onBeforeRender = function ( renderer, scene, camera, geometry, material, group ) { | |
| material.uniforms.samplerColor.value = _compColor.renderTarget2.texture; | |
| }; | |
| return mesh; | |
| } | |
| function createDeferredLight( originalLight ) { | |
| if ( originalLight.isPointLight ) { | |
| return createDeferredPointLight( originalLight ); | |
| } else if ( originalLight.isSpotLight ) { | |
| return createDeferredSpotLight( originalLight ); | |
| } else if ( originalLight.isDirectionalLight ) { | |
| return createDeferredDirectionalLight( originalLight ); | |
| } | |
| return null; | |
| } | |
| function createDeferredLightMaterial( originalLight ) { | |
| if ( originalLight.isPointLight ) { | |
| return createDeferredPointLightMaterial(); | |
| } else if ( originalLight.isSpotLight ) { | |
| return createDeferredSpotLightMaterial(); | |
| } else if ( originalLight.isDirectionalLight ) { | |
| return createDeferredDirectionalLightMaterial(); | |
| } | |
| return null; | |
| } | |
| function getDeferredLightMaterial( light ) { | |
| var cache = ( _lightPrePass ) ? _lightPrePassMaterialsCache : _classicDeferredLightMaterialsCache; | |
| var data = cache[ light.uuid ]; | |
| if ( data === undefined ) { | |
| data = createCacheData(); | |
| data.material = createDeferredLightMaterial( light.userData.originalLight ); | |
| cache[ light.uuid ] = data; | |
| } | |
| data.used = true; | |
| return data.material; | |
| } | |
| function updateDeferredLight( light ) { | |
| var originalLight = light.userData.originalLight; | |
| if ( originalLight.isPointLight ) { | |
| updateDeferredPointLight( light ); | |
| } | |
| } | |
| function createDeferredLightMesh( light, geometry ) { | |
| var mesh = new THREE.Mesh( geometry, _tmpMaterial ); | |
| mesh.userData.originalLight = light; | |
| return mesh; | |
| } | |
| function createDeferredLightShaderMaterial( shader ) { | |
| var material = new THREE.ShaderMaterial( { | |
| uniforms: Object.assign( {}, shader.uniforms ), | |
| vertexShader: shader.vertexShader, | |
| fragmentShader: shader.fragmentShader, | |
| transparent: true, | |
| blending: THREE.AdditiveBlending, | |
| depthWrite: false | |
| } ); | |
| if ( _lightPrePass ) material.premultipliedAlpha = true; | |
| return material; | |
| } | |
| function updateDeferredLightCommonUniforms( uniforms ) { | |
| if ( _lightPrePass ) { | |
| uniforms.samplerNormalDepthShininess.value = _compNormalDepth.renderTarget2.texture; | |
| } else { | |
| uniforms.samplerNormalDepth.value = _compNormalDepth.renderTarget2.texture; | |
| uniforms.samplerColor.value = _compColor.renderTarget2.texture; | |
| } | |
| } | |
| function createDeferredPointLight( light ) { | |
| var mesh = createDeferredLightMesh( light, new THREE.SphereBufferGeometry( 1, 16, 8 ) ); | |
| mesh.onBeforeRender = updateDeferredPointLightUniforms; | |
| return mesh; | |
| } | |
| /* | |
| * optimization: | |
| * Renders PointLight only back face with stencil test. | |
| */ | |
| function createDeferredPointLightMaterial() { | |
| var shader = ( _lightPrePass ) ? THREE.ShaderDeferred[ 'pointLightPre' ] : THREE.ShaderDeferred[ 'pointLight' ]; | |
| var material = createDeferredLightShaderMaterial( shader ); | |
| material.side = THREE.BackSide; | |
| material.depthFunc = THREE.GreaterEqualDepth; | |
| return material; | |
| } | |
| function updateDeferredPointLight( light ) { | |
| var originalLight = light.userData.originalLight; | |
| var distance = originalLight.distance; | |
| if ( distance > 0 ) { | |
| light.scale.set( 1, 1, 1 ).multiplyScalar( distance ); | |
| light.position.setFromMatrixPosition( originalLight.matrixWorld ); | |
| } | |
| } | |
| function updateDeferredPointLightUniforms( renderer, scene, camera, geometry, material, group ) { | |
| var light = this; | |
| var originalLight = light.userData.originalLight; | |
| var distance = originalLight.distance; | |
| var uniforms = material.uniforms; | |
| uniforms.lightColor.value.copy( originalLight.color ); | |
| if ( distance > 0 ) { | |
| uniforms.lightRadius.value = distance; | |
| uniforms.lightIntensity.value = originalLight.intensity; | |
| uniforms.lightPositionVS.value.setFromMatrixPosition( originalLight.matrixWorld ).applyMatrix4( _currentCamera.matrixWorldInverse ); | |
| } else { | |
| uniforms.lightRadius.value = Infinity; | |
| } | |
| updateDeferredLightCommonUniforms( uniforms ); | |
| } | |
| function createDeferredSpotLight( light ) { | |
| var mesh = createDeferredLightMesh( light, new THREE.PlaneBufferGeometry( 2, 2 ) ); | |
| mesh.onBeforeRender = updateDeferredSpotLightUniforms; | |
| return mesh; | |
| } | |
| function createDeferredSpotLightMaterial() { | |
| var shader = ( _lightPrePass ) ? THREE.ShaderDeferred[ 'spotLightPre' ] : THREE.ShaderDeferred[ 'spotLight' ]; | |
| var material = createDeferredLightShaderMaterial( shader ); | |
| material.depthTest = false; | |
| return material; | |
| } | |
| function updateDeferredSpotLightUniforms( renderer, scene, camera, geometry, material, group ) { | |
| var light = this; | |
| var originalLight = light.userData.originalLight; | |
| var uniforms = light.material.uniforms; | |
| uniforms.lightAngle.value = originalLight.angle; | |
| uniforms.lightColor.value.copy( originalLight.color ); | |
| uniforms.lightIntensity.value = originalLight.intensity; | |
| uniforms.lightPositionVS.value.setFromMatrixPosition( originalLight.matrixWorld ).applyMatrix4( _currentCamera.matrixWorldInverse ); | |
| var vec = uniforms.lightDirectionVS.value; | |
| var vec2 = _tmpVector3; | |
| vec.setFromMatrixPosition( originalLight.matrixWorld ); | |
| vec2.setFromMatrixPosition( originalLight.target.matrixWorld ); | |
| vec.sub( vec2 ).normalize().transformDirection( _currentCamera.matrixWorldInverse ); | |
| updateDeferredLightCommonUniforms( uniforms ); | |
| } | |
| function createDeferredDirectionalLight( light ) { | |
| var mesh = createDeferredLightMesh( light, new THREE.PlaneBufferGeometry( 2, 2 ) ); | |
| mesh.onBeforeRender = updateDeferredDirectionalLightUniforms; | |
| return mesh; | |
| } | |
| function createDeferredDirectionalLightMaterial() { | |
| var shader = ( _lightPrePass ) ? THREE.ShaderDeferred[ 'directionalLightPre' ] : THREE.ShaderDeferred[ 'directionalLight' ]; | |
| var material = createDeferredLightShaderMaterial( shader ); | |
| material.depthTest = false; | |
| return material; | |
| } | |
| function updateDeferredDirectionalLightUniforms( renderer, scene, camera, geometry, material, group ) { | |
| var light = this; | |
| var originalLight = light.userData.originalLight; | |
| var uniforms = light.material.uniforms; | |
| uniforms.lightColor.value.copy( originalLight.color ); | |
| uniforms.lightIntensity.value = originalLight.intensity; | |
| var vec = uniforms.lightDirectionVS.value; | |
| var vec2 = _tmpVector3; | |
| vec.setFromMatrixPosition( originalLight.matrixWorld ); | |
| vec2.setFromMatrixPosition( originalLight.target.matrixWorld ); | |
| vec.sub( vec2 ).normalize().transformDirection( _currentCamera.matrixWorldInverse ); | |
| updateDeferredLightCommonUniforms( uniforms ); | |
| } | |
| function saveOriginalOnBeforeRenderAndCheckTransparency( object ) { | |
| if ( object.material === undefined ) return; | |
| _originalOnBeforeRendersTable[ object.uuid ] = object.onBeforeRender; | |
| // _hasTransparentObject is used only for Classic Deferred Rendering | |
| if ( _hasTransparentObject || _lightPrePass ) return; | |
| if ( ! object.visible ) return; | |
| if ( Array.isArray( object.material ) ) { | |
| for ( var i = 0, il = object.material.length; i < il; i ++ ) { | |
| if ( object.material[ i ].visible === true && object.material[ i ].transparent === true ) { | |
| _hasTransparentObject = true; | |
| break; | |
| } | |
| } | |
| } else { | |
| if ( object.material.visible === true && object.material.transparent === true ) _hasTransparentObject = true; | |
| } | |
| } | |
| function restoreOriginalOnBeforeRender( object ) { | |
| if ( object.material === undefined ) return; | |
| object.onBeforeRender = _originalOnBeforeRendersTable[ object.uuid ]; | |
| } | |
| function addDeferredLightsToLightScene( object ) { | |
| if ( object.isLight !== true ) return; | |
| var data = _deferredLightsCache[ object.uuid ]; | |
| if ( data === undefined ) { | |
| data = createCacheData(); | |
| data.light = createDeferredLight( object ); | |
| _deferredLightsCache[ object.uuid ] = data; | |
| } | |
| data.used = true; | |
| var light = data.light; | |
| if ( light === null ) return; | |
| var scene = ( object.isPointLight === true ) ? _lightScene : _lightFullscreenScene; | |
| var lights = scene.userData.lights; | |
| if ( lights[ light.uuid ] === undefined ) { | |
| scene.add( light ); | |
| lights[ light.uuid ] = { | |
| light: light, | |
| found: true | |
| }; | |
| } | |
| lights[ light.uuid ].found = true; | |
| } | |
| function updateDeferredLightsInLightScene( scene ) { | |
| var lights = scene.userData.lights; | |
| var keys = Object.keys( lights ); | |
| for ( var i = 0, il = keys.length; i < il; i ++ ) { | |
| var key = keys[ i ]; | |
| if ( lights[ key ].found === false ) { | |
| scene.remove( lights[ key ].light ); | |
| delete lights[ key ]; | |
| } else { | |
| var light = lights[ key ].light; | |
| light.material = getDeferredLightMaterial( light ); | |
| updateDeferredLight( light ); | |
| lights[ key ].found = false; | |
| } | |
| } | |
| } | |
| function updateDeferredCommonUniforms( camera ) { | |
| var uniforms = THREE.ShaderDeferredCommon[ 'commonUniforms' ]; | |
| uniforms.viewWidth.value = _width; | |
| uniforms.viewHeight.value = _height; | |
| uniforms.matProjInverse.value.getInverse( camera.projectionMatrix ); | |
| } | |
| function enableFinalPasses() { | |
| if ( _lightPrePass ) { | |
| _passForward.enabled = false; | |
| _passCopy.enabled = false; | |
| if ( _antialias ) { | |
| _passFXAA.enabled = true; | |
| } else { | |
| _passFXAA.enabled = false; | |
| } | |
| } else { | |
| if ( _hasTransparentObject ) { | |
| if ( _antialias ) { | |
| _passForward.enabled = true; | |
| _passCopy.enabled = false; | |
| _passFXAA.enabled = true; | |
| } else { | |
| _passForward.enabled = true; | |
| _passCopy.enabled = true; | |
| _passFXAA.enabled = false; | |
| } | |
| } else { | |
| if ( _antialias ) { | |
| _passForward.enabled = false; | |
| _passCopy.enabled = false; | |
| _passFXAA.enabled = true; | |
| } else { | |
| _passForward.enabled = false; | |
| _passCopy.enabled = false; | |
| _passFXAA.enabled = false; | |
| } | |
| } | |
| } | |
| } | |
| function createCacheData() { | |
| return { | |
| used: true, | |
| keepAlive: _cacheKeepAlive, | |
| count: 0 | |
| }; | |
| } | |
| function cleanupCache( cache ) { | |
| var keys = Object.keys( cache ); | |
| for ( var i = 0, il = keys.length; i < il; i ++ ) { | |
| var key = keys[ i ]; | |
| if ( cache[ key ].used === false ) { | |
| cache[ key ].count ++; | |
| if ( cache[ key ].keepAlive === false && cache[ key ].count > _removeThresholdCount ) { | |
| delete cache[ key ]; | |
| } | |
| } else { | |
| cache[ key ].used = false; | |
| cache[ key ].count = 0; | |
| } | |
| } | |
| } | |
| function cleanupTable( table ) { | |
| var keys = Object.keys( table ); | |
| for ( var i = 0, il = keys.length; i < il; i ++ ) { | |
| var key = keys[ i ]; | |
| table[ key ] = undefined; | |
| } | |
| } | |
| function cleanupCaches() { | |
| cleanupCache( _lightScenesCache ); | |
| cleanupCache( _lightFullscreenScenesCache ); | |
| cleanupCache( _normalDepthMaterialsCache ); | |
| cleanupCache( _normalDepthShininessMaterialsCache ); | |
| cleanupCache( _colorMaterialsCache ); | |
| cleanupCache( _reconstructionMaterialsCache ); | |
| cleanupCache( _classicDeferredLightMaterialsCache ); | |
| cleanupCache( _lightPrePassMaterialsCache ); | |
| cleanupCache( _deferredLightsCache ); | |
| cleanupTable( _originalMaterialsTable ); | |
| cleanupTable( _originalOnBeforeRendersTable ); | |
| cleanupTable( _originalVisibleTable ); | |
| } | |
| /* | |
| * Classic Deferred Rendering | |
| * | |
| * 1) g-buffer normal + depth pass | |
| * | |
| * RGB: normal | |
| * A: depth | |
| * | |
| * | |
| * Light Pre-Pass Rendering | |
| * | |
| * 1') g-buffer normal + depth pass + shininess | |
| * | |
| * RG: normal | |
| * B: shininess | |
| * A: depth | |
| */ | |
| function renderNormalDepth( scene, camera ) { | |
| scene.traverse( setMaterialNormalDepth ); | |
| _passNormalDepth.scene = scene; | |
| _passNormalDepth.camera = camera; | |
| _this.renderer.autoClearDepth = true; | |
| _this.renderer.autoClearStencil = true; | |
| _state.buffers.stencil.setTest( true ); | |
| _state.buffers.stencil.setFunc( _context.ALWAYS, 1, 0xffffffff ); | |
| _state.buffers.stencil.setOp( _context.REPLACE, _context.REPLACE, _context.REPLACE ); | |
| _compNormalDepth.render(); | |
| scene.traverse( restoreOriginalMaterial ); | |
| } | |
| /* | |
| * Classic Deferred Rendering | |
| * | |
| * 2) g-buffer color pass | |
| * | |
| * R: diffuse | |
| * G: emissive | |
| * B: specular | |
| * A: shininess | |
| */ | |
| function renderColor( scene, camera ) { | |
| scene.traverse( setMaterialColor ); | |
| _passColor.scene = scene; | |
| _passColor.camera = camera; | |
| _this.renderer.autoClearDepth = false; | |
| _this.renderer.autoClearStencil = false; | |
| _state.buffers.stencil.setFunc( _context.EQUAL, 1, 0xffffffff ); | |
| _state.buffers.stencil.setOp( _context.KEEP, _context.KEEP, _context.KEEP ); | |
| _compColor.render(); | |
| scene.traverse( restoreOriginalMaterial ); | |
| } | |
| /* | |
| * Classic Deferred Rendering | |
| * | |
| * 3) light pass | |
| */ | |
| function renderLight( scene, camera ) { | |
| scene.traverse( addDeferredLightsToLightScene ); | |
| updateDeferredLightsInLightScene( _lightScene ); | |
| updateDeferredLightsInLightScene( _lightFullscreenScene ); | |
| _passLight.scene = _lightScene; | |
| _passLight.camera = camera; | |
| _passLightFullscreen.scene = _lightFullscreenScene; | |
| _this.renderer.autoClearDepth = false; | |
| _this.renderer.autoClearStencil = false; | |
| _compLight.render(); | |
| _state.buffers.stencil.setTest( false ); | |
| } | |
| /* | |
| * Light Pre-Pass Rendering | |
| * | |
| * 2') Light pre pass | |
| */ | |
| function renderLightPre( scene, camera ) { | |
| scene.traverse( addDeferredLightsToLightScene ); | |
| updateDeferredLightsInLightScene( _lightScene ); | |
| updateDeferredLightsInLightScene( _lightFullscreenScene ); | |
| _passLight.scene = _lightScene; | |
| _passLight.camera = camera; | |
| _passLightFullscreen.scene = _lightFullscreenScene; | |
| _this.renderer.autoClearDepth = false; | |
| _this.renderer.autoClearStencil = false; | |
| _state.buffers.stencil.setFunc( _context.EQUAL, 1, 0xffffffff ); | |
| _state.buffers.stencil.setOp( _context.KEEP, _context.KEEP, _context.KEEP ); | |
| _compLight.render(); | |
| } | |
| /* | |
| * Light Pre-Pass Rendering | |
| * | |
| * 3') Reconstruction pass | |
| * | |
| * Transprency handling: | |
| * Here renders transparent objects with normal forward rendering. | |
| */ | |
| function renderReconstruction( scene, camera ) { | |
| scene.traverse( setMaterialReconstruction ); | |
| _passReconstruction.scene = scene; | |
| _passReconstruction.camera = camera; | |
| _this.renderer.autoClearDepth = false; | |
| _this.renderer.autoClearStencil = false; | |
| _compReconstruction.render(); | |
| _state.buffers.stencil.setTest( false ); | |
| scene.traverse( restoreOriginalMaterial ); | |
| } | |
| /* | |
| * Classic Deferred Rendering | |
| * | |
| * 4) Final pass | |
| * | |
| * transparency handling: | |
| * If there's any transparent objects, here renders them on the deferred rendering result | |
| * with normal forward rendering. This may be the easist way but heavy. | |
| * We should consider any better ways someday. | |
| * | |
| * | |
| * Light Pre-Pass Rendering | |
| * | |
| * 4') Final pass | |
| * | |
| * | |
| * Common | |
| * | |
| * antialias handling: | |
| * Here uses postprocessing FXAA for antialias. | |
| * | |
| */ | |
| function renderFinal( scene, camera ) { | |
| if ( ! _lightPrePass && _hasTransparentObject ) { | |
| scene.traverse( setVisibleForForwardRendering ); | |
| scene.traverse( restoreOriginalOnBeforeRender ); | |
| _passForward.scene = scene; | |
| _passForward.camera = camera; | |
| } | |
| enableFinalPasses(); | |
| _this.renderer.autoClearDepth = false; | |
| _this.renderer.autoClearStencil = false; | |
| _compFinal.render(); | |
| if ( ! _lightPrePass && _hasTransparentObject ) { | |
| scene.traverse( restoreVisible ); | |
| } | |
| } | |
| // external APIs | |
| this.setSize = function ( width, height ) { | |
| _width = width; | |
| _height = height; | |
| this.renderer.setSize( _width, _height ); | |
| _compNormalDepth.setSize( _width, _height ); | |
| _compColor.setSize( _width, _height ); | |
| _compLight.setSize( _width, _height ); | |
| _compReconstruction.setSize( _width, _height ); | |
| _compFinal.setSize( _width, _height ); | |
| _depthTexture.image.width = _width; | |
| _depthTexture.image.height = _height; | |
| _depthTexture.needsUpdate = true; | |
| _passFXAA.uniforms.resolution.value.set( 1 / _width, 1 / _height ); | |
| }; | |
| this.setAntialias = function ( enabled ) { | |
| _antialias = enabled; | |
| }; | |
| this.enableLightPrePass = function ( enabled ) { | |
| _lightPrePass = enabled; | |
| _passFinal.uniforms.samplerResult.value = ( _lightPrePass ) ? _compReconstruction.renderTarget2.texture : _compLight.renderTarget2.texture; | |
| }; | |
| this.render = function ( scene, camera ) { | |
| // for debug to compare with normal forward rendering | |
| if ( this.forwardRendering ) { | |
| this.renderer.render( scene, camera ); | |
| return; | |
| } | |
| var currentSceneAutoUpdate = scene.autoUpdate; | |
| var currentAutoClearColor = this.renderer.autoClearColor; | |
| var currentAutoClearDepth = this.renderer.autoClearDepth; | |
| var currentAutoClearStencil = this.renderer.autoClearStencil; | |
| _currentCamera = camera; | |
| initLightScene( scene ); | |
| scene.autoUpdate = false; | |
| scene.updateMatrixWorld(); | |
| _hasTransparentObject = false; | |
| scene.traverse( saveOriginalOnBeforeRenderAndCheckTransparency ); | |
| updateDeferredCommonUniforms( camera ); | |
| renderNormalDepth( scene, camera ); | |
| if ( _lightPrePass ) { | |
| renderLightPre( scene, camera ); | |
| renderReconstruction( scene, camera ); | |
| } else { | |
| renderColor( scene, camera ); | |
| renderLight( scene, camera ); | |
| } | |
| renderFinal( scene, camera ); | |
| scene.traverse( restoreOriginalOnBeforeRender ); | |
| cleanupCaches(); | |
| scene.autoUpdate = currentSceneAutoUpdate; | |
| this.renderer.autoClearColor = currentAutoClearColor; | |
| this.renderer.autoClearDepth = currentAutoClearDepth; | |
| this.renderer.autoClearStencil = currentAutoClearStencil; | |
| }; | |
| // initialize | |
| init( parameters ); | |
| }; | |
| THREE.DeferredShaderChunk = { | |
| packVector3: [ | |
| "float vec3_to_float( vec3 data ) {", | |
| " const float unit = 255.0/256.0;", | |
| " highp float compressed = fract( data.x * unit ) + floor( data.y * unit * 255.0 ) + floor( data.z * unit * 255.0 ) * 255.0;", | |
| " return compressed;", | |
| "}" | |
| ].join( "\n" ), | |
| unpackFloat: [ | |
| "vec3 float_to_vec3( float data ) {", | |
| " const float unit = 255.0;", | |
| " vec3 uncompressed;", | |
| " uncompressed.x = fract( data );", | |
| " float zInt = floor( data / unit );", | |
| " uncompressed.z = fract( zInt / unit );", | |
| " uncompressed.y = fract( floor( data - ( zInt * unit ) ) / unit );", | |
| " return uncompressed;", | |
| "}" | |
| ].join( "\n" ), | |
| // Refer to http://aras-p.info/texts/CompactNormalStorage.html | |
| packNormal: [ | |
| "vec2 normal_to_vec2( vec3 normal ) {", | |
| " return normal.xy / sqrt( normal.z * 8.0 + 8.0 ) + 0.5;", | |
| "}" | |
| ].join( "\n" ), | |
| unpackVector2: [ | |
| "vec3 vec2_to_normal( vec2 data ) {", | |
| " vec2 fenc = data * 4.0 - 2.0;", | |
| " float f = dot( fenc, fenc );", | |
| " float g = sqrt( 1.0 - f / 4.0 );", | |
| " vec3 normal;", | |
| " normal.xy = fenc * g;", | |
| " normal.z = 1.0 - f / 2.0;", | |
| " return normal;", | |
| "}" | |
| ].join( "\n" ), | |
| computeTextureCoord: [ | |
| "vec2 texCoord = gl_FragCoord.xy / vec2( viewWidth, viewHeight );" | |
| ].join( "\n" ), | |
| packNormalDepth: [ | |
| "vec4 packedNormalDepth;", | |
| "packedNormalDepth.xyz = normal * 0.5 + 0.5;", | |
| "packedNormalDepth.w = position.z / position.w;" | |
| ].join( "\n" ), | |
| unpackNormalDepth: [ | |
| "vec4 normalDepthMap = texture2D( samplerNormalDepth, texCoord );", | |
| "float depth = normalDepthMap.w;", | |
| "if ( depth == 0.0 ) discard;", | |
| "vec3 normal = normalDepthMap.xyz * 2.0 - 1.0;" | |
| ].join( "\n" ), | |
| packNormalDepthShininess: [ | |
| "vec4 packedNormalDepthShininess;", | |
| "packedNormalDepthShininess.xy = normal_to_vec2( normal );", | |
| "packedNormalDepthShininess.z = shininess;", | |
| "packedNormalDepthShininess.w = position.z / position.w;" | |
| ].join( "\n" ), | |
| unpackNormalDepthShininess: [ | |
| "vec4 normalDepthMap = texture2D( samplerNormalDepthShininess, texCoord );", | |
| "float depth = normalDepthMap.w;", | |
| "if ( depth == 0.0 ) discard;", | |
| "vec3 normal = vec2_to_normal( normalDepthMap.xy );", | |
| "float shininess = normalDepthMap.z;" | |
| ].join( "\n" ), | |
| packColor: [ | |
| "vec4 packedColor;", | |
| "packedColor.x = vec3_to_float( diffuseColor.rgb );", | |
| "packedColor.y = vec3_to_float( emissiveColor );", | |
| "packedColor.z = vec3_to_float( specularColor );", | |
| "packedColor.w = shininess;" | |
| ].join( "\n" ), | |
| unpackColor: [ | |
| "vec4 colorMap = texture2D( samplerColor, texCoord );", | |
| "vec3 diffuseColor = float_to_vec3( colorMap.x );", | |
| "vec3 emissiveColor = float_to_vec3( colorMap.y );", | |
| "vec3 specularColor = float_to_vec3( colorMap.z );", | |
| "float shininess = colorMap.w;" | |
| ].join( "\n" ), | |
| packLight: [ | |
| "vec4 packedLight;", | |
| "packedLight.xyz = lightIntensity * lightColor * max( dot( lightVector, normal ), 0.0 ) * attenuation;", | |
| "packedLight.w = lightIntensity * specular * max( dot( lightVector, normal ), 0.0 ) * attenuation;" | |
| ].join( "\n" ), | |
| computeVertexPositionVS: [ | |
| "vec2 xy = texCoord * 2.0 - 1.0;", | |
| "vec4 vertexPositionProjected = vec4( xy, depth, 1.0 );", | |
| "vec4 vertexPositionVS = matProjInverse * vertexPositionProjected;", | |
| "vertexPositionVS.xyz /= vertexPositionVS.w;", | |
| "vertexPositionVS.w = 1.0;" | |
| ].join( "\n" ), | |
| // TODO: calculate schlick | |
| computeSpecular: [ | |
| "vec3 halfVector = normalize( lightVector - normalize( vertexPositionVS.xyz ) );", | |
| "float dotNormalHalf = max( dot( normal, halfVector ), 0.0 );", | |
| "float specular = 0.31830988618 * ( shininess * 0.5 + 1.0 ) * pow( dotNormalHalf, shininess );" | |
| ].join( "\n" ), | |
| combine: [ | |
| "gl_FragColor = vec4( lightIntensity * lightColor * max( dot( lightVector, normal ), 0.0 ) * ( diffuseColor + specular * specularColor ) * attenuation, 1.0 );" | |
| ].join( "\n" ) | |
| }; | |
| THREE.ShaderDeferredCommon = { | |
| commonUniforms: { | |
| matProjInverse: new THREE.Uniform( new THREE.Matrix4() ), | |
| viewWidth: new THREE.Uniform( 800 ), | |
| viewHeight: new THREE.Uniform( 600 ) | |
| } | |
| }; | |
| THREE.ShaderDeferred = { | |
| normalDepth: { | |
| uniforms: {}, | |
| vertexShader: [ | |
| "varying vec3 vNormal;", | |
| "varying vec4 vPosition;", | |
| "#include <morphtarget_pars_vertex>", | |
| "#include <skinning_pars_vertex>", | |
| "void main() {", | |
| "#include <begin_vertex>", | |
| "#include <beginnormal_vertex>", | |
| "#include <skinbase_vertex>", | |
| "#include <skinnormal_vertex>", | |
| "#include <defaultnormal_vertex>", | |
| "#include <morphtarget_vertex>", | |
| "#include <skinning_vertex>", | |
| "#include <project_vertex>", | |
| " vNormal = normalize( transformedNormal );", | |
| " vPosition = gl_Position;", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "varying vec3 vNormal;", | |
| "varying vec4 vPosition;", | |
| "void main() {", | |
| " vec3 normal = vNormal;", | |
| " vec4 position = vPosition;", | |
| THREE.DeferredShaderChunk[ "packNormalDepth" ], | |
| " gl_FragColor = packedNormalDepth;", | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| color: { | |
| uniforms: { | |
| map: new THREE.Uniform( null ), | |
| offsetRepeat: new THREE.Uniform( new THREE.Vector4( 0, 0, 1, 1 ) ), | |
| diffuse: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| emissive: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| specular: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| shininess: new THREE.Uniform( 30.0 ) | |
| }, | |
| vertexShader: [ | |
| "#include <uv_pars_vertex>", | |
| "#include <morphtarget_pars_vertex>", | |
| "#include <skinning_pars_vertex>", | |
| "void main() {", | |
| "#include <uv_vertex>", | |
| "#include <begin_vertex>", | |
| "#include <beginnormal_vertex>", | |
| "#include <skinbase_vertex>", | |
| "#include <skinnormal_vertex>", | |
| "#include <defaultnormal_vertex>", | |
| "#include <morphtarget_vertex>", | |
| "#include <skinning_vertex>", | |
| "#include <project_vertex>", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "uniform vec3 diffuse;", | |
| "uniform vec3 emissive;", | |
| "uniform vec3 specular;", | |
| "uniform float shininess;", | |
| "#include <uv_pars_fragment>", | |
| "#include <map_pars_fragment>", | |
| THREE.DeferredShaderChunk[ "packVector3" ], | |
| "void main() {", | |
| " vec4 diffuseColor = vec4( diffuse, 1.0 );", | |
| " vec3 emissiveColor = emissive;", | |
| " vec3 specularColor = specular;", | |
| "#include <map_fragment>", | |
| THREE.DeferredShaderChunk[ "packColor" ], | |
| " gl_FragColor = packedColor;", | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| emissiveLight: { | |
| uniforms: Object.assign( | |
| { | |
| samplerColor: new THREE.Uniform( null ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "void main() { ", | |
| " gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );", | |
| "}" | |
| ].join( '\n' ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerColor;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| "void main() {", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| THREE.DeferredShaderChunk[ "unpackColor" ], | |
| " gl_FragColor = vec4( emissiveColor, 1.0 );", | |
| "}" | |
| ].join( '\n' ) | |
| }, | |
| pointLight: { | |
| uniforms: Object.assign( | |
| { | |
| samplerNormalDepth: new THREE.Uniform( null ), | |
| samplerColor: new THREE.Uniform( null ), | |
| lightColor: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| lightPositionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightIntensity: new THREE.Uniform( 1.0 ), | |
| lightRadius: new THREE.Uniform( 1.0 ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "void main() {", | |
| " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerNormalDepth;", | |
| "uniform sampler2D samplerColor;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| "uniform vec3 lightColor;", | |
| "uniform vec3 lightPositionVS;", | |
| "uniform float lightIntensity;", | |
| "uniform float lightRadius;", | |
| "uniform mat4 matProjInverse;", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| "void main() {", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| THREE.DeferredShaderChunk[ "unpackNormalDepth" ], | |
| THREE.DeferredShaderChunk[ "computeVertexPositionVS" ], | |
| " vec3 lightVector = lightPositionVS - vertexPositionVS.xyz;", | |
| " float distance = length( lightVector );", | |
| " if ( distance > lightRadius ) discard;", | |
| " lightVector = normalize( lightVector );", | |
| THREE.DeferredShaderChunk[ "unpackColor" ], | |
| THREE.DeferredShaderChunk[ "computeSpecular" ], | |
| " //float cutoff = 0.3;", | |
| " //float denom = distance / lightRadius + 1.0;", | |
| " //float attenuation = 1.0 / ( denom * denom );", | |
| " //attenuation = ( attenuation - cutoff ) / ( 1.0 - cutoff );", | |
| " //attenuation = max( attenuation, 0.0 );", | |
| " //attenuation *= attenuation;", | |
| " //diffuseColor *= saturate( -distance / lightRadius + 1.0 );", | |
| " //float attenuation = 1.0;", | |
| " float attenuation = saturate( -distance / lightRadius + 1.0 );", | |
| THREE.DeferredShaderChunk[ "combine" ], | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| spotLight: { | |
| uniforms: Object.assign( | |
| { | |
| samplerNormalDepth: new THREE.Uniform( null ), | |
| samplerColor: new THREE.Uniform( null ), | |
| lightColor: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| lightDirectionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightPositionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightAngle: new THREE.Uniform( 1.0 ), | |
| lightIntensity: new THREE.Uniform( 1.0 ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "void main() { ", | |
| " gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerNormalDepth;", | |
| "uniform sampler2D samplerColor;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| "uniform vec3 lightColor;", | |
| "uniform vec3 lightPositionVS;", | |
| "uniform vec3 lightDirectionVS;", | |
| "uniform float lightAngle;", | |
| "uniform float lightIntensity;", | |
| "uniform mat4 matProjInverse;", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| "void main() {", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| THREE.DeferredShaderChunk[ "unpackNormalDepth" ], | |
| THREE.DeferredShaderChunk[ "computeVertexPositionVS" ], | |
| THREE.DeferredShaderChunk[ "unpackColor" ], | |
| " vec3 lightVector = normalize( lightPositionVS.xyz - vertexPositionVS.xyz );", | |
| " float rho = dot( lightDirectionVS, lightVector );", | |
| " float rhoMax = cos( lightAngle );", | |
| " if ( rho <= rhoMax ) discard;", | |
| " float theta = rhoMax + 0.0001;", | |
| " float phi = rhoMax + 0.05;", | |
| " float falloff = 4.0;", | |
| " float spot = 0.0;", | |
| " if ( rho >= phi ) {", | |
| " spot = 1.0;", | |
| " } else if ( rho <= theta ) {", | |
| " spot = 0.0;", | |
| " } else { ", | |
| " spot = pow( ( rho - theta ) / ( phi - theta ), falloff );", | |
| " }", | |
| " diffuseColor *= spot;", | |
| THREE.DeferredShaderChunk[ "computeSpecular" ], | |
| " const float attenuation = 1.0;", | |
| THREE.DeferredShaderChunk[ "combine" ], | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| directionalLight: { | |
| uniforms: Object.assign( | |
| { | |
| samplerNormalDepth: new THREE.Uniform( null ), | |
| samplerColor: new THREE.Uniform( null ), | |
| lightColor: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| lightDirectionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightIntensity: new THREE.Uniform( 1.0 ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "void main() { ", | |
| " gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );", | |
| "}" | |
| ].join( '\n' ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerNormalDepth;", | |
| "uniform sampler2D samplerColor;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| "uniform vec3 lightColor;", | |
| "uniform vec3 lightDirectionVS;", | |
| "uniform float lightIntensity;", | |
| "uniform mat4 matProjInverse;", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| "void main() {", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| THREE.DeferredShaderChunk[ "unpackNormalDepth" ], | |
| THREE.DeferredShaderChunk[ "computeVertexPositionVS" ], | |
| THREE.DeferredShaderChunk[ "unpackColor" ], | |
| " vec3 lightVector = normalize( lightDirectionVS );", | |
| THREE.DeferredShaderChunk[ "computeSpecular" ], | |
| " const float attenuation = 1.0;", | |
| THREE.DeferredShaderChunk[ "combine" ], | |
| "}" | |
| ].join( '\n' ) | |
| }, | |
| normalDepthShininess: { | |
| uniforms: { | |
| shininess: new THREE.Uniform( 30.0 ) | |
| }, | |
| vertexShader: [ | |
| "varying vec3 vNormal;", | |
| "varying vec4 vPosition;", | |
| "#include <morphtarget_pars_vertex>", | |
| "#include <skinning_pars_vertex>", | |
| "void main() {", | |
| "#include <begin_vertex>", | |
| "#include <beginnormal_vertex>", | |
| "#include <skinbase_vertex>", | |
| "#include <skinnormal_vertex>", | |
| "#include <defaultnormal_vertex>", | |
| "#include <morphtarget_vertex>", | |
| "#include <skinning_vertex>", | |
| "#include <project_vertex>", | |
| " vNormal = normalize( transformedNormal );", | |
| " vPosition = gl_Position;", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "varying vec3 vNormal;", | |
| "varying vec4 vPosition;", | |
| "uniform float shininess;", | |
| THREE.DeferredShaderChunk[ "packNormal" ], | |
| "void main() {", | |
| " vec3 normal = vNormal;", | |
| " vec4 position = vPosition;", | |
| THREE.DeferredShaderChunk[ "packNormalDepthShininess" ], | |
| " gl_FragColor = packedNormalDepthShininess;", | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| pointLightPre: { | |
| uniforms: Object.assign( | |
| { | |
| samplerNormalDepthShininess: new THREE.Uniform( null ), | |
| lightColor: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| lightPositionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightIntensity: new THREE.Uniform( 1.0 ), | |
| lightRadius: new THREE.Uniform( 1.0 ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "void main() {", | |
| " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerNormalDepthShininess;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| "uniform vec3 lightColor;", | |
| "uniform vec3 lightPositionVS;", | |
| "uniform float lightIntensity;", | |
| "uniform float lightRadius;", | |
| "uniform mat4 matProjInverse;", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| THREE.DeferredShaderChunk[ "unpackVector2" ], | |
| "void main() {", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| THREE.DeferredShaderChunk[ "unpackNormalDepthShininess" ], | |
| THREE.DeferredShaderChunk[ "computeVertexPositionVS" ], | |
| " vec3 lightVector = lightPositionVS - vertexPositionVS.xyz;", | |
| " float distance = length( lightVector );", | |
| " if ( distance > lightRadius ) discard;", | |
| " lightVector = normalize( lightVector );", | |
| THREE.DeferredShaderChunk[ "computeSpecular" ], | |
| " float attenuation = saturate( -distance / lightRadius + 1.0 );", | |
| THREE.DeferredShaderChunk[ "packLight" ], | |
| " gl_FragColor = packedLight;", | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| spotLightPre: { | |
| uniforms: Object.assign( | |
| { | |
| samplerNormalDepthShininess: new THREE.Uniform( null ), | |
| lightColor: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| lightDirectionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightPositionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightAngle: new THREE.Uniform( 1.0 ), | |
| lightIntensity: new THREE.Uniform( 1.0 ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "void main() { ", | |
| " gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerNormalDepthShininess;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| "uniform vec3 lightColor;", | |
| "uniform vec3 lightPositionVS;", | |
| "uniform vec3 lightDirectionVS;", | |
| "uniform float lightAngle;", | |
| "uniform float lightIntensity;", | |
| "uniform mat4 matProjInverse;", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| THREE.DeferredShaderChunk[ "unpackVector2" ], | |
| "void main() {", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| THREE.DeferredShaderChunk[ "unpackNormalDepthShininess" ], | |
| THREE.DeferredShaderChunk[ "computeVertexPositionVS" ], | |
| " vec3 lightVector = normalize( lightPositionVS.xyz - vertexPositionVS.xyz );", | |
| " float rho = dot( lightDirectionVS, lightVector );", | |
| " float rhoMax = cos( lightAngle );", | |
| " if ( rho <= rhoMax ) discard;", | |
| " float theta = rhoMax + 0.0001;", | |
| " float phi = rhoMax + 0.05;", | |
| " float falloff = 4.0;", | |
| " float spot = 0.0;", | |
| " if ( rho >= phi ) {", | |
| " spot = 1.0;", | |
| " } else if ( rho <= theta ) {", | |
| " spot = 0.0;", | |
| " } else { ", | |
| " spot = pow( ( rho - theta ) / ( phi - theta ), falloff );", | |
| " }", | |
| THREE.DeferredShaderChunk[ "computeSpecular" ], | |
| " const float attenuation = 1.0;", | |
| THREE.DeferredShaderChunk[ "packLight" ], | |
| " gl_FragColor = spot * packedLight;", | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| directionalLightPre: { | |
| uniforms: Object.assign( | |
| { | |
| samplerNormalDepthShininess: new THREE.Uniform( null ), | |
| lightColor: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| lightDirectionVS: new THREE.Uniform( new THREE.Vector3( 0, 1, 0 ) ), | |
| lightIntensity: new THREE.Uniform( 1.0 ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "void main() { ", | |
| " gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );", | |
| "}" | |
| ].join( '\n' ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerNormalDepthShininess;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| "uniform vec3 lightColor;", | |
| "uniform vec3 lightDirectionVS;", | |
| "uniform float lightIntensity;", | |
| "uniform mat4 matProjInverse;", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| THREE.DeferredShaderChunk[ "unpackVector2" ], | |
| "void main() {", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| THREE.DeferredShaderChunk[ "unpackNormalDepthShininess" ], | |
| THREE.DeferredShaderChunk[ "computeVertexPositionVS" ], | |
| " vec3 lightVector = normalize( lightDirectionVS );", | |
| THREE.DeferredShaderChunk[ "computeSpecular" ], | |
| " const float attenuation = 1.0;", | |
| THREE.DeferredShaderChunk[ "packLight" ], | |
| " gl_FragColor = packedLight;", | |
| "}" | |
| ].join( '\n' ) | |
| }, | |
| reconstruction: { | |
| uniforms: Object.assign( | |
| { | |
| samplerLight: new THREE.Uniform( null ), | |
| map: new THREE.Uniform( null ), | |
| offsetRepeat: new THREE.Uniform( new THREE.Vector4( 0, 0, 1, 1 ) ), | |
| diffuse: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| emissive: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| specular: new THREE.Uniform( new THREE.Color( 0x000000 ) ), | |
| shininess: new THREE.Uniform( 30.0 ) | |
| }, | |
| THREE.ShaderDeferredCommon[ 'commonUniforms' ] | |
| ), | |
| vertexShader: [ | |
| "#include <uv_pars_vertex>", | |
| "#include <morphtarget_pars_vertex>", | |
| "#include <skinning_pars_vertex>", | |
| "void main() {", | |
| "#include <uv_vertex>", | |
| "#include <begin_vertex>", | |
| "#include <beginnormal_vertex>", | |
| "#include <skinbase_vertex>", | |
| "#include <skinnormal_vertex>", | |
| "#include <defaultnormal_vertex>", | |
| "#include <morphtarget_vertex>", | |
| "#include <skinning_vertex>", | |
| "#include <project_vertex>", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "uniform sampler2D samplerLight;", | |
| "uniform vec3 diffuse;", | |
| "uniform vec3 emissive;", | |
| "uniform vec3 specular;", | |
| "uniform float shininess;", | |
| "uniform float viewHeight;", | |
| "uniform float viewWidth;", | |
| "#include <uv_pars_fragment>", | |
| "#include <map_pars_fragment>", | |
| THREE.DeferredShaderChunk[ "unpackFloat" ], | |
| "void main() {", | |
| " vec4 diffuseColor = vec4( diffuse, 1.0 );", | |
| " vec3 emissiveColor = emissive;", | |
| " vec3 specularColor = specular;", | |
| THREE.DeferredShaderChunk[ "computeTextureCoord" ], | |
| " vec4 light = texture2D( samplerLight, texCoord );", | |
| "#include <map_fragment>", | |
| " vec3 diffuseFinal = diffuseColor.rgb * light.rgb;", | |
| " vec3 emissiveFinal = emissiveColor;", | |
| " vec3 specularFinal = specularColor * light.rgb * light.a;", | |
| " gl_FragColor = vec4( diffuseFinal + emissiveFinal + specularFinal, 1.0 );", | |
| "}" | |
| ].join( "\n" ) | |
| }, | |
| // TODO: implement tone mapping | |
| final: { | |
| uniforms: { | |
| samplerResult: new THREE.Uniform( null ) | |
| }, | |
| vertexShader: [ | |
| "varying vec2 texCoord;", | |
| "void main() {", | |
| " vec4 pos = vec4( sign( position.xy ), 0.0, 1.0 );", | |
| " texCoord = pos.xy * vec2( 0.5 ) + 0.5;", | |
| " gl_Position = pos;", | |
| "}" | |
| ].join( "\n" ), | |
| fragmentShader: [ | |
| "varying vec2 texCoord;", | |
| "uniform sampler2D samplerResult;", | |
| "void main() {", | |
| " gl_FragColor = texture2D( samplerResult, texCoord );", | |
| "}" | |
| ].join( "\n" ) | |
| } | |
| }; | |