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

Soft shadows #3731

Merged
merged 4 commits into from
Mar 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 17 additions & 6 deletions Apps/Sandcastle/gallery/development/Shadows.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@
<td>Fit near/far</td>
<td><input type="checkbox" data-bind="checked: fitNearFar"/></td>
</tr>
<tr>
<td>Soft shadows</td>
<td><input type="checkbox" data-bind="checked: softShadows"/></td>
</tr>
</tbody></table>
</div>
<script id="cesium_sandcastle_script">
Expand All @@ -114,10 +118,11 @@
freeze : false,
cascadeColors : false,
fitNearFar : true,
softShadows : true,
cascadeOptions : [1, 4],
cascades : 4,
lightSourceOptions : ['Freeform', 'Sun', 'Fixed', 'Point'],
lightSource : 'Point',
lightSource : 'Freeform',
sizeOptions : [256, 512, 1024, 2048],
size : 1024
};
Expand All @@ -135,6 +140,7 @@
Cesium.knockout.getObservable(viewModel, 'terrainCast').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, 'cascades').subscribe(updateShadows);
Cesium.knockout.getObservable(viewModel, 'lightSource').subscribe(updateShadows);
Cesium.knockout.getObservable(viewModel, 'size').subscribe(updateSettings);
Expand Down Expand Up @@ -169,6 +175,7 @@
var cascades = viewModel.cascades;
var lightSource = viewModel.lightSource;
var cascadeColors = viewModel.cascadeColors;
var softShadows = viewModel.softShadows;

var lightCamera;
if (lightSource === 'Freeform') {
Expand All @@ -181,25 +188,29 @@
resetShadows({
context : context,
lightCamera : fixedLightCamera,
cascadesEnabled : false
cascadesEnabled : false,
softShadows : softShadows
});
} else if (lightSource === 'Point') {
resetShadows({
context : context,
lightCamera : pointLightCamera,
isPointLight : true
isPointLight : true,
softShadows : softShadows
});
} else if (cascades === 4) {
resetShadows({
context : context,
lightCamera : lightCamera
lightCamera : lightCamera,
softShadows : softShadows
});
shadowMap.debugVisualizeCascades = cascadeColors;
} else if (cascades === 1) {
resetShadows({
context : context,
lightCamera : lightCamera,
numberOfCascades : 1
numberOfCascades : 1,
softShadows : softShadows
});
}

Expand Down Expand Up @@ -316,7 +327,7 @@
var position3 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 15.0);

//createModel('../../SampleData/models/CesiumAir/Cesium_Air.glb', position3);
createModel('../../SampleData/models/ShadowTester/Shadow_Tester_Point.gltf', center);
createModel('../../SampleData/models/ShadowTester/Shadow_Tester.gltf', center);
//createModel('../../SampleData/models/WoodTower/Wood_Tower.gltf', ground);
createBox(position3);
createBoxRTC(position2);
Expand Down
14 changes: 14 additions & 0 deletions Source/Renderer/AutomaticUniforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,20 @@ define([
getValue : function(uniformState) {
return uniformState.shadowMap.lightPositionEC;
}
}),

/**
* An automatic GLSL uniform representing the step size for sampling a neighbor texel, equivalent to 1.0 / textureDimensionInPixels.
*
* @alias czm_shadowMapTexelStepSize
* @glslUniform
*/
czm_shadowMapTexelStepSize : new AutomaticUniform({
size : 1,
datatype : WebGLConstants.FLOAT_VEC2,
getValue : function(uniformState) {
return uniformState.shadowMap.texelStepSize;
}
})
};

Expand Down
13 changes: 12 additions & 1 deletion Source/Scene/ShadowMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ define([
* @param {Boolean} [options.cascadesEnabled=true] Use multiple shadow maps to cover different partitions of the view frustum.
* @param {Number} [options.numberOfCascades=4] The number of cascades to use for the shadow map. Supported values are one and four.
* @param {Number} [options.size=1024] The width and height, in pixels, of each shadow map.
* @param {Boolean} [options.softShadows=false] Whether percentage-closer-filtering is enabled for producing softer shadows.
*
* @see ShadowMapShader
*
Expand All @@ -135,6 +136,7 @@ define([
//>>includeEnd('debug');

this.enabled = false;
this.softShadows = defaultValue(options.softShadows, false);

// Framebuffer resources
this._depthAttachment = undefined;
Expand Down Expand Up @@ -197,7 +199,7 @@ define([

this._usesDepthTexture = context.depthTexture;

if (this._isPointLight && this._usesCubeMap) {
if (this._isPointLight) {
this._usesDepthTexture = false;
}

Expand Down Expand Up @@ -242,6 +244,8 @@ define([
});
}

var scratchTexelStepSize = new Cartesian2();

defineProperties(ShadowMap.prototype, {
/**
* The shadow map texture used in shadow receive programs.
Expand Down Expand Up @@ -364,6 +368,13 @@ define([
get : function() {
return this._usesDepthTexture;
}
},
texelStepSize : {
get : function() {
scratchTexelStepSize.x = 1.0 / this._textureSize.x;
scratchTexelStepSize.y = 1.0 / this._textureSize.y;
return scratchTexelStepSize;
}
}
});

Expand Down
68 changes: 41 additions & 27 deletions Source/Scene/ShadowMapShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ define([
ShadowMapShader.createShadowCastVertexShader = function(vs, frameState, positionVaryingName) {
var hasPositionVarying = defined(positionVaryingName) && (vs.indexOf(positionVaryingName) > -1);
var isPointLight = frameState.shadowMap.isPointLight;
var usesCubeMap = frameState.shadowMap.usesCubeMap;

if (isPointLight && usesCubeMap && !hasPositionVarying) {
if (isPointLight && !hasPositionVarying) {
vs = ShaderSource.replaceMain(vs, 'czm_shadow_main');
vs +=
'varying vec3 v_positionEC; \n' +
Expand All @@ -39,7 +38,6 @@ define([
var hasPositionVarying = defined(positionVaryingName) && (fs.indexOf(positionVaryingName) > -1);
positionVaryingName = hasPositionVarying ? positionVaryingName : 'v_positionEC';
var isPointLight = frameState.shadowMap.isPointLight;
var usesCubeMap = frameState.shadowMap.usesCubeMap;
var usesDepthTexture = frameState.shadowMap.usesDepthTexture;

if (opaque) {
Expand All @@ -56,7 +54,7 @@ define([
' } \n';
}

if (isPointLight && usesCubeMap) {
if (isPointLight) {
fs +=
'float distance = length(' + positionVaryingName + '); \n' +
'distance /= czm_shadowMapLightPositionEC.w; // radius \n' +
Expand All @@ -69,7 +67,7 @@ define([

fs += '}';

if (isPointLight && usesCubeMap && !hasPositionVarying) {
if (isPointLight && !hasPositionVarying) {
fs = 'varying vec3 v_positionEC; \n' + fs;
}

Expand Down Expand Up @@ -100,6 +98,7 @@ define([
var usesCubeMap = frameState.shadowMap.usesCubeMap;
var hasCascades = frameState.shadowMap.numberOfCascades > 1;
var debugVisualizeCascades = frameState.shadowMap.debugVisualizeCascades;
var softShadows = frameState.shadowMap.softShadows;

fs = ShaderSource.replaceMain(fs, 'czm_shadow_main');
fs +=
Expand Down Expand Up @@ -161,24 +160,40 @@ define([
' return czm_unpackDepth(texture2D(czm_shadowMapTexture, uv)); \n') +
'} \n' +
' \n') +

(isPointLight ? (usesCubeMap ?
(isPointLight && usesCubeMap ?
'float depthCompare(vec3 uv, float depth) \n' :
'float depthCompare(vec2 uv, float depth) \n') +
'{ \n' +
' float shadowDepth = sampleTexture(uv); \n' +
' return step(depth, shadowDepth); \n' +
'} \n' +

(isPointLight && usesCubeMap ?
'float getVisibility(vec3 uv, float depth, vec3 lightDirectionEC)' :
'float getVisibility(vec2 uv, float depth, vec3 lightDirectionEC, vec2 faceUV)') :
'float getVisibility(vec2 uv, float depth, vec3 lightDirectionEC)') +

'{ \n' +
' float shadowDepth = sampleTexture(uv); \n' +

(isPointLight && !usesCubeMap ?
' vec4 shadowCoord = vec4(faceUV, shadowDepth, 1.0); \n' +
' shadowCoord = shadowCoord * 2.0 - 1.0; \n' +
' shadowCoord = czm_shadowMapMatrix * shadowCoord; \n' +
' shadowCoord /= shadowCoord.w; \n' +
' shadowDepth = length(shadowCoord.xyz); \n' : '') +

' float visibility = step(depth, shadowDepth); \n' +

(softShadows ?
' float radius = 1.0; \n' +
' float dx0 = -czm_shadowMapTexelStepSize.x * radius; \n' +
' float dy0 = -czm_shadowMapTexelStepSize.y * radius; \n' +
' float dx1 = czm_shadowMapTexelStepSize.x * radius; \n' +
' float dy1 = czm_shadowMapTexelStepSize.y * radius; \n' +
' float visibility = ( \n' +
' depthCompare(uv, depth) + \n' +
' depthCompare(uv + vec2(dx0, dy0), depth) + \n' +
' depthCompare(uv + vec2(0.0, dy0), depth) + \n' +
' depthCompare(uv + vec2(dx1, dy0), depth) + \n' +
' depthCompare(uv + vec2(dx0, 0.0), depth) + \n' +
' depthCompare(uv + vec2(dx1, 0.0), depth) + \n' +
' depthCompare(uv + vec2(dx0, dy1), depth) + \n' +
' depthCompare(uv + vec2(0.0, dy1), depth) + \n' +
' depthCompare(uv + vec2(dx1, dy1), depth) \n' +
' ) * (1.0 / 9.0); \n' :

' float visibility = depthCompare(uv, depth); \n') +

(hasNormalVarying ?
' // If the normal is facing away from the light, then it is in shadow \n' +
' float angle = dot(normalize(' + normalVaryingName + '), lightDirectionEC); \n' +
Expand All @@ -190,20 +205,19 @@ define([
' return visibility; \n' +
'} \n' +
' \n' +
'vec2 directionToUV(vec3 d, out vec2 faceUV) { \n' +
'vec2 directionToUV(vec3 d) { \n' +
' \n' +
' vec3 abs = abs(d); \n' +
' float max = max(max(abs.x, abs.y), abs.z); // Get the largest component \n' +
' vec3 weights = step(max, abs); // 1.0 for the largest component, 0.0 for the others \n' +
' float sign = dot(weights, sign(d)) * 0.5 + 0.5; // 0 or 1 \n' +
' float sc = mix(dot(weights, vec3(d.z, d.x, -d.x)), dot(weights, vec3(-d.z, d.x, d.x)), sign); \n' +
' float tc = mix(dot(weights, vec3(-d.y, -d.z, -d.y)), dot(weights, vec3(-d.y, d.z, -d.y)), sign); \n' +
' faceUV = (vec2(sc, tc) / max) * 0.5 + 0.5; \n' +
' vec2 uv = (vec2(sc, tc) / max) * 0.5 + 0.5; \n' +
' float offsetX = dot(weights, vec3(0.0, 1.0, 2.0)); \n' +
' float offsetY = sign; \n' +
' vec2 uv; \n' +
' uv.x = (faceUV.x + offsetX) / 3.0; \n' +
' uv.y = (faceUV.y + offsetY) / 2.0; \n' +
' uv.x = (uv.x + offsetX) / 3.0; \n' +
' uv.y = (uv.y + offsetY) / 2.0; \n' +
' return uv; \n' +
'} \n';

Expand All @@ -224,13 +238,13 @@ define([
' return; \n' +
' } \n' +
' vec3 directionWC = czm_inverseViewRotation * directionEC; \n' +
' distance /= radius; \n' +

(usesCubeMap ?
' float visibility = getVisibility(directionWC, distance / radius, -directionEC); \n' :
' float visibility = getVisibility(directionWC, distance, -directionEC); \n' :

' vec2 faceUV; \n' +
' vec2 uv = directionToUV(directionWC, faceUV); \n' +
' float visibility = getVisibility(uv, distance, -directionEC, faceUV); \n') +
' vec2 uv = directionToUV(directionWC); \n' +
' float visibility = getVisibility(uv, distance, -directionEC); \n') +

' gl_FragColor.rgb *= visibility; \n' +
'} \n';
Expand Down