From 222505983848be98d08daf290fcb6fe75b742801 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 13 May 2016 15:50:54 -0400 Subject: [PATCH] Update derived commands when shadow maps are dirty --- Apps/Sandcastle/gallery/3D Models.html | 2 +- .../gallery/development/Shadows.html | 152 ++++++++---------- Source/Renderer/DrawCommand.js | 2 + Source/Scene/FrameState.js | 7 +- Source/Scene/Scene.js | 34 ++-- Source/Scene/ShadowMap.js | 136 +++++++++++++--- Source/Scene/ShadowMapShader.js | 4 +- Source/Widgets/CesiumWidget/CesiumWidget.js | 4 +- Source/Widgets/Viewer/Viewer.js | 18 ++- Specs/Widgets/Viewer/ViewerSpec.js | 6 +- 10 files changed, 231 insertions(+), 134 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index e5795beed4e0..f9debcaa59cc 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -31,7 +31,7 @@ scene3DOnly: true, infoBox : false, selectionIndicator : false, - shadowsEnabled : true + shadows : true }); function createModel(url, height) { diff --git a/Apps/Sandcastle/gallery/development/Shadows.html b/Apps/Sandcastle/gallery/development/Shadows.html index 570f6e368d10..43c19726d987 100644 --- a/Apps/Sandcastle/gallery/development/Shadows.html +++ b/Apps/Sandcastle/gallery/development/Shadows.html @@ -189,11 +189,11 @@ } var viewModel = { - lightAngle : 0.0, + lightAngle : 40.0, lightAngleEnabled : true, - lightHorizon : 0.0, + lightHorizon : 70.0, lightHorizonEnabled : true, - distance : 1000.0, + distance : 10000.0, distanceEnabled : true, radius : 200.0, radiusEnabled : true, @@ -263,17 +263,6 @@ biasMode : Cesium.knockout.observable() }; -// Temp values for testing -viewModel.lightSource = 'Freeform'; -viewModel.lightHorizon = 71.0; -viewModel.lightAngle = 40.0; -viewModel.distance = 10000.0; -viewModel.location = 'Pinnacle PA'; -viewModel.model = 'Shadow Tester'; -viewModel.modelPosition = 'Center'; -viewModel.terrain = true; -viewModel.terrainCast = true; - var uiOptions = { all : ['lightHorizon', 'lightAngle', 'distance', 'radius', 'terrainCast', 'cascades', 'cascadeColors', 'fitNearFar', 'softShadows'], disable : { @@ -334,26 +323,26 @@ Cesium.knockout.getObservable(viewModel, 'terrainCast').subscribe(updateSettings); Cesium.knockout.getObservable(viewModel, 'terrainReceive').subscribe(updateSettings); Cesium.knockout.getObservable(viewModel, 'fitNearFar').subscribe(updateSettings); -Cesium.knockout.getObservable(viewModel, 'cascadeColors').subscribe(updateShadows); -Cesium.knockout.getObservable(viewModel, 'softShadows').subscribe(updateShadows); +Cesium.knockout.getObservable(viewModel, 'cascadeColors').subscribe(updateSettings); +Cesium.knockout.getObservable(viewModel, 'softShadows').subscribe(updateSettings); Cesium.knockout.getObservable(viewModel, 'cascades').subscribe(updateShadows); Cesium.knockout.getObservable(viewModel, 'lightSource').subscribe(updateShadows); Cesium.knockout.getObservable(viewModel, 'size').subscribe(updateSettings); -Cesium.knockout.getObservable(viewModel, 'model').subscribe(updateShadows); +Cesium.knockout.getObservable(viewModel, 'model').subscribe(updateModels); Cesium.knockout.getObservable(viewModel, 'modelPosition').subscribe(updateLocation); -Cesium.knockout.getObservable(viewModel, 'grid').subscribe(updateShadows); +Cesium.knockout.getObservable(viewModel, 'grid').subscribe(updateModels); Cesium.knockout.getObservable(viewModel, 'location').subscribe(updateLocation); for (var i = 0; i < viewModel.biasModes.length; ++i) { var biasMode = viewModel.biasModes[i]; - biasMode.polygonOffset.subscribe(updateShadows); - biasMode.polygonOffsetFactor.subscribe(updateShadows); - biasMode.polygonOffsetUnits.subscribe(updateShadows); - biasMode.normalOffset.subscribe(updateShadows); - biasMode.normalOffsetScale.subscribe(updateShadows); - biasMode.normalShading.subscribe(updateShadows); - biasMode.normalShadingSmooth.subscribe(updateShadows); - biasMode.depthBias.subscribe(updateShadows); + biasMode.polygonOffset.subscribe(updateSettings); + biasMode.polygonOffsetFactor.subscribe(updateSettings); + biasMode.polygonOffsetUnits.subscribe(updateSettings); + biasMode.normalOffset.subscribe(updateSettings); + biasMode.normalOffsetScale.subscribe(updateSettings); + biasMode.normalShading.subscribe(updateSettings); + biasMode.normalShadingSmooth.subscribe(updateSettings); + biasMode.depthBias.subscribe(updateSettings); } var offset = new Cesium.Cartesian3(); @@ -371,14 +360,36 @@ } function updateSettings() { - shadowMap._maximumDistance = Number(viewModel.distance); + shadowMap.maximumDistance = Number(viewModel.distance); shadowMap._pointLightRadius = Number(viewModel.radius); shadowMap._fitNearFar = viewModel.fitNearFar; shadowMap.darkness = viewModel.darkness; shadowMap.debugShow = viewModel.debug; shadowMap.debugFreezeFrame = viewModel.freeze; shadowMap.enabled = viewModel.shadows; - shadowMap.setSize(viewModel.size); + shadowMap.size = viewModel.size; + shadowMap.debugCascadeColors = viewModel.cascadeColors; + shadowMap.softShadows = viewModel.softShadows; + + // Update biases + for (var i = 0; i < viewModel.biasModes.length; ++i) { + var biasMode = viewModel.biasModes[i]; + var bias = shadowMap['_' + biasMode.type + 'Bias']; + bias.polygonOffset = biasMode.polygonOffset(); + bias.polygonOffsetFactor = biasMode.polygonOffsetFactor(); + bias.polygonOffsetUnits = biasMode.polygonOffsetUnits(); + bias.normalOffset = biasMode.normalOffset(); + bias.normalOffsetScale = biasMode.normalOffsetScale(); + bias.normalShading = biasMode.normalShading(); + bias.normalShadingSmooth = biasMode.normalShadingSmooth(); + bias.depthBias = biasMode.depthBias(); + } + + // Update render states for when polygon offset values change + shadowMap.debugCreateRenderStates(); + + // Force all derived commands to update + shadowMap.dirty = true; globe.castShadows = viewModel.terrainCast; globe.receiveShadows = viewModel.terrainReceive; @@ -389,8 +400,6 @@ function updateShadows() { var cascades = viewModel.cascades; var lightSource = viewModel.lightSource; - var cascadeColors = viewModel.cascadeColors; - var softShadows = viewModel.softShadows; var lightCamera; if (lightSource === 'Freeform') { @@ -399,59 +408,45 @@ lightCamera = sunCamera; } + var shadowOptions; + if (lightSource === 'Fixed') { - resetShadows({ + shadowOptions = { context : context, lightCamera : fixedLightCamera, - cascadesEnabled : false, - softShadows : softShadows - }); + cascadesEnabled : false + }; } else if (lightSource === 'Point') { - resetShadows({ + shadowOptions = { context : context, lightCamera : pointLightCamera, - isPointLight : true, - softShadows : softShadows - }); + isPointLight : true + }; } else if (lightSource === 'Spot') { - resetShadows({ + shadowOptions = { context : context, lightCamera : spotLightCamera, - cascadesEnabled : false, - softShadows : softShadows - }); + cascadesEnabled : false + }; } else if (cascades === 4) { - resetShadows({ + shadowOptions = { context : context, - lightCamera : lightCamera, - softShadows : softShadows - }); - shadowMap.debugVisualizeCascades = cascadeColors; + lightCamera : lightCamera + }; } else if (cascades === 1) { - resetShadows({ + shadowOptions = { context : context, lightCamera : lightCamera, - numberOfCascades : 1, - softShadows : softShadows - }); + numberOfCascades : 1 + }; } - // Update biases - for (var i = 0; i < viewModel.biasModes.length; ++i) { - var biasMode = viewModel.biasModes[i]; - var bias = shadowMap['_' + biasMode.type + 'Bias']; - bias.polygonOffset = biasMode.polygonOffset(); - bias.polygonOffsetFactor = biasMode.polygonOffsetFactor(); - bias.polygonOffsetUnits = biasMode.polygonOffsetUnits(); - bias.normalOffset = biasMode.normalOffset(); - bias.normalOffsetScale = biasMode.normalOffsetScale(); - bias.normalShading = biasMode.normalShading(); - bias.normalShadingSmooth = biasMode.normalShadingSmooth(); - bias.depthBias = biasMode.depthBias(); - } + scene.shadowMap.destroy(); + scene.shadowMap = new Cesium.ShadowMap(shadowOptions); - // Update render states for when polygon offset values change - shadowMap.debugCreateRenderStates(); + shadowMap = scene.shadowMap; + shadowMap.enabled = true; + shadowMap.debugShow = true; updateSettings(); updateUI(); @@ -496,10 +491,6 @@ var ellipsoidTerrainProvider = new Cesium.EllipsoidTerrainProvider(); -var imageryProvider = new Cesium.BingMapsImageryProvider({ - url : 'https://dev.virtualearth.net' -}); - updateLocation(); function getModelPosition() { @@ -521,6 +512,7 @@ var location = uiOptions.locations[viewModel.location]; var positions = [new Cesium.Cartographic(location.centerLongitude, location.centerLatitude)]; var terrainProvider = viewModel.terrain ? cesiumTerrainProvider : ellipsoidTerrainProvider; + globe.terrainProvider = terrainProvider; var promise = Cesium.sampleTerrain(terrainProvider, 11, positions); Cesium.when(promise, function(updatedPositions) { location.height = updatedPositions[0].height + getModelPosition(); @@ -557,18 +549,11 @@ camera.lookAt(center, new Cesium.Cartesian3(25.0, 25.0, 30.0)); updateLightDirection(); + updateModels(); updateShadows(); } -function resetGlobe() { - globe = scene.globe = new Cesium.Globe(); - globe.imageryLayers.addImageryProvider(imageryProvider); - globe.terrainProvider = viewModel.terrain ? cesiumTerrainProvider : ellipsoidTerrainProvider; -} - -function resetShadows(shadowOptions) { - // Changing the number of cascades or toggling cascade debug colors requires the receive-shadows shaders to recompile. - // For testing purposes it is easier to clear the whole scene and start fresh rather than handling dynamic updates. +function updateModels() { scene.primitives.removeAll(); var location = uiOptions.locations[viewModel.location]; @@ -576,15 +561,6 @@ var centerLatitude = location.centerLatitude; var height = location.height; - resetGlobe(); - - scene.shadowMap.destroy(); - scene.shadowMap = new Cesium.ShadowMap(shadowOptions); - - shadowMap = scene.shadowMap; - shadowMap.enabled = true; - shadowMap.debugShow = true; - var position1 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 5.0); var position2 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 10.0); var position3 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 15.0); diff --git a/Source/Renderer/DrawCommand.js b/Source/Renderer/DrawCommand.js index ded3af975927..616f8aa1c26a 100644 --- a/Source/Renderer/DrawCommand.js +++ b/Source/Renderer/DrawCommand.js @@ -40,6 +40,7 @@ define([ this._castShadows = defaultValue(options.castShadows, false); this._receiveShadows = defaultValue(options.receiveShadows, false); this._dirty = true; + this._lastDirtyTime = 0; /** * @private @@ -464,6 +465,7 @@ define([ result._debugShowBoundingVolume = command._debugShowBoundingVolume; result._debugOverlappingFrustums = command._debugOverlappingFrustums; result._dirty = true; + result._lastDirtyTime = 0; return result; }; diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js index 5940c73096e0..1e79c388f0b8 100644 --- a/Source/Scene/FrameState.js +++ b/Source/Scene/FrameState.js @@ -186,7 +186,12 @@ define([ * The size of the bounding volume that is closest to the camera. Used to estimate cascade splits. * @type {Number} */ - closestObjectSize : undefined + closestObjectSize : undefined, + + /** + * The time when a shadow map was last dirty + */ + lastDirtyTime : 0 }; } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index c204d8188603..691503e6429e 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -184,7 +184,7 @@ define([ * @param {Boolean} [options.orderIndependentTranslucency=true] If true and the configuration supports it, use order independent translucency. * @param {Boolean} [options.scene3DOnly=false] If true, optimizes memory use and performance for 3D mode but disables the ability to use 2D or Columbus View. * @param {Number} [options.terrainExaggeration=1.0] A scalar used to exaggerate the terrain. Note that terrain exaggeration will not modify any other primitive as they are positioned relative to the ellipsoid. - * @param {Boolean} [options.shadowsEnabled=false] Determines if shadows are cast by the sun. + * @param {Boolean} [options.shadows=false] Determines if shadows are cast by the sun. * * @see CesiumWidget * @see {@link http://www.khronos.org/registry/webgl/specs/latest/#5.2|WebGLContextAttributes} @@ -551,7 +551,7 @@ define([ this.shadowMap = new ShadowMap({ context : context, lightCamera : this._sunCamera, - enabled : defaultValue(options.shadowsEnabled, false) + enabled : defaultValue(options.shadows, false) }); this._terrainExaggeration = defaultValue(options.terrainExaggeration, 1.0); @@ -1062,17 +1062,28 @@ define([ } function updateDerivedCommands(scene, command) { + var frameState = scene.frameState; + var shadowMaps = frameState.shadowMaps; + var context = scene._context; + + var shadowsDirty = false; + if (shadowMaps.length > 0 && (command.receiveShadows || command.castShadows)) { + // Update derived commands when any shadow maps become dirty + var lastDirtyTime = frameState.shadowHints.lastDirtyTime; + if (command._lastDirtyTime !== lastDirtyTime) { + command._lastDirtyTime = lastDirtyTime; + command._dirty = true; + shadowsDirty = true; + } + } + if (command._dirty) { command._dirty = false; - var context = scene._context; - var frameState = scene.frameState; - var derivedCommands = command.derivedCommands; - var shadowMaps = frameState.shadowMaps; - if (shadowMaps.length > 0) { - derivedCommands.shadows = ShadowMap.createDerivedCommands(shadowMaps, command, context, derivedCommands.shadows); + if (shadowMaps.length > 0 && (command.receiveShadows || command.castShadows)) { + derivedCommands.shadows = ShadowMap.createDerivedCommands(shadowMaps, command, shadowsDirty, context, derivedCommands.shadows); } var oit = scene._oit; @@ -1276,7 +1287,7 @@ define([ // throw off the near/far fitting for the shadow map. Only update for globe tiles that the // camera isn't inside. if (command.receiveShadows && (distances.start < ShadowMap.MAXIMUM_DISTANCE) && - !((pass === Pass.GLOBE) && (distances.start < 0.0) && (distances.stop > 0.0))) { + !((pass === Pass.GLOBE) && (distances.start < -100.0) && (distances.stop > 100.0))) { // Get the smallest bounding volume the camera is near. This is used to improve cascaded shadow splits. var size = distances.stop - distances.start; @@ -2045,6 +2056,11 @@ define([ var shadowMap = shadowMaps[i]; shadowMap.update(frameState); + if (shadowMap.dirty) { + ++frameState.shadowHints.lastDirtyTime; + shadowMap.dirty = false; + } + if (!shadowMap.outOfView && defined(globe) && globe.castShadows) { var sceneCamera = frameState.camera; var sceneCullingVolume = frameState.cullingVolume; diff --git a/Source/Scene/ShadowMap.js b/Source/Scene/ShadowMap.js index bbc4869c2141..3ed600a418d1 100644 --- a/Source/Scene/ShadowMap.js +++ b/Source/Scene/ShadowMap.js @@ -135,8 +135,6 @@ define([ * @exception {DeveloperError} Only one or four cascades are supported. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Shadows.html|Cesium Sandcastle Shadows Demo} - * - * @private */ function ShadowMap(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); @@ -154,11 +152,30 @@ define([ } //>>includeEnd('debug'); - this.enabled = defaultValue(options.enabled, true); - this.softShadows = defaultValue(options.softShadows, false); + + this._enabled = defaultValue(options.enabled, true); + this._softShadows = defaultValue(options.softShadows, false); + this._dirty = false; + + /** + * Determines the darkness of the shadows. + * + * @memberof ShadowMap.prototype + * @type {Number} + * @default 0.3 + */ this.darkness = defaultValue(options.darkness, 0.3); this._darkness = this.darkness; + /** + * Determines the maximum distance of the shadow map. Only applicable for cascaded shadows. Larger distances may result in lower quality shadows. + * + * @memberof ShadowMap.prototype + * @type {Number} + * @default 5000.0 + */ + this.maximumDistance = defaultValue(options.maximumDistance, 5000.0); + this._outOfView = false; this._outOfViewPrevious = false; this._needsUpdate = true; @@ -219,7 +236,6 @@ define([ this._cascadesEnabled = this._isPointLight ? false : defaultValue(options.cascadesEnabled, true); this._numberOfCascades = !this._cascadesEnabled ? 0 : defaultValue(options.numberOfCascades, 4); this._fitNearFar = true; - this._maximumDistance = defaultValue(options.maximumDistance, 5000.0); this._maximumCascadeDistances = [25.0, 150.0, 700.0, Number.MAX_VALUE]; this._textureSize = new Cartesian2(); @@ -258,7 +274,7 @@ define([ this.debugShow = false; this.debugFreezeFrame = false; this._debugFreezeFrame = false; - this.debugVisualizeCascades = false; + this.debugCascadeColors = false; this._debugLightFrustum = undefined; this._debugCameraFrustum = undefined; this._debugCascadeFrustums = new Array(this._numberOfCascades); @@ -284,8 +300,8 @@ define([ this._clearPassState = new PassState(context); - var size = defaultValue(options.size, 2048); - this.setSize(size); + this._size = defaultValue(options.size, 2048); + this.size = this._size; } // Global maximum shadow distance used to prevent far off receivers from extending @@ -341,8 +357,77 @@ define([ }; defineProperties(ShadowMap.prototype, { + /** + * Determines if the shadow map will be shown. + * + * @memberof ShadowMap.prototype + * @type {Boolean} + * @default true + */ + enabled : { + get : function() { + return this._enabled; + }, + set : function(value) { + this._dirty = this._enabled !== value; + this._enabled = value; + } + }, + + /** + * Determines if soft shadows are enabled. Uses pcf filtering which requires more texture reads and may hurt performance. + * + * @memberof ShadowMap.prototype + * @type {Boolean} + * @default false + */ + softShadows : { + get : function() { + return this._softShadows; + }, + set : function(value) { + this._dirty = this._softShadows !== value; + this._softShadows = value; + } + }, + + /** + * Determines whether derived shadow commands need to update. + * + * @memberof ShadowMap.prototype + * @type {Boolean} + * @default false + */ + dirty : { + get : function() { + return this._dirty; + }, + set : function(value) { + this._dirty = value; + } + }, + + /** + * The width and height, in pixels, of each shadow map. + * + * @memberof ShadowMap.prototype + * @type {Number} + */ + size : { + get : function() { + return this._size; + }, + set : function(value) { + resize(this, value); + } + }, + /** * Whether the shadow map is out of view of the scene camera. + * + * @memberof ShadowMap.prototype + * @type {Boolean} + * @readonly */ outOfView : { get : function() { @@ -604,12 +689,12 @@ define([ } } - ShadowMap.prototype.setSize = function(size) { - var passes = this._passes; + function resize(shadowMap, size) { + var passes = shadowMap._passes; var numberOfPasses = passes.length; - var textureSize = this._textureSize; + var textureSize = shadowMap._textureSize; - if (this._isPointLight) { + if (shadowMap._isPointLight) { size = (ContextLimits.maximumCubeMapSize >= size) ? size : ContextLimits.maximumCubeMapSize; textureSize.x = size; textureSize.y = size; @@ -644,7 +729,7 @@ define([ } // Update clear pass state - this._clearPassState.viewport = new BoundingRectangle(0, 0, textureSize.x, textureSize.y); + shadowMap._clearPassState.viewport = new BoundingRectangle(0, 0, textureSize.x, textureSize.y); // Transforms shadow coordinates [0, 1] into the pass's region of the texture for (var i = 0; i < numberOfPasses; ++i) { @@ -656,7 +741,7 @@ define([ var scaleY = viewport.height / textureSize.y; pass.textureOffsets = new Matrix4(scaleX, 0.0, 0.0, biasX, 0.0, scaleY, 0.0, biasY, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); } - }; + } var scratchViewport = new BoundingRectangle(); @@ -1124,6 +1209,7 @@ define([ // 2. Set bounding box back to include objects in the light's view max.z += 1000.0; // Note: in light space, a positive number is behind the camera + min.z -= 10.0; // Extend the shadow volume forward slightly to avoid problems right at the edge // 3. Adjust light view matrix so that it is centered on the bounding volume var translation = scratchTranslation; @@ -1220,7 +1306,7 @@ define([ // Check whether the shadow map is in view and needs to be updated if (shadowMap._cascadesEnabled) { // If the nearest shadow receiver is further than the shadow map's maximum distance then the shadow map is out of view. - if (sceneCamera.frustum.near >= shadowMap._maximumDistance) { + if (sceneCamera.frustum.near >= shadowMap.maximumDistance) { shadowMap._outOfView = true; shadowMap._needsUpdate = false; return; @@ -1305,12 +1391,12 @@ define([ var near; var far; if (shadowMap._fitNearFar) { - // shadowFar can be very large, so limit to shadowMap._maximumDistance - near = Math.min(frameState.shadowHints.nearPlane, shadowMap._maximumDistance); - far = Math.min(frameState.shadowHints.farPlane, shadowMap._maximumDistance); + // shadowFar can be very large, so limit to shadowMap.maximumDistance + near = Math.min(frameState.shadowHints.nearPlane, shadowMap.maximumDistance); + far = Math.min(frameState.shadowHints.farPlane, shadowMap.maximumDistance); } else { near = camera.frustum.near; - far = shadowMap._maximumDistance; + far = shadowMap.maximumDistance; } shadowMap._sceneCamera = Camera.clone(camera, sceneCamera); @@ -1406,7 +1492,7 @@ define([ return Cartesian4.fromElements(texelStepSize.x, texelStepSize.y, bias.depthBias, bias.normalShadingSmooth, this.combinedUniforms1); }, shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness : function() { - return Cartesian4.fromElements(bias.normalOffsetScale, shadowMap._distance, shadowMap._maximumDistance, shadowMap._darkness, this.combinedUniforms2); + return Cartesian4.fromElements(bias.normalOffsetScale, shadowMap._distance, shadowMap.maximumDistance, shadowMap._darkness, this.combinedUniforms2); }, combinedUniforms1 : new Cartesian4(), @@ -1416,7 +1502,7 @@ define([ return combine(uniforms, mapUniforms, false); } - function createCastDerivedCommand(shadowMap, command, context, oldShaderId, result) { + function createCastDerivedCommand(shadowMap, shadowsDirty, command, context, oldShaderId, result) { var castShader; var castRenderState; var castUniformMap; @@ -1428,7 +1514,7 @@ define([ result = DrawCommand.shallowClone(command, result); - if (!defined(castShader) || oldShaderId !== command.shaderProgram.id) { + if (!defined(castShader) || oldShaderId !== command.shaderProgram.id || shadowsDirty) { if (defined(castShader)) { castShader.destroy(); } @@ -1477,7 +1563,7 @@ define([ return result; } - ShadowMap.createDerivedCommands = function(shadowMaps, command, context, result) { + ShadowMap.createDerivedCommands = function(shadowMaps, command, shadowsDirty, context, result) { if (!defined(result)) { result = {}; } @@ -1504,7 +1590,7 @@ define([ castCommands.length = shadowMapLength; for (var i = 0; i < shadowMapLength; ++i) { - castCommands[i] = createCastDerivedCommand(shadowMaps[i], command, context, oldShaderId, castCommands[i]); + castCommands[i] = createCastDerivedCommand(shadowMaps[i], shadowsDirty, command, context, oldShaderId, castCommands[i]); } result.castShaderProgramId = command.shaderProgram.id; @@ -1525,7 +1611,7 @@ define([ var castShadowsDirty = result.receiveShaderCastShadows !== command.castShadows; var shaderDirty = result.receiveShaderProgramId !== command.shaderProgram.id; - if (!defined(receiveShader) || shaderDirty || castShadowsDirty) { + if (!defined(receiveShader) || shaderDirty || shadowsDirty || castShadowsDirty) { if (defined(receiveShader)) { receiveShader.destroy(); } diff --git a/Source/Scene/ShadowMapShader.js b/Source/Scene/ShadowMapShader.js index a806428644b3..7bea56c5f962 100644 --- a/Source/Scene/ShadowMapShader.js +++ b/Source/Scene/ShadowMapShader.js @@ -137,7 +137,7 @@ define([ var isPointLight = shadowMap._isPointLight; var isSpotLight = shadowMap._isSpotLight; var hasCascades = shadowMap._numberOfCascades > 1; - var debugVisualizeCascades = shadowMap.debugVisualizeCascades; + var debugCascadeColors = shadowMap.debugCascadeColors; var softShadows = shadowMap.softShadows; var bias = isPointLight ? shadowMap._pointBias : (isTerrain ? shadowMap._terrainBias : shadowMap._primitiveBias); @@ -302,7 +302,7 @@ define([ ' float fade = max((depth - shadowMapMaximumDistance * 0.8) / (shadowMapMaximumDistance * 0.2), 0.0); \n' + ' visibility = mix(visibility, 1.0, fade); \n' + - (debugVisualizeCascades ? + (debugCascadeColors ? ' // Draw cascade colors for debugging \n' + ' gl_FragColor *= czm_cascadeColor(weights); \n' : ''); } else { diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.js b/Source/Widgets/CesiumWidget/CesiumWidget.js index 2adf9b4e2291..0f8099dcb4ca 100644 --- a/Source/Widgets/CesiumWidget/CesiumWidget.js +++ b/Source/Widgets/CesiumWidget/CesiumWidget.js @@ -159,7 +159,7 @@ define([ * @param {Element|String} [options.creditContainer] The DOM element or ID that will contain the {@link CreditDisplay}. If not specified, the credits are added * to the bottom of the widget itself. * @param {Number} [options.terrainExaggeration=1.0] A scalar used to exaggerate the terrain. Note that terrain exaggeration will not modify any other primitive as they are positioned relative to the ellipsoid. - * @param {Boolean} [options.shadowsEnabled=false] Determines if shadows are cast by the sun. + * @param {Boolean} [options.shadows=false] Determines if shadows are cast by the sun. * * @exception {DeveloperError} Element with id "container" does not exist in the document. * @@ -257,7 +257,7 @@ define([ orderIndependentTranslucency : options.orderIndependentTranslucency, scene3DOnly : defaultValue(options.scene3DOnly, false), terrainExaggeration : options.terrainExaggeration, - shadowsEnabled : options.shadowsEnabled + shadows : options.shadows }); this._scene = scene; diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 60b1c0a9ef7c..a1b0726981c1 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -276,7 +276,7 @@ define([ * @param {DataSourceCollection} [options.dataSources=new DataSourceCollection()] The collection of data sources visualized by the widget. If this parameter is provided, * the instance is assumed to be owned by the caller and will not be destroyed when the viewer is destroyed. * @param {Number} [options.terrainExaggeration=1.0] A scalar used to exaggerate the terrain. Note that terrain exaggeration will not modify any other primitive as they are positioned relative to the ellipsoid. - * @param {Boolean} [options.shadowsEnabled=false] Determines if shadows are cast by the sun. + * @param {Boolean} [options.shadows=false] Determines if shadows are cast by the sun. * * @exception {DeveloperError} Element with id "container" does not exist in the document. * @exception {DeveloperError} options.imageryProvider is not available when using the BaseLayerPicker widget, specify options.selectedImageryProviderViewModel instead. @@ -410,7 +410,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to creditContainer : defined(options.creditContainer) ? options.creditContainer : bottomContainer, scene3DOnly : scene3DOnly, terrainExaggeration : options.terrainExaggeration, - shadowsEnabled : options.shadowsEnabled + shadows : options.shadows }); var dataSourceCollection = options.dataSources; @@ -950,7 +950,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to * @type {Boolean} * @readonly */ - shadowsEnabled : { + shadows : { get : function() { return this.scene.shadowMap.enabled; }, @@ -959,6 +959,18 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to } }, + /** + * Get the scene's shadow map + * @memberof Viewer.prototype + * @type {ShadowMap} + * @readonly + */ + shadowMap : { + get : function() { + return this.scene.shadowMap; + } + }, + /** * Gets the collection of image layers that will be rendered on the globe. * @memberof Viewer.prototype diff --git a/Specs/Widgets/Viewer/ViewerSpec.js b/Specs/Widgets/Viewer/ViewerSpec.js index b7e70fb2f0a0..39d04c25dc77 100644 --- a/Specs/Widgets/Viewer/ViewerSpec.js +++ b/Specs/Widgets/Viewer/ViewerSpec.js @@ -354,11 +354,11 @@ defineSuite([ viewer.render(); }); - it('can set shadowsEnabled', function() { + it('can set shadows', function() { viewer = createViewer(container, { - shadowsEnabled : true + shadows : true }); - expect(viewer.shadowsEnabled).toBe(true); + expect(viewer.shadows).toBe(true); }); it('can set terrainProvider', function() {