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

Use cube map for point light shadows #3718

Merged
merged 2 commits into from
Mar 17, 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
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
32 changes: 16 additions & 16 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 Expand Up @@ -1563,31 +1577,17 @@ define([
}),

/**
* An automatic GLSL uniform representing the sun's shadow map light position in eye coordinates.
* An automatic GLSL uniform that stores the point light's eye space position and radius.
*
* @alias czm_sunShadowMapLightPositionEC
* @glslUniform
*/
czm_sunShadowMapLightPositionEC : new AutomaticUniform({
size : 1,
datatype : WebGLConstants.FLOAT_VEC3,
datatype : WebGLConstants.FLOAT_VEC4,
getValue : function(uniformState) {
return uniformState.shadowMap.lightPositionEC;
}
}),

/**
* An automatic GLSL uniform representing the sun's shadow map radius.
*
* @alias czm_sunShadowMapRadius
* @glslUniform
*/
czm_sunShadowMapRadius : new AutomaticUniform({
size : 1,
datatype : WebGLConstants.FLOAT,
getValue : function(uniformState) {
return uniformState.shadowMap.radius;
}
})
};

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);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would be cleaner just to call _bind here, but then you end up re-binding the FBO when you render the shadow casters.

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
158 changes: 139 additions & 19 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 @@ -138,7 +140,7 @@ define([
this._shadowMapMatrix = new Matrix4();
this._shadowMapTexture = undefined;
this._lightDirectionEC = new Cartesian3();
this._lightPositionEC = new Cartesian3();
this._lightPositionEC = new Cartesian4();

this._framebuffer = undefined;
this._shadowMapSize = defaultValue(options.size, 1024);
Expand All @@ -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,9 +350,9 @@ define([
return this._isPointLight;
}
},
radius : {
usesCubeMap : {
get : function() {
return this._radius;
return this._usesCubeMap;
}
},
usesDepthTexture : {
Expand All @@ -354,7 +363,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 +427,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 +501,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 +563,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 @@ -900,6 +1010,7 @@ define([

// Get the light position in eye coordinates
Matrix4.multiplyByPoint(camera.viewMatrix, shadowMapCamera.positionWC, shadowMap._lightPositionEC);
shadowMap._lightPositionEC.w = shadowMap._radius;

// Get the near and far of the scene camera
var near;
Expand Down Expand Up @@ -944,6 +1055,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