Skip to content

Commit

Permalink
Use cube map for point light shadows
Browse files Browse the repository at this point in the history
  • Loading branch information
lilleyse committed Mar 17, 2016
1 parent 4876a80 commit e2f05f6
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 44 deletions.
2 changes: 2 additions & 0 deletions Apps/Sandcastle/gallery/development/Shadows.html
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@
var center;
var height;

scene.debugShowFramesPerSecond = true;

var fixedLightCamera = new Cesium.Camera(scene);
var freeformLightCamera = new Cesium.Camera(scene);
var sunCamera = scene._sunCamera;
Expand Down
14 changes: 14 additions & 0 deletions Source/Renderer/AutomaticUniforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,20 @@ define([
}
}),

/**
* An automatic GLSL uniform representing the sun's shadow map texture as a cube map.
*
* @alias czm_sunShadowMapTextureCube
* @glslUniform
*/
czm_sunShadowMapTextureCube : new AutomaticUniform({
size : 1,
datatype : WebGLConstants.SAMPLER_CUBE,
getValue : function(uniformState) {
return uniformState.shadowMap.shadowMapTexture;
}
}),

/**
* An automatic GLSL uniform representing the sun's shadow map matrix.
*
Expand Down
4 changes: 4 additions & 0 deletions Source/Renderer/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,10 @@ define([
}
};

Context.prototype.bindFramebuffer = function(framebuffer) {
bindFramebuffer(this, framebuffer);
};

Context.prototype.readPixels = function(readState) {
var gl = this._gl;

Expand Down
2 changes: 1 addition & 1 deletion Source/Renderer/CubeMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ define([
this._positiveZ = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_Z, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY);
this._negativeZ = new CubeMapFace(gl, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY);

this.sampler = new Sampler();
this.sampler = defined(options.sampler) ? options.sampler : new Sampler();
}

defineProperties(CubeMap.prototype, {
Expand Down
5 changes: 5 additions & 0 deletions Source/Renderer/Framebuffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,11 @@ define([
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};

Framebuffer.prototype._attachTexture = function(context, attachment, texture) {
context.bindFramebuffer(this);
attachTexture(this, attachment, texture);
};

Framebuffer.prototype._getActiveColorAttachments = function() {
return this._activeColorAttachments;
};
Expand Down
4 changes: 2 additions & 2 deletions Source/Scene/GlobeSurfaceShaderSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,8 @@ define([
vs.sources.push(getPositionMode(sceneMode));
vs.sources.push(get2DYPositionFraction(useWebMercatorProjection));

vs.sources[1] = ShadowMapShader.createShadowCastVertexShader(vs.sources[1]);
fs.sources[0] = ShadowMapShader.createShadowCastFragmentShader(fs.sources[0], frameState, true);
vs.sources[1] = ShadowMapShader.createShadowCastVertexShader(vs.sources[1], frameState, 'v_positionEC');
fs.sources[0] = ShadowMapShader.createShadowCastFragmentShader(fs.sources[0], frameState, true, 'v_positionEC');

shadowCastShader = this._shadowCastPrograms[flags] = ShaderProgram.fromCache({
context : frameState.context,
Expand Down
4 changes: 2 additions & 2 deletions Source/Scene/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -1502,8 +1502,8 @@ define([
// Create shadow cast program
var shadowsEnabled = defined(frameState.shadowMap) && frameState.shadowMap.enabled;
if (shadowsEnabled && model.castShadows) {
var shadowCastVS = ShadowMapShader.createShadowCastVertexShader(drawVS);
var shadowCastFS = ShadowMapShader.createShadowCastFragmentShader(drawFS, frameState, false);
var shadowCastVS = ShadowMapShader.createShadowCastVertexShader(drawVS, frameState, 'v_positionEC');
var shadowCastFS = ShadowMapShader.createShadowCastFragmentShader(drawFS, frameState, false, 'v_positionEC');
model._rendererResources.shadowCastPrograms[id] = ShaderProgram.fromCache({
context : context,
vertexShaderSource : shadowCastVS,
Expand Down
4 changes: 2 additions & 2 deletions Source/Scene/Primitive.js
Original file line number Diff line number Diff line change
Expand Up @@ -1113,8 +1113,8 @@ define([
// Create shadow cast program
var shadowsEnabled = defined(frameState.shadowMap) && frameState.shadowMap.enabled;
if (!defined(primitive._shadowCastSP) && shadowsEnabled && primitive._castShadows) {
var shadowCastVS = ShadowMapShader.createShadowCastVertexShader(vs);
var shadowCastFS = ShadowMapShader.createShadowCastFragmentShader(fs, frameState, false);
var shadowCastVS = ShadowMapShader.createShadowCastVertexShader(vs, frameState, 'v_positionEC');
var shadowCastFS = ShadowMapShader.createShadowCastFragmentShader(fs, frameState, false, 'v_positionEC');
primitive._shadowCastSP = ShaderProgram.fromCache({
context : context,
vertexShaderSource : shadowCastVS,
Expand Down
4 changes: 1 addition & 3 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -1699,15 +1699,13 @@ define([
var shadowMap = scene.sunShadowMap;
var renderState = shadowMap.renderState;

// Clear shadow depth
shadowMap.clearCommand.execute(context);

var commands = getShadowMapCommands(scene);
var numberOfCommands = commands.length;
var numberOfPasses = shadowMap.numberOfPasses;
for (var i = 0; i < numberOfPasses; ++i) {
uniformState.updateCamera(shadowMap.passCameras[i]);
var passState = shadowMap.passStates[i];
shadowMap.updatePass(context, i);
for (var j = 0; j < numberOfCommands; ++j) {
var command = commands[j];
executeCommand(command, scene, context, passState, renderState, command.shadowCastProgram);
Expand Down
156 changes: 140 additions & 16 deletions Source/Scene/ShadowMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ define([
'../Core/PrimitiveType',
'../Core/SphereOutlineGeometry',
'../Renderer/ClearCommand',
'../Renderer/CubeMap',
'../Renderer/Framebuffer',
'../Renderer/PassState',
'../Renderer/PixelDatatype',
Expand Down Expand Up @@ -70,6 +71,7 @@ define([
PrimitiveType,
SphereOutlineGeometry,
ClearCommand,
CubeMap,
Framebuffer,
PassState,
PixelDatatype,
Expand Down Expand Up @@ -150,9 +152,10 @@ define([

this._isPointLight = defaultValue(options.isPointLight, false);
this._radius = defaultValue(options.radius, 100.0);
this._usesCubeMap = true;

this._cascadesEnabled = this._isPointLight ? false : defaultValue(options.cascadesEnabled, true);
this._numberOfCascades = !this._cascadesEnabled ? 0 :defaultValue(options.numberOfCascades, 4);
this._numberOfCascades = !this._cascadesEnabled ? 0 : defaultValue(options.numberOfCascades, 4);
this._fitNearFar = true;
this._distance = 1000.0;

Expand All @@ -173,6 +176,7 @@ define([
this._numberOfPasses = numberOfPasses;
this._passCameras = new Array(numberOfPasses);
this._passStates = new Array(numberOfPasses);
this._passFaces = new Array(numberOfPasses); // For point shadows

for (var i = 0; i < numberOfPasses; ++i) {
this._passCameras[i] = new ShadowMapCamera();
Expand All @@ -190,6 +194,11 @@ define([

// Only enable the color mask if the depth texture extension is not supported
this._usesDepthTexture = context.depthTexture;

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

var colorMask = !this._usesDepthTexture;

// For shadow casters
Expand Down Expand Up @@ -341,6 +350,11 @@ define([
return this._isPointLight;
}
},
usesCubeMap : {
get : function() {
return this._usesCubeMap;
}
},
radius : {
get : function() {
return this._radius;
Expand All @@ -354,7 +368,12 @@ define([
});

function destroyFramebuffer(shadowMap) {
shadowMap._framebuffer = shadowMap._framebuffer && !shadowMap._framebuffer.isDestroyed() && shadowMap._framebuffer.destroy();
shadowMap._framebuffer = shadowMap._framebuffer && shadowMap._framebuffer.destroy();

// Need to destroy cube map separately
if (shadowMap._isPointLight && shadowMap._usesCubeMap) {
shadowMap._shadowMapTexture = shadowMap._shadowMapTexture && shadowMap._shadowMapTexture.destroy();
}
}

function createSampler() {
Expand Down Expand Up @@ -413,9 +432,49 @@ define([
return framebuffer;
}

function createFramebufferCube(shadowMap, context) {
var depthRenderbuffer = new Renderbuffer({
context : context,
width : shadowMap._shadowMapSize,
height : shadowMap._shadowMapSize,
format : RenderbufferFormat.DEPTH_COMPONENT16
});

var cubeMap = new CubeMap({
context : context,
width : shadowMap._shadowMapSize,
height : shadowMap._shadowMapSize,
pixelFormat : PixelFormat.RGBA,
pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
sampler : createSampler()
});

var framebuffer = new Framebuffer({
context : context,
depthRenderbuffer : depthRenderbuffer,
colorTextures : [cubeMap.positiveX]
});

shadowMap._passFaces[0] = cubeMap.negativeX;
shadowMap._passFaces[1] = cubeMap.negativeY;
shadowMap._passFaces[2] = cubeMap.negativeZ;
shadowMap._passFaces[3] = cubeMap.positiveX;
shadowMap._passFaces[4] = cubeMap.positiveY;
shadowMap._passFaces[5] = cubeMap.positiveZ;

shadowMap._shadowMapTexture = cubeMap;
return framebuffer;
}

function createFramebuffer(shadowMap, context) {
var createFunction = (shadowMap._usesDepthTexture) ? createFramebufferDepth : createFramebufferColor;
var framebuffer = createFunction(shadowMap, context);
var framebuffer;
if (shadowMap._isPointLight && shadowMap._usesCubeMap) {
framebuffer = createFramebufferCube(shadowMap, context);
} else if (shadowMap._usesDepthTexture) {
framebuffer = createFramebufferDepth(shadowMap, context);
} else {
framebuffer = createFramebufferColor(shadowMap, context);
}
shadowMap._framebuffer = framebuffer;
shadowMap._clearCommand.framebuffer = framebuffer;
for (var i = 0; i < shadowMap._numberOfPasses; ++i) {
Expand Down Expand Up @@ -447,7 +506,17 @@ define([
var numberOfPasses = this._numberOfPasses;
var textureSize = this._textureSize;

if (numberOfPasses === 1) {
if (this._isPointLight && this._usesCubeMap) {
textureSize.x = size;
textureSize.y = size;
var viewport = new BoundingRectangle(0, 0, size, size);
this._passStates[0].viewport = viewport;
this._passStates[1].viewport = viewport;
this._passStates[2].viewport = viewport;
this._passStates[3].viewport = viewport;
this._passStates[4].viewport = viewport;
this._passStates[5].viewport = viewport;
} else if (numberOfPasses === 1) {
// +----+
// | 1 |
// +----+
Expand Down Expand Up @@ -499,17 +568,63 @@ define([
var height = size;

if (!defined(shadowMap._debugShadowViewCommand)) {
var fs =
'varying vec2 v_textureCoordinates; \n' +
'void main() \n' +
'{ \n' +

(shadowMap._usesDepthTexture ?
' float shadow = texture2D(czm_sunShadowMapTexture, v_textureCoordinates).r; \n' :
' float shadow = czm_unpackDepth(texture2D(czm_sunShadowMapTexture, v_textureCoordinates)); \n') +

' gl_FragColor = vec4(vec3(shadow), 1.0); \n' +
'} \n';
var fs;
if (shadowMap._isPointLight && shadowMap._usesCubeMap) {
fs = 'varying vec2 v_textureCoordinates; \n' +
'void main() \n' +
'{ \n' +
' vec2 uv = v_textureCoordinates; \n' +
' vec3 dir; \n' +
' \n' +
' if (uv.y < 0.5) { \n' +
' if (uv.x < 0.333) { \n' +
' dir.x = -1.0; \n' +
' dir.y = uv.x * 6.0 - 1.0; \n' +
' dir.z = uv.y * 4.0 - 1.0; \n' +
' } \n' +
' else if (uv.x < 0.666) { \n' +
' dir.y = -1.0; \n' +
' dir.x = uv.x * 6.0 - 3.0; \n' +
' dir.z = uv.y * 4.0 - 1.0; \n' +
' } \n' +
' else { \n' +
' dir.z = -1.0; \n' +
' dir.x = uv.x * 6.0 - 5.0; \n' +
' dir.y = uv.y * 4.0 - 1.0; \n' +
' } \n' +
' } else { \n' +
' if (uv.x < 0.333) { \n' +
' dir.x = 1.0; \n' +
' dir.y = uv.x * 6.0 - 1.0; \n' +
' dir.z = uv.y * 4.0 - 3.0; \n' +
' } \n' +
' else if (uv.x < 0.666) { \n' +
' dir.y = 1.0; \n' +
' dir.x = uv.x * 6.0 - 3.0; \n' +
' dir.z = uv.y * 4.0 - 3.0; \n' +
' } \n' +
' else { \n' +
' dir.z = 1.0; \n' +
' dir.x = uv.x * 6.0 - 5.0; \n' +
' dir.y = uv.y * 4.0 - 3.0; \n' +
' } \n' +
' } \n' +
' \n' +
' float shadow = czm_unpackDepth(textureCube(czm_sunShadowMapTextureCube, dir)); \n' +
' gl_FragColor = vec4(vec3(shadow), 1.0); \n' +
'} \n';
} else {
fs = 'varying vec2 v_textureCoordinates; \n' +
'void main() \n' +
'{ \n' +

(shadowMap._usesDepthTexture ?
' float shadow = texture2D(czm_sunShadowMapTexture, v_textureCoordinates).r; \n' :
' float shadow = czm_unpackDepth(texture2D(czm_sunShadowMapTexture, v_textureCoordinates)); \n') +

' gl_FragColor = vec4(vec3(shadow), 1.0); \n' +
'} \n';
}

// Set viewport now to avoid using a cached render state
var renderState = RenderState.fromCache({
Expand Down Expand Up @@ -944,6 +1059,15 @@ define([
}
};

ShadowMap.prototype.updatePass = function(context, pass) {
if (this._isPointLight && this._usesCubeMap) {
this._framebuffer._attachTexture(context, WebGLConstants.COLOR_ATTACHMENT0, this._passFaces[pass]);
this._clearCommand.execute(context);
} else if (pass === 0) {
this._clearCommand.execute(context);
}
};

ShadowMap.prototype.updateShadowMapMatrix = function(uniformState) {
if (this._isPointLight) {
// Point light shadows do not project into light space
Expand Down
Loading

0 comments on commit e2f05f6

Please sign in to comment.