Skip to content

Commit

Permalink
Update derived commands when shadow maps are dirty
Browse files Browse the repository at this point in the history
  • Loading branch information
lilleyse committed May 13, 2016
1 parent ccda0e0 commit 2225059
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 134 deletions.
2 changes: 1 addition & 1 deletion Apps/Sandcastle/gallery/3D Models.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
scene3DOnly: true,
infoBox : false,
selectionIndicator : false,
shadowsEnabled : true
shadows : true
});

function createModel(url, height) {
Expand Down
152 changes: 64 additions & 88 deletions Apps/Sandcastle/gallery/development/Shadows.html
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 : {
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand All @@ -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') {
Expand All @@ -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();
Expand Down Expand Up @@ -496,10 +491,6 @@

var ellipsoidTerrainProvider = new Cesium.EllipsoidTerrainProvider();

var imageryProvider = new Cesium.BingMapsImageryProvider({
url : 'https://dev.virtualearth.net'
});

updateLocation();

function getModelPosition() {
Expand All @@ -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();
Expand Down Expand Up @@ -557,34 +549,18 @@
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];
var centerLongitude = location.centerLongitude;
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);
Expand Down
2 changes: 2 additions & 0 deletions Source/Renderer/DrawCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ define([
this._castShadows = defaultValue(options.castShadows, false);
this._receiveShadows = defaultValue(options.receiveShadows, false);
this._dirty = true;
this._lastDirtyTime = 0;

/**
* @private
Expand Down Expand Up @@ -464,6 +465,7 @@ define([
result._debugShowBoundingVolume = command._debugShowBoundingVolume;
result._debugOverlappingFrustums = command._debugOverlappingFrustums;
result._dirty = true;
result._lastDirtyTime = 0;

return result;
};
Expand Down
7 changes: 6 additions & 1 deletion Source/Scene/FrameState.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}

Expand Down
34 changes: 25 additions & 9 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 2225059

Please sign in to comment.