diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index efd5ebb855ab9fb271725bf508b722093c3b74c9..dc33c52547cac39ad3fbf01cd0b8f0a765cb6fb6 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -55,7 +55,8 @@ function WebGLRenderer( parameters ) { _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false; - var lights = []; + var lightsArray = []; + var shadowsArray = []; var currentRenderList = null; @@ -156,31 +157,6 @@ function WebGLRenderer( parameters ) { _projScreenMatrix = new Matrix4(), _vector3 = new Vector3(), - _matrix4 = new Matrix4(), - _matrix42 = new Matrix4(), - - // light arrays cache - - _lights = { - - hash: '', - - ambient: [ 0, 0, 0 ], - directional: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - point: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [], - - shadows: [] - - }, // info @@ -261,8 +237,8 @@ function WebGLRenderer( parameters ) { } var extensions, capabilities, state; - var properties, textures, attributes, geometries, objects; - var programCache, lightCache, renderLists; + var properties, textures, attributes, geometries, objects, lights; + var programCache, renderLists; var background, bufferRenderer, indexedBufferRenderer; @@ -295,7 +271,7 @@ function WebGLRenderer( parameters ) { geometries = new WebGLGeometries( _gl, attributes, _infoMemory ); objects = new WebGLObjects( _gl, geometries, _infoRender ); programCache = new WebGLPrograms( _this, capabilities ); - lightCache = new WebGLLights(); + lights = new WebGLLights(); renderLists = new WebGLRenderLists(); background = new WebGLBackground( _this, state, objects, _premultipliedAlpha ); @@ -329,7 +305,7 @@ function WebGLRenderer( parameters ) { // shadow map - var shadowMap = new WebGLShadowMap( this, _lights, objects, capabilities ); + var shadowMap = new WebGLShadowMap( this, shadowsArray, objects, capabilities ); this.shadowMap = shadowMap; @@ -1048,19 +1024,26 @@ function WebGLRenderer( parameters ) { this.compile = function ( scene, camera ) { - lights = []; + lightsArray.length = 0; + shadowsArray.length = 0; scene.traverse( function ( object ) { if ( object.isLight ) { - lights.push( object ); + lightsArray.push( object ); + + if ( object.castShadow ) { + + shadowsArray.push( object ); + + } } } ); - setupLights( lights, camera ); + lights.setup( lightsArray, shadowsArray, camera ); scene.traverse( function ( object ) { @@ -1136,7 +1119,9 @@ function WebGLRenderer( parameters ) { _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); - lights.length = 0; + lightsArray.length = 0; + shadowsArray.length = 0; + sprites.length = 0; lensFlares.length = 0; @@ -1160,11 +1145,9 @@ function WebGLRenderer( parameters ) { if ( _clippingEnabled ) _clipping.beginShadows(); - setupShadows( lights ); - shadowMap.render( scene, camera ); - setupLights( lights, camera ); + lights.setup( lightsArray, shadowsArray, camera ); if ( _clippingEnabled ) _clipping.endShadows(); @@ -1306,7 +1289,13 @@ function WebGLRenderer( parameters ) { if ( object.isLight ) { - lights.push( object ); + lightsArray.push( object ); + + if ( object.castShadow ) { + + shadowsArray.push( object ); + + } } else if ( object.isSprite ) { @@ -1474,7 +1463,7 @@ function WebGLRenderer( parameters ) { var materialProperties = properties.get( material ); var parameters = programCache.getParameters( - material, _lights, fog, _clipping.numPlanes, _clipping.numIntersection, object ); + material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); var code = programCache.getProgramCode( material, parameters ); @@ -1586,25 +1575,25 @@ function WebGLRenderer( parameters ) { // store the light setup it was created for - materialProperties.lightsHash = _lights.hash; + materialProperties.lightsHash = lights.state.hash; if ( material.lights ) { // wire up the material to this renderer's lighting state - uniforms.ambientLightColor.value = _lights.ambient; - uniforms.directionalLights.value = _lights.directional; - uniforms.spotLights.value = _lights.spot; - uniforms.rectAreaLights.value = _lights.rectArea; - uniforms.pointLights.value = _lights.point; - uniforms.hemisphereLights.value = _lights.hemi; - - uniforms.directionalShadowMap.value = _lights.directionalShadowMap; - uniforms.directionalShadowMatrix.value = _lights.directionalShadowMatrix; - uniforms.spotShadowMap.value = _lights.spotShadowMap; - uniforms.spotShadowMatrix.value = _lights.spotShadowMatrix; - uniforms.pointShadowMap.value = _lights.pointShadowMap; - uniforms.pointShadowMatrix.value = _lights.pointShadowMatrix; + uniforms.ambientLightColor.value = lights.state.ambient; + uniforms.directionalLights.value = lights.state.directional; + uniforms.spotLights.value = lights.state.spot; + uniforms.rectAreaLights.value = lights.state.rectArea; + uniforms.pointLights.value = lights.state.point; + uniforms.hemisphereLights.value = lights.state.hemi; + + uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; + uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; + uniforms.spotShadowMap.value = lights.state.spotShadowMap; + uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; + uniforms.pointShadowMap.value = lights.state.pointShadowMap; + uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } @@ -1652,7 +1641,7 @@ function WebGLRenderer( parameters ) { material.needsUpdate = true; - } else if ( material.lights && materialProperties.lightsHash !== _lights.hash ) { + } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) { material.needsUpdate = true; @@ -2243,226 +2232,6 @@ function WebGLRenderer( parameters ) { } - // Lighting - - function setupShadows( lights ) { - - var lightShadowsLength = 0; - - for ( var i = 0, l = lights.length; i < l; i ++ ) { - - var light = lights[ i ]; - - if ( light.castShadow ) { - - _lights.shadows[ lightShadowsLength ] = light; - lightShadowsLength ++; - - } - - } - - _lights.shadows.length = lightShadowsLength; - - } - - function setupLights( lights, camera ) { - - var l, ll, light, shadow, - r = 0, g = 0, b = 0, - color, - intensity, - distance, - shadowMap, - - viewMatrix = camera.matrixWorldInverse, - - directionalLength = 0, - pointLength = 0, - spotLength = 0, - rectAreaLength = 0, - hemiLength = 0; - - for ( l = 0, ll = lights.length; l < ll; l ++ ) { - - light = lights[ l ]; - - color = light.color; - intensity = light.intensity; - distance = light.distance; - - shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - - if ( light.isAmbientLight ) { - - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; - - } else if ( light.isDirectionalLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( _vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - uniforms.shadow = light.castShadow; - - if ( light.castShadow ) { - - shadow = light.shadow; - - uniforms.shadowBias = shadow.bias; - uniforms.shadowRadius = shadow.radius; - uniforms.shadowMapSize = shadow.mapSize; - - } - - _lights.directionalShadowMap[ directionalLength ] = shadowMap; - _lights.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - _lights.directional[ directionalLength ] = uniforms; - - directionalLength ++; - - } else if ( light.isSpotLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( _vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; - - uniforms.shadow = light.castShadow; - - if ( light.castShadow ) { - - shadow = light.shadow; - - uniforms.shadowBias = shadow.bias; - uniforms.shadowRadius = shadow.radius; - uniforms.shadowMapSize = shadow.mapSize; - - } - - _lights.spotShadowMap[ spotLength ] = shadowMap; - _lights.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - _lights.spot[ spotLength ] = uniforms; - - spotLength ++; - - } else if ( light.isRectAreaLight ) { - - var uniforms = lightCache.get( light ); - - // (a) intensity controls irradiance of entire light - uniforms.color - .copy( color ) - .multiplyScalar( intensity / ( light.width * light.height ) ); - - // (b) intensity controls the radiance per light area - // uniforms.color.copy( color ).multiplyScalar( intensity ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - // extract local rotation of light to derive width/height half vectors - _matrix42.identity(); - _matrix4.copy( light.matrixWorld ); - _matrix4.premultiply( viewMatrix ); - _matrix42.extractRotation( _matrix4 ); - - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - - uniforms.halfWidth.applyMatrix4( _matrix42 ); - uniforms.halfHeight.applyMatrix4( _matrix42 ); - - // TODO (abelnation): RectAreaLight distance? - // uniforms.distance = distance; - - _lights.rectArea[ rectAreaLength ] = uniforms; - - rectAreaLength ++; - - } else if ( light.isPointLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; - - uniforms.shadow = light.castShadow; - - if ( light.castShadow ) { - - shadow = light.shadow; - - uniforms.shadowBias = shadow.bias; - uniforms.shadowRadius = shadow.radius; - uniforms.shadowMapSize = shadow.mapSize; - uniforms.shadowCameraNear = shadow.camera.near; - uniforms.shadowCameraFar = shadow.camera.far; - - } - - _lights.pointShadowMap[ pointLength ] = shadowMap; - _lights.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - _lights.point[ pointLength ] = uniforms; - - pointLength ++; - - } else if ( light.isHemisphereLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); - - _lights.hemi[ hemiLength ] = uniforms; - - hemiLength ++; - - } - - } - - _lights.ambient[ 0 ] = r; - _lights.ambient[ 1 ] = g; - _lights.ambient[ 2 ] = b; - - _lights.directional.length = directionalLength; - _lights.spot.length = spotLength; - _lights.rectArea.length = rectAreaLength; - _lights.point.length = pointLength; - _lights.hemi.length = hemiLength; - - // TODO (sam-g-steel) why aren't we using join - _lights.hash = directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + _lights.shadows.length; - - } - // GL state setting this.setFaceCulling = function ( cullFace, frontFaceDirection ) { diff --git a/src/renderers/webgl/WebGLLights.js b/src/renderers/webgl/WebGLLights.js index dc67f7c66aadb48507af561bda1eef6a5a09f6cd..6866578ea0174a37ffc3eaa6adbe0d222cb13711 100644 --- a/src/renderers/webgl/WebGLLights.js +++ b/src/renderers/webgl/WebGLLights.js @@ -3,10 +3,11 @@ */ import { Color } from '../../math/Color'; -import { Vector3 } from '../../math/Vector3'; +import { Matrix4 } from '../../math/Matrix4'; import { Vector2 } from '../../math/Vector2'; +import { Vector3 } from '../../math/Vector3'; -function WebGLLights() { +function UniformsCache() { var lights = {}; @@ -99,5 +100,231 @@ function WebGLLights() { } +function WebGLLights() { + + var cache = new UniformsCache(); + + var state = { + + hash: '', + + ambient: [ 0, 0, 0 ], + directional: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotShadowMap: [], + spotShadowMatrix: [], + rectArea: [], + point: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [] + + }; + + var vector3 = new Vector3(); + var matrix4 = new Matrix4(); + var matrix42 = new Matrix4(); + + function setup( lights, shadows, camera ) { + + var r = 0, g = 0, b = 0; + + var directionalLength = 0; + var pointLength = 0; + var spotLength = 0; + var rectAreaLength = 0; + var hemiLength = 0; + + var viewMatrix = camera.matrixWorldInverse; + + for ( var i = 0, l = lights.length; i < l; i ++ ) { + + var light = lights[ i ]; + + var color = light.color; + var intensity = light.intensity; + var distance = light.distance; + + var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; + + if ( light.isAmbientLight ) { + + r += color.r * intensity; + g += color.g * intensity; + b += color.b * intensity; + + } else if ( light.isDirectionalLight ) { + + var uniforms = cache.get( light ); + + uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); + + uniforms.shadow = light.castShadow; + + if ( light.castShadow ) { + + var shadow = light.shadow; + + uniforms.shadowBias = shadow.bias; + uniforms.shadowRadius = shadow.radius; + uniforms.shadowMapSize = shadow.mapSize; + + } + + state.directionalShadowMap[ directionalLength ] = shadowMap; + state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; + state.directional[ directionalLength ] = uniforms; + + directionalLength ++; + + } else if ( light.isSpotLight ) { + + var uniforms = cache.get( light ); + + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); + + uniforms.color.copy( color ).multiplyScalar( intensity ); + uniforms.distance = distance; + + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); + + uniforms.coneCos = Math.cos( light.angle ); + uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); + uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; + + uniforms.shadow = light.castShadow; + + if ( light.castShadow ) { + + var shadow = light.shadow; + + uniforms.shadowBias = shadow.bias; + uniforms.shadowRadius = shadow.radius; + uniforms.shadowMapSize = shadow.mapSize; + + } + + state.spotShadowMap[ spotLength ] = shadowMap; + state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; + state.spot[ spotLength ] = uniforms; + + spotLength ++; + + } else if ( light.isRectAreaLight ) { + + var uniforms = cache.get( light ); + + // (a) intensity controls irradiance of entire light + uniforms.color + .copy( color ) + .multiplyScalar( intensity / ( light.width * light.height ) ); + + // (b) intensity controls the radiance per light area + // uniforms.color.copy( color ).multiplyScalar( intensity ); + + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); + + // extract local rotation of light to derive width/height half vectors + matrix42.identity(); + matrix4.copy( light.matrixWorld ); + matrix4.premultiply( viewMatrix ); + matrix42.extractRotation( matrix4 ); + + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + + uniforms.halfWidth.applyMatrix4( matrix42 ); + uniforms.halfHeight.applyMatrix4( matrix42 ); + + // TODO (abelnation): RectAreaLight distance? + // uniforms.distance = distance; + + state.rectArea[ rectAreaLength ] = uniforms; + + rectAreaLength ++; + + } else if ( light.isPointLight ) { + + var uniforms = cache.get( light ); + + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); + + uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + uniforms.distance = light.distance; + uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; + + uniforms.shadow = light.castShadow; + + if ( light.castShadow ) { + + var shadow = light.shadow; + + uniforms.shadowBias = shadow.bias; + uniforms.shadowRadius = shadow.radius; + uniforms.shadowMapSize = shadow.mapSize; + uniforms.shadowCameraNear = shadow.camera.near; + uniforms.shadowCameraFar = shadow.camera.far; + + } + + state.pointShadowMap[ pointLength ] = shadowMap; + state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; + state.point[ pointLength ] = uniforms; + + pointLength ++; + + } else if ( light.isHemisphereLight ) { + + var uniforms = cache.get( light ); + + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.direction.transformDirection( viewMatrix ); + uniforms.direction.normalize(); + + uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); + uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); + + state.hemi[ hemiLength ] = uniforms; + + hemiLength ++; + + } + + } + + state.ambient[ 0 ] = r; + state.ambient[ 1 ] = g; + state.ambient[ 2 ] = b; + + state.directional.length = directionalLength; + state.spot.length = spotLength; + state.rectArea.length = rectAreaLength; + state.point.length = pointLength; + state.hemi.length = hemiLength; + + // TODO (sam-g-steel) why aren't we using join + state.hash = directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + shadows.length; + + } + + return { + setup: setup, + state: state + } + +} + export { WebGLLights }; diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index c0e6ad3f91c206458224f6641f9e8bd7a4253fd3..ae6b120ab5a0227a63ba01ca4f160ff2dd1d8351 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -103,7 +103,7 @@ function WebGLPrograms( renderer, capabilities ) { } - this.getParameters = function ( material, lights, fog, nClipPlanes, nClipIntersection, object ) { + this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) { var shaderID = shaderIDs[ material.type ]; @@ -187,7 +187,7 @@ function WebGLPrograms( renderer, capabilities ) { dithering: material.dithering, - shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0, + shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: renderer.toneMapping, diff --git a/src/renderers/webgl/WebGLShadowMap.js b/src/renderers/webgl/WebGLShadowMap.js index fc5613616b7e066d8c3a5a10fb635246a3370d45..6572bf4e6dc3bd00e18d384e1d796c8fb2f86252 100644 --- a/src/renderers/webgl/WebGLShadowMap.js +++ b/src/renderers/webgl/WebGLShadowMap.js @@ -15,15 +15,13 @@ import { Vector2 } from '../../math/Vector2'; import { Matrix4 } from '../../math/Matrix4'; import { Frustum } from '../../math/Frustum'; -function WebGLShadowMap( _renderer, _lights, _objects, capabilities ) { +function WebGLShadowMap( _renderer, _shadows, _objects, capabilities ) { var _gl = _renderer.context, _state = _renderer.state, _frustum = new Frustum(), _projScreenMatrix = new Matrix4(), - _lightShadows = _lights.shadows, - _shadowMapSize = new Vector2(), _maxShadowMapSize = new Vector2( capabilities.maxTextureSize, capabilities.maxTextureSize ), @@ -110,7 +108,7 @@ function WebGLShadowMap( _renderer, _lights, _objects, capabilities ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - if ( _lightShadows.length === 0 ) return; + if ( _shadows.length === 0 ) return; // Set GL state for depth map. _state.disable( _gl.BLEND ); @@ -122,9 +120,9 @@ function WebGLShadowMap( _renderer, _lights, _objects, capabilities ) { var faceCount; - for ( var i = 0, il = _lightShadows.length; i < il; i ++ ) { + for ( var i = 0, il = _shadows.length; i < il; i ++ ) { - var light = _lightShadows[ i ]; + var light = _shadows[ i ]; var shadow = light.shadow; var isPointLight = light && light.isPointLight;