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

Shadow Map Start #3551

Merged
merged 5 commits into from
Feb 12, 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
235 changes: 235 additions & 0 deletions Apps/SampleData/models/shadow_tester.gltf

Large diffs are not rendered by default.

109 changes: 109 additions & 0 deletions Apps/Sandcastle/gallery/development/Shadows.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="description" content="Shadow maps.">
<meta name="cesium-sandcastle-labels" content="Tutorials,Showcases">
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
<script type="text/javascript">
require.config({
baseUrl : '../../../Source',
waitSeconds : 60
});
</script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar"></div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
"use strict";
//Sandcastle_Begin

var globeVisible = false;

var viewer = new Cesium.Viewer('cesiumContainer', {
infoBox : false,
selectionIndicator : false,
globe : globeVisible ? undefined : false,
skyAtmosphere : globeVisible ? undefined : false
});

var scene = viewer.scene;
var camera = scene.camera;
var shadowMap = scene.sunShadowMap;
shadowMap.debugShow = true;

var centerLongitude = -1.31968;
var centerLatitude = 0.698874;

camera.lookAt(Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, 50), new Cesium.Cartesian3(0.0, 0.0, 30.0));

function createModel(url) {
var origin = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, 50);
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, 0.0, 0.0, 0.0);

var model = scene.primitives.add(Cesium.Model.fromGltf({
url : url,
modelMatrix : modelMatrix
}));

model.readyPromise.then(function(model) {
// Play and loop all animations at half-speed
model.activeAnimations.addAll({
speedup : 0.5,
loop : Cesium.ModelAnimationLoop.REPEAT
});
}).otherwise(function(error){
window.alert(error);
});
}

//createModel('../../SampleData/models/CesiumAir/Cesium_Air.glb');
createModel('../../SampleData/models/shadow_tester.gltf');

function setCascades(cascades) {
return function() {
window.alert('Set cascades: ' + i);
};
}

var cascades = 4;
var cascadeOptions = [];
for (var i = 1; i <= cascades; ++i) {
cascadeOptions.push({
text : 'Cascades: ' + i,
onSelect : setCascades(i)
});
}

Sandcastle.addToolbarMenu(cascadeOptions);

Sandcastle.addToolbarButton('Debug on/off', function() {
shadowMap.debugShow = !shadowMap.debugShow;
});

Sandcastle.addToolbarButton('Freeze on/off', function() {
shadowMap.debugFreezeFrame = !shadowMap.debugFreezeFrame;
});


//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
32 changes: 32 additions & 0 deletions Source/Renderer/AutomaticUniforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,38 @@ define([
getValue : function(uniformState) {
return uniformState.fogDensity;
}
}),

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

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

Expand Down
33 changes: 24 additions & 9 deletions Source/Renderer/UniformState.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ define([
this._resolutionScale = 1.0;

this._fogDensity = undefined;

this.shadowMap = undefined;
}

defineProperties(UniformState.prototype, {
Expand Down Expand Up @@ -845,6 +847,23 @@ define([
projection.project(sunCartographic, uniformState._sunPositionColumbusView);
}

/**
* Synchronizes the frustum's state with the camera state. This is called
* by the {@link Scene} when rendering to ensure that automatic GLSL uniforms
* are set to the right value.
*
* @param {Object} camera The camera to synchronize with.
*/
UniformState.prototype.updateCamera = function(camera) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@bagnell so far is this inline with your vision of having separate viewports with separate cameras for things like picture-in-picture?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup, this is fine with me.

setView(this, camera.viewMatrix);
setInverseView(this, camera.inverseViewMatrix);
setCamera(this, camera);

this._entireFrustum.x = camera.frustum.near;
this._entireFrustum.y = camera.frustum.far;
this.updateFrustum(camera.frustum);
};

/**
* Synchronizes the frustum's state with the uniform state. This is called
* by the {@link Scene} when rendering to ensure that automatic GLSL uniforms
Expand Down Expand Up @@ -876,19 +895,17 @@ define([
* are set to the right value.
*
* @param {FrameState} frameState The frameState to synchronize with.
* @param {ShadowMap} [shadowMap] The shadow map.
*/
UniformState.prototype.update = function(frameState) {
UniformState.prototype.update = function(frameState, shadowMap) {
this._mode = frameState.mode;
this._mapProjection = frameState.mapProjection;

var canvas = frameState.context._canvas;
this._resolutionScale = canvas.width / canvas.clientWidth;

var camera = frameState.camera;

setView(this, camera.viewMatrix);
setInverseView(this, camera.inverseViewMatrix);
setCamera(this, camera);
this.updateCamera(camera);
Copy link
Contributor

Choose a reason for hiding this comment

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

I haven't looked at the rest of the code yet, but does it make sense to not call this here and require the user to call it, e.g.,

renderFrame
  uniformState.update()
  foreach camera
    uniformState.updateCamera()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing this.updateCamera(camera); breaks a lot of tests, so I'm leaving it in.

Copy link
Contributor

Choose a reason for hiding this comment

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

Add a TODO to your roadmap since those tests should probably call update and updateCamera (or we can make a tests-specific helper that calls both). I hate to make the render workflow more complicated so we don't have to change tests.


if (frameState.mode === SceneMode.SCENE2D) {
this._frustum2DWidth = camera.frustum.right - camera.frustum.left;
Expand All @@ -902,12 +919,10 @@ define([

setSunAndMoonDirections(this, frameState);

this._entireFrustum.x = camera.frustum.near;
this._entireFrustum.y = camera.frustum.far;
this.updateFrustum(camera.frustum);

this._fogDensity = frameState.fog.density;

this.shadowMap = shadowMap;

this._frameState = frameState;
this._temeToPseudoFixed = Transforms.computeTemeToPseudoFixedMatrix(frameState.time, this._temeToPseudoFixed);
};
Expand Down
6 changes: 6 additions & 0 deletions Source/Scene/FrameState.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ define([
* @type {Number}
*/
this.terrainExaggeration = 1.0;

/**
* Whether shadow mapping is enabled or not.
* @type {Boolean}
*/
this.shadowsEnabled = false;
}

/**
Expand Down
8 changes: 3 additions & 5 deletions Source/Scene/GlobeDepth.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,6 @@ define([
}

function createFramebuffers(globeDepth, context, width, height) {
destroyTextures(globeDepth);
destroyFramebuffers(globeDepth);

createTextures(globeDepth, context, width, height);

globeDepth.framebuffer = new Framebuffer({
context : context,
colorTextures : [globeDepth._colorTexture],
Expand All @@ -137,6 +132,9 @@ define([
var colorTexture = globeDepth._colorTexture;
var textureChanged = !defined(colorTexture) || colorTexture.width !== width || colorTexture.height !== height;
if (!defined(globeDepth.framebuffer) || textureChanged) {
destroyTextures(globeDepth);
destroyFramebuffers(globeDepth);
createTextures(globeDepth, context, width, height);
createFramebuffers(globeDepth, context, width, height);
}
}
Expand Down
33 changes: 26 additions & 7 deletions Source/Scene/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ define([
'./ModelMesh',
'./ModelNode',
'./Pass',
'./SceneMode'
'./SceneMode',
'./ShadowMap'
], function(
BoundingSphere,
Cartesian2,
Expand Down Expand Up @@ -108,7 +109,8 @@ define([
ModelMesh,
ModelNode,
Pass,
SceneMode) {
SceneMode,
ShadowMap) {
'use strict';

// Bail out if the browser doesn't support typed arrays, to prevent the setup function
Expand Down Expand Up @@ -556,6 +558,16 @@ define([
// CESIUM_RTC extension
this._rtcCenter = undefined; // in world coordinates
this._rtcCenterEye = undefined; // in eye coordinates

/**
* DOC_TBA
*/
this.receiveShadows = true;

/**
* @DOC_TBA
*/
this.castShadows = true;
}

defineProperties(Model.prototype, {
Expand Down Expand Up @@ -1461,7 +1473,8 @@ define([
return shader;
}

function createProgram(id, model, context) {
function createProgram(id, model, frameState) {
var context = frameState.context;
var programs = model.gltf.programs;
var shaders = model._loadResources.shaders;
var program = programs[id];
Expand All @@ -1473,6 +1486,12 @@ define([
var drawVS = modifyShader(vs, id, model._vertexShaderLoaded);
var drawFS = modifyShader(fs, id, model._fragmentShaderLoaded);

// TODO : handle updating these properties at runtime
if (frameState.shadowsEnabled && model.receiveShadows) {
drawVS = ShadowMap.createReceiveShadowsVertexShader(drawVS);
drawFS = ShadowMap.createReceiveShadowsFragmentShader(drawFS);
}

// Add pre-created attributes to attributeLocations
var attributesLength = program.attributes.length;
var precreatedAttributes = model._precreatedAttributes;
Expand Down Expand Up @@ -1509,7 +1528,7 @@ define([
}
}

function createPrograms(model, context) {
function createPrograms(model, frameState) {
var loadResources = model._loadResources;
var id;

Expand All @@ -1527,13 +1546,13 @@ define([
// Create one program per frame
if (loadResources.programsToCreate.length > 0) {
id = loadResources.programsToCreate.dequeue();
createProgram(id, model, context);
createProgram(id, model, frameState);
}
} else {
// Create all loaded programs this frame
while (loadResources.programsToCreate.length > 0) {
id = loadResources.programsToCreate.dequeue();
createProgram(id, model, context);
createProgram(id, model, frameState);
}
}
}
Expand Down Expand Up @@ -2733,7 +2752,7 @@ define([
}
} else {
createBuffers(model, context); // using glTF bufferViews
createPrograms(model, context);
createPrograms(model, frameState);
createSamplers(model, context);
loadTexturesFromBufferViews(model);
createTextures(model, context);
Expand Down
Loading