Skip to content

Commit

Permalink
Improve shadow derived command creation. Split up receive from cast
Browse files Browse the repository at this point in the history
  • Loading branch information
lilleyse committed Sep 5, 2018
1 parent 7c1a5b0 commit 6de0c0b
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 19 deletions.
12 changes: 8 additions & 4 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -1450,15 +1450,13 @@ define([
var frameState = scene._frameState;
var context = scene._context;
var oit = scene._view.oit;
var shadowsEnabled = frameState.shadowState.shadowsEnabled;
var shadowMaps = frameState.shadowState.shadowMaps;
var lightShadowMaps = frameState.shadowState.lightShadowMaps;
var lightShadowsEnabled = frameState.shadowState.lightShadowsEnabled;

var derivedCommands = command.derivedCommands;

if (shadowsEnabled && (command.receiveShadows || command.castShadows)) {
derivedCommands.shadows = ShadowMap.createDerivedCommands(shadowMaps, lightShadowMaps, command, shadowsDirty, context, derivedCommands.shadows);
if (lightShadowsEnabled && command.receiveShadows) {
derivedCommands.shadows = ShadowMap.createReceiveDerivedCommand(lightShadowMaps, command, shadowsDirty, context, derivedCommands.shadows);
}

if (defined(command.pickId)) {
Expand Down Expand Up @@ -1513,6 +1511,12 @@ define([
if (command.dirty) {
command.dirty = false;

var shadowMaps = frameState.shadowState.shadowMaps;
var shadowsEnabled = frameState.shadowState.shadowsEnabled;
if (shadowsEnabled && command.castShadows) {
derivedCommands.shadows = ShadowMap.createCastDerivedCommand(shadowMaps, command, shadowsDirty, context, derivedCommands.shadows);
}

if (hasLogDepthDerivedCommands || needsLogDepthDerivedCommands) {
derivedCommands.logDepth = DerivedCommand.createLogDepthCommand(command, context, derivedCommands.logDepth);
updateDerivedCommands(this, derivedCommands.logDepth.command, shadowsDirty);
Expand Down
87 changes: 87 additions & 0 deletions Source/Scene/ShadowMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,93 @@ define([
return result;
};

ShadowMap.createReceiveDerivedCommand = function(lightShadowMaps, command, shadowsDirty, context, result) {
if (!defined(result)) {
result = {};
}

var lightShadowMapsEnabled = (lightShadowMaps.length > 0);
var shaderProgram = command.shaderProgram;
var vertexShaderSource = shaderProgram.vertexShaderSource;
var fragmentShaderSource = shaderProgram.fragmentShaderSource;
var isTerrain = command.pass === Pass.GLOBE;

var hasTerrainNormal = false;
if (isTerrain) {
hasTerrainNormal = command.owner.data.pickTerrain.mesh.encoding.hasVertexNormals;
}

if (command.receiveShadows && lightShadowMapsEnabled) {
// Only generate a receiveCommand if there is a shadow map originating from a light source.
var receiveShader;
var receiveUniformMap;
if (defined(result.receiveCommand)) {
receiveShader = result.receiveCommand.shaderProgram;
receiveUniformMap = result.receiveCommand.uniformMap;
}

result.receiveCommand = DrawCommand.shallowClone(command, result.receiveCommand);
result.castShadows = false;
result.receiveShadows = true;

// If castShadows changed, recompile the receive shadows shader. The normal shading technique simulates
// self-shadowing so it should be turned off if castShadows is false.
var castShadowsDirty = result.receiveShaderCastShadows !== command.castShadows;
var shaderDirty = result.receiveShaderProgramId !== command.shaderProgram.id;

if (!defined(receiveShader) || shaderDirty || shadowsDirty || castShadowsDirty) {
var keyword = ShadowMapShader.getShadowReceiveShaderKeyword(lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal);
receiveShader = context.shaderCache.getDerivedShaderProgram(shaderProgram, keyword);
if (!defined(receiveShader)) {
var receiveVS = ShadowMapShader.createShadowReceiveVertexShader(vertexShaderSource, isTerrain, hasTerrainNormal);
var receiveFS = ShadowMapShader.createShadowReceiveFragmentShader(fragmentShaderSource, lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal);

receiveShader = context.shaderCache.createDerivedShaderProgram(shaderProgram, keyword, {
vertexShaderSource : receiveVS,
fragmentShaderSource : receiveFS,
attributeLocations : shaderProgram._attributeLocations
});
}

receiveUniformMap = combineUniforms(lightShadowMaps[0], command.uniformMap, isTerrain);
}

result.receiveCommand.shaderProgram = receiveShader;
result.receiveCommand.uniformMap = receiveUniformMap;
result.receiveShaderProgramId = command.shaderProgram.id;
result.receiveShaderCastShadows = command.castShadows;
}

console.log(result);
return result;
};

ShadowMap.createCastDerivedCommand = function(shadowMaps, command, shadowsDirty, context, result) {
if (!defined(result)) {
result = {};
}

if (command.castShadows) {
var castCommands = result.castCommands;
if (!defined(castCommands)) {
castCommands = result.castCommands = [];
}

var oldShaderId = result.castShaderProgramId;

var shadowMapLength = shadowMaps.length;
castCommands.length = shadowMapLength;

for (var i = 0; i < shadowMapLength; ++i) {
castCommands[i] = createCastDerivedCommand(shadowMaps[i], shadowsDirty, command, context, oldShaderId, castCommands[i]);
}

result.castShaderProgramId = command.shaderProgram.id;
}

return result;
};

/**
* @private
*/
Expand Down
4 changes: 2 additions & 2 deletions Source/Scene/ShadowMapShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ define([
var defines = vs.defines.slice(0);
var sources = vs.sources.slice(0);

vs.defines.push('SHADOW_MAP');
defines.push('SHADOW_MAP');

if (isTerrain) {
defines.push('GENERATE_POSITION');
Expand Down Expand Up @@ -132,7 +132,7 @@ define([
var defines = vs.defines.slice(0);
var sources = vs.sources.slice(0);

vs.defines.push('SHADOW_MAP');
defines.push('SHADOW_MAP');

if (isTerrain) {
if (hasTerrainNormal) {
Expand Down
19 changes: 6 additions & 13 deletions Specs/Scene/ShadowMapSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,8 @@ defineSuite([
});

it('model updates derived commands when the shadow map is dirty', function() {
var spy = spyOn(ShadowMap, 'createDerivedCommands').and.callThrough();
var spy1 = spyOn(ShadowMap, 'createReceiveDerivedCommand').and.callThrough();
var spy2 = spyOn(ShadowMap, 'createCastDerivedCommand').and.callThrough();

box.show = true;
floor.show = true;
Expand Down Expand Up @@ -1117,18 +1118,10 @@ defineSuite([
scene.render();
}

var callCount;
if (!scene.logarithmicDepthBuffer) {
// Expect derived commands to be updated twice for both the floor and box,
// once on the first frame and again when the shadow map is dirty
callCount = 4;
} else {
// Same as without log z, but doubled. The derived cast commands do not write log z, but
// the derived receive commands do.
callCount = 8;
}

expect(spy.calls.count()).toEqual(callCount);
// Expect derived commands to be updated twice for both the floor and box,
// once on the first frame and again when the shadow map is dirty
expect(spy1.calls.count()).toEqual(4);
expect(spy2.calls.count()).toEqual(4);

box.show = false;
floor.show = false;
Expand Down

0 comments on commit 6de0c0b

Please sign in to comment.