Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cull shadow commands #3819

Merged
merged 54 commits into from
Apr 15, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f58ded2
Cull shadow commands
lilleyse Apr 7, 2016
7b943e8
Merge branch 'shadows' into shadows-commands
lilleyse Apr 7, 2016
9dbb5c3
Use numberOfPasses
lilleyse Apr 7, 2016
ae48196
Fix precision issues with spot lights
lilleyse Apr 7, 2016
cc09976
getTerrainShadowCommands temp
lilleyse Apr 8, 2016
b5c45ea
Added a test version of getTerrainShadowCommands
lilleyse Apr 8, 2016
def5187
Added radiusSquared
lilleyse Apr 11, 2016
40a68bc
Added grid button
lilleyse Apr 11, 2016
0582b1f
Check when shadow map is out of view and needs update
lilleyse Apr 8, 2016
6babfbd
Merge branch 'shadows-commands' into shadows-multiple
bagnell Apr 11, 2016
c30e606
Add TODO for 3d-tiles
lilleyse Apr 12, 2016
916050d
Merge branch 'shadows-derived' into shadows-merged
bagnell Apr 12, 2016
249322a
Merge branch 'shadows-commands' into shadows-merged
bagnell Apr 12, 2016
21f79df
Temporarily disable globe update when getting terrain commands.
bagnell Apr 12, 2016
8648679
Move around shadow map and globe updates. Make an array of shadow maps.
bagnell Apr 12, 2016
aba1981
Add ability to render to multiple shadow maps.
bagnell Apr 12, 2016
465d88a
Rename pass to shadowPass
lilleyse Apr 12, 2016
32afa8e
Change angle check from 0.1 to 0.05
lilleyse Apr 12, 2016
481b239
Merge branch 'shadows' into shadows-commands
lilleyse Apr 12, 2016
bd98b6b
Add Sandcastle example to for multiple shadow maps and demonstrates t…
bagnell Apr 12, 2016
b3c5fcf
Merge branch 'shadows' into shadows-multiple
bagnell Apr 12, 2016
16da6a8
Add temporary fix for shadow casters
lilleyse Apr 13, 2016
6471382
Prevent crash when shadow map is not enabled
lilleyse Apr 13, 2016
8409ee6
Fix positionCartographic
lilleyse Apr 12, 2016
de6192e
Reduce terrain aliasing
lilleyse Apr 12, 2016
7436308
Improve shadow quality for the closest cascade
lilleyse Apr 12, 2016
0d1b1f1
Model can toggle casting or receiving shadows at runtime
lilleyse Apr 12, 2016
fee0316
Remove renderState getters
lilleyse Apr 13, 2016
282e963
Don't render terrain skirts for shadow casters
lilleyse Apr 13, 2016
b756123
Change Camera back to inverseTransformation
lilleyse Apr 13, 2016
833f158
Remove unneeded normalize in GlobeFS
lilleyse Apr 13, 2016
9cb906f
Turn off normal shading if cast shadows is false
lilleyse Apr 13, 2016
4f1b65e
Added button for terrain receive shadows
lilleyse Apr 13, 2016
6407d78
Fix visibility check after 0582b1f
lilleyse Apr 13, 2016
49cb616
Fix broken 2d view
lilleyse Apr 13, 2016
2cffb31
Add more model position options, including space
lilleyse Apr 13, 2016
1bd1c37
Slight bias tweaks
lilleyse Apr 13, 2016
f1ede3c
Merge branch 'shadows-commands' into shadows-multiple
bagnell Apr 14, 2016
5677298
Revert changes to shadows Sandcastle example.
bagnell Apr 14, 2016
e59ef50
Updates after merge.
bagnell Apr 14, 2016
9ef9d74
Merge pull request #3848 from AnalyticalGraphicsInc/shadows-misc
bagnell Apr 14, 2016
ad94eb8
Merge branch 'shadows-commands' into shadows-multiple
bagnell Apr 14, 2016
1b89577
Update after merge.
bagnell Apr 14, 2016
524e431
Formatting
lilleyse Apr 14, 2016
6198cfb
Remove FrameState.shadowMap
lilleyse Apr 14, 2016
9c6f37f
Remove TODO
lilleyse Apr 14, 2016
5538b42
Fix camera clone bug
lilleyse Apr 14, 2016
ba3a661
Tweak point light bias
lilleyse Apr 14, 2016
df04f64
Added shadow darkness uniform
lilleyse Apr 14, 2016
5626da7
Increase default maximum shadow distance
lilleyse Apr 14, 2016
21faca4
Added model with transparent texture
lilleyse Apr 15, 2016
a28d349
Only set maximum cascade sizes when the scene camera is close
lilleyse Apr 15, 2016
1abfe29
Merge pull request #3852 from AnalyticalGraphicsInc/shadows-multiple
lilleyse Apr 15, 2016
cf89f82
Merge pull request #3855 from AnalyticalGraphicsInc/shadows-misc
bagnell Apr 15, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 32 additions & 22 deletions Apps/Sandcastle/gallery/development/Shadows.html
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,15 @@
};

// Temp values for testing
viewModel.lightSource = 'Freeform';
viewModel.lightSource = 'Spot';
viewModel.lightHorizon = 71.0;
viewModel.lightAngle = 40.0;
viewModel.distance = 10000.0;
viewModel.location = 'Pinnacle PA';
viewModel.model = 'Shadow Tester 4';
viewModel.modelPosition = 'Ground';
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'],
Expand Down Expand Up @@ -540,7 +542,7 @@
spotLightCamera.frustum.fov = Cesium.Math.PI_OVER_TWO;
spotLightCamera.frustum.aspectRatio = 1.0;
spotLightCamera.frustum.near = 1.0;
spotLightCamera.frustum.far = 200.0;
spotLightCamera.frustum.far = 500.0;
spotLightCamera.lookAt(center, new Cesium.Cartesian3(location.tilt, location.tilt, location.offsetZ));

pointLightCamera.position = center;
Expand Down Expand Up @@ -580,34 +582,40 @@
var position2 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 10.0);
var position3 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 15.0);

var model = createModel(uiOptions.modelUrls[viewModel.model], location, viewModel.modelPosition);
addModelToView(model);
var modelHeight;
if (viewModel.modelPosition === 'Ground') {
modelHeight = height - 50.0;
} else if (viewModel.modelPosition === 'Center') {
modelHeight = height;
}

var modelPosition = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, modelHeight);

createModel(uiOptions.modelUrls[viewModel.model], modelPosition);
createBox(position3);
createBoxRTC(position2);
createSphere(position1);
}

function createModel(url, location, position) {
var origin;
if (position === 'Ground') {
origin = Cesium.Cartesian3.fromRadians(location.centerLongitude, location.centerLatitude, location.height - 50.0);
}
else if (position === 'Center') {
origin = Cesium.Cartesian3.fromRadians(location.centerLongitude, location.centerLatitude, location.height);
// Add a grid of models
var spacing = 0.00002;
var gridSize = 10;
for (var i = 0; i < gridSize * gridSize; ++i) {
var x = i % gridSize;
var y = Math.floor(i / gridSize);
var longitude = centerLongitude + spacing * (x - gridSize / 2.0);
var latitude = centerLatitude + spacing * (y - gridSize / 2.0);
var position = Cesium.Cartesian3.fromRadians(longitude, latitude, modelHeight);
createModel(uiOptions.modelUrls['Shadow Tester'], position);
}
}

function createModel(url, origin) {
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, 0.0, 0.0, 0.0);

var model = Cesium.Model.fromGltf({
var model = scene.primitives.add(Cesium.Model.fromGltf({
url : url,
modelMatrix : modelMatrix
});

return model;
}

function addModelToView(model) {
model = scene.primitives.add(model);
}));

model.readyPromise.then(function(model) {
// Play and loop all animations at half-speed
Expand All @@ -618,6 +626,8 @@
}).otherwise(function(error){
window.alert(error);
});

return model;
}

function createBoxRTC(origin) {
Expand Down
149 changes: 111 additions & 38 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -1685,64 +1685,137 @@ define([
}
}

function getShadowMapCommands(scene) {
// TODO : temporary solution for testing
var terrainCommands = [];
var primitiveCommands = [];
var frustumCommandsList = scene._frustumCommandsList;
var numFrustums = frustumCommandsList.length;
for (var i = 0; i < numFrustums; ++i) {
var frustumCommands = frustumCommandsList[i];
var startPass = Pass.GLOBE;
var endPass = Pass.TRANSLUCENT;
for (var pass = startPass; pass <= endPass; ++pass) {
var shadowCommands = (pass === Pass.GLOBE) ? terrainCommands : primitiveCommands;
var commands = frustumCommands.commands[pass];
var length = frustumCommands.indices[pass];
for (var j = 0; j < length; ++j) {
var command = commands[j];
if (command.castShadows) {
shadowCommands.push(command);
function getTerrainShadowCommands(scene, shadowMap) {
var frameState = scene.frameState;

var sceneCommandList = frameState.commandList;
var sceneCamera = frameState.camera;
var sceneCullingVolume = frameState.cullingVolume;

var terrainCommands = []; // TODO : avoid allocation

// Update frame state to render from the light camera
frameState.commandList = terrainCommands;
frameState.camera = shadowMap.shadowMapCamera;
frameState.cullingVolume = shadowMap.shadowMapCullingVolume;

// Collect terrain commands from the light's POV
if (scene._globe && scene._globe.castShadows) {
scene._globe.update(frameState);
}

// Revert back to original frame state
frameState.commandList = sceneCommandList;
frameState.camera = sceneCamera;
frameState.cullingVolume = sceneCullingVolume;

return terrainCommands;
}

// function getTerrainShadowCommands(scene, shadowMap) {
// // TODO : Temporary for testing. Globe.update doesn't work with orthographic frustums currently
// var terrainCommands = [];
// var commandList = scene.frameState.commandList;
// var length = commandList.length;
// for (var i = 0; i < length; ++i) {
// var command = commandList[i];
// if (command.castShadows && command.pass === Pass.GLOBE) {
// terrainCommands.push(command);
// }
// }
//
// return terrainCommands;
// }

function insertShadowCommands(commandList, insertAll, shadowMap, shadowPassCommands) {
var shadowVolume = shadowMap.shadowMapCullingVolume;
var passVolumes = shadowMap.passCullingVolumes;

var isPointLight = shadowMap.isPointLight;
var center = shadowMap.pointLightPosition;
var radius = shadowMap.pointLightRadius;

var numberOfPasses = shadowMap.numberOfPasses;

var length = commandList.length;
for (var i = 0; i < length; ++i) {
var command = commandList[i];
// Don't insert globe commands with the rest of the scene commands since they are handled separately
if (command.castShadows && (insertAll || (command.pass === Pass.OPAQUE || command.pass === Pass.TRANSLUCENT))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but long-term, we can evaluate avoiding linear searches like this, e.g., "is opaque or translucent", by using separate lists.

if (isPointLight) {
if (defined(command.boundingVolume)) {
var distance = command.boundingVolume.distanceSquaredTo(center);
if (distance < radius * radius) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Store radius * radius in a local outside of the loop; I'm not confident that it would get optimized.

for (var k = 0; k < numberOfPasses; ++k) {
shadowPassCommands[k].push(command);
}
}
}
} else {
if (isVisible(command, shadowVolume)) {
if (numberOfPasses <= 1) {
shadowPassCommands[0].push(command);
} else {
var wasVisible = false;
// Loop over cascades from largest to smallest
for (var j = numberOfPasses - 1; j >= 0; --j) {
var cascadeVolume = passVolumes[j];
if (isVisible(command, cascadeVolume)) {
shadowPassCommands[j].push(command);
wasVisible = true;
} else if (wasVisible) {
// If it was visible in the previous cascade but now isn't
// then there is no need to check any more cascades
break;
}
}
}
}
}
}
}
return [terrainCommands, primitiveCommands];
}

function resetShadowCommands(passCommands) {
var length = passCommands.length;
for (var i = 0; i < length; ++i) {
passCommands[i].length = 0;
}
}

function executeShadowMapCommands(scene) {
var context = scene.context;
var uniformState = context.uniformState;
var shadowMap = scene.shadowMap;
var isPointLight = shadowMap.isPointLight;
var renderState = isPointLight ? shadowMap.pointRenderState : shadowMap.primitiveRenderState;

var commands = getShadowMapCommands(scene);
var terrainCommands = commands[0];
var primitiveCommands = commands[1];
var shadowPassCommands = shadowMap.passCommands;
resetShadowCommands(shadowPassCommands);

var j;
var command;
// Insert the scene commands into the shadow map passes
var sceneCommands = scene.frameState.commandList;
insertShadowCommands(sceneCommands, false, shadowMap, shadowPassCommands);
var terrainCommands = getTerrainShadowCommands(scene, shadowMap);
insertShadowCommands(terrainCommands, true, shadowMap, shadowPassCommands);

var numberOfPasses = shadowMap.numberOfPasses;

// // TODO : testing only
// for (var k = 0; k < numberOfPasses; ++k) {
// console.log('Pass ' + k + ': ' + shadowPassCommands[k].length + ' commands.');
// }

for (var i = 0; i < numberOfPasses; ++i) {
uniformState.updateCamera(shadowMap.passCameras[i]);
var passState = shadowMap.passStates[i];
shadowMap.updatePass(context, i);

// Execute terrain commands
var terrainRenderState = isPointLight ? shadowMap.pointRenderState : shadowMap.terrainRenderState;
var numberOfTerrainCommands = terrainCommands.length;
for (j = 0; j < numberOfTerrainCommands; ++j) {
command = terrainCommands[j];
executeCommand(command, scene, context, passState, terrainRenderState, command.shadowCastProgram);
}

// Execute primitive commands
var primitiveRenderState = isPointLight ? shadowMap.pointRenderState : shadowMap.primitiveRenderState;
var numberOfPrimitiveCommands = primitiveCommands.length;
for (j = 0; j < numberOfPrimitiveCommands; ++j) {
command = primitiveCommands[j];
executeCommand(command, scene, context, passState, primitiveRenderState, command.shadowCastProgram);
var passCommands = shadowPassCommands[i];
var numberOfCommands = passCommands.length;
for (var j = 0; j < numberOfCommands; ++j) {
var command = passCommands[j];
executeCommand(command, scene, context, passState, renderState, command.shadowCastProgram);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit tangential, but executeCommand is becoming unwieldy; we could probably not pass in context, and then just access scene.context inside it.

}
}
}
Expand Down
Loading