diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index f93f35934cb1..8161349f7924 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -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)) { @@ -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); diff --git a/Source/Scene/ShadowMap.js b/Source/Scene/ShadowMap.js index 98d875e1e8fa..005febbc3ee8 100644 --- a/Source/Scene/ShadowMap.js +++ b/Source/Scene/ShadowMap.js @@ -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 */ diff --git a/Source/Scene/ShadowMapShader.js b/Source/Scene/ShadowMapShader.js index 6b04939e652f..74bb6cc01c34 100644 --- a/Source/Scene/ShadowMapShader.js +++ b/Source/Scene/ShadowMapShader.js @@ -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'); @@ -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) { diff --git a/Specs/Scene/ShadowMapSpec.js b/Specs/Scene/ShadowMapSpec.js index 06ea1505f331..d8609a591018 100644 --- a/Specs/Scene/ShadowMapSpec.js +++ b/Specs/Scene/ShadowMapSpec.js @@ -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; @@ -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;