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

Shadows for terrain #3595

Merged
merged 5 commits into from
Feb 18, 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
78 changes: 57 additions & 21 deletions Apps/Sandcastle/gallery/development/Shadows.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
'use strict';
//Sandcastle_Begin

var globeVisible = false;
var globeVisible = true;
var terrainEnabled = true;

var viewer = new Cesium.Viewer('cesiumContainer', {
scene3DOnly : true,
Expand All @@ -38,19 +39,57 @@
skyAtmosphere : globeVisible ? undefined : false
});

var terrainProvider;
if (terrainEnabled) {
terrainProvider = new Cesium.CesiumTerrainProvider({
url : '//assets.agi.com/stk-terrain/world',
requestWaterMask : true,
requestVertexNormals : true
});
} else {
terrainProvider = new Cesium.EllipsoidTerrainProvider();
}

viewer.terrainProvider = terrainProvider;

// Exton
//var centerLongitude = -1.31968;
//var centerLatitude = 0.698874;

// Everest
var centerLongitude = 1.517132688;
var centerLatitude = 0.4884844964;

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

var centerLongitude = -1.31968;
var centerLatitude = 0.698874;
var positions = [new Cesium.Cartographic(centerLongitude, centerLatitude)];
var promise = Cesium.sampleTerrain(terrainProvider, 11, positions);
Cesium.when(promise, function(updatedPositions) {
var height = updatedPositions[0].height;
createScene(height);
});

camera.lookAt(Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, 50), new Cesium.Cartesian3(0.0, 0.0, 30.0));
function createScene(height) {
height += 50.0;
var center = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height);
var position1 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 5.0);
var position2 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 10.0);
var position3 = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, height + 15.0);

function createModel(url) {
var origin = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, 50);
camera.lookAt(center, new Cesium.Cartesian3(25.0, 25.0, 30.0));

//createModel('../../SampleData/models/CesiumAir/Cesium_Air.glb');
createModel('../../SampleData/models/shadow_tester.gltf', center);
createBox(position3);
createBoxRTC(position2);
createSphere(position1);
}

function createModel(url, origin) {
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, 0.0, 0.0, 0.0);

var model = scene.primitives.add(Cesium.Model.fromGltf({
Expand All @@ -69,8 +108,7 @@
});
}

function createBoxRTC() {
var origin = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, 60);
function createBoxRTC(origin) {
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, 0.0, 0.0, 0.0);

var boxGeometry = Cesium.BoxGeometry.createGeometry(Cesium.BoxGeometry.fromDimensions({
Expand Down Expand Up @@ -104,14 +142,15 @@
closed : true
}),
asynchronous : false,
rtcCenter : boxGeometry.boundingSphere.center
rtcCenter : boxGeometry.boundingSphere.center,
castShadows : true,
receiveShadows : true
});

scene.primitives.add(box);
}

function createBox() {
var origin = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, 65);
function createBox(origin) {
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, 0.0, 0.0, 0.0);

var box = new Cesium.Primitive({
Expand All @@ -129,14 +168,15 @@
translucent : false,
closed : true
}),
asynchronous : false
asynchronous : false,
castShadows : true,
receiveShadows : true
});

scene.primitives.add(box);
}

function createSphere() {
var origin = Cesium.Cartesian3.fromRadians(centerLongitude, centerLatitude, 55);
function createSphere(origin) {
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, 0.0, 0.0, 0.0);

var sphere = new Cesium.Primitive({
Expand All @@ -154,18 +194,14 @@
translucent : true,
closed : true
}),
asynchronous : false
asynchronous : false,
castShadows : true,
receiveShadows : true
});

scene.primitives.add(sphere);
}

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

function setCascades(cascades) {
return function() {
window.alert('Set cascades: ' + i);
Expand Down
18 changes: 18 additions & 0 deletions Source/Scene/Globe.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,22 @@ define([
*/
this.depthTestAgainstTerrain = false;

/**
* Determines whether the globe will cast shadows when shadow mapping is enabled.
*
* @type {Boolean}
* @default true
*/
this.castShadows = true;

/**
* Determines whether the globe will receive shadows when shadow mapping is enabled.
*
* @type {Boolean}
* @default true
*/
this.receiveShadows = true;

this._oceanNormalMap = undefined;
this._zoomedOutOceanSpecularIntensity = 0.5;
}
Expand Down Expand Up @@ -464,6 +480,8 @@ define([
tileProvider.hasWaterMask = hasWaterMask;
tileProvider.oceanNormalMap = this._oceanNormalMap;
tileProvider.enableLighting = this.enableLighting;
tileProvider.castShadows = this.castShadows;
tileProvider.receiveShadows = this.receiveShadows;

surface.update(frameState);
}
Expand Down
83 changes: 76 additions & 7 deletions Source/Scene/GlobeSurfaceShaderSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ define([
'../Core/destroyObject',
'../Core/TerrainQuantization',
'../Renderer/ShaderProgram',
'../Scene/SceneMode'
'../Scene/SceneMode',
'../Scene/ShadowMapShader'
], function(
defined,
destroyObject,
TerrainQuantization,
ShaderProgram,
SceneMode) {
SceneMode,
ShadowMapShader) {
'use strict';

function GlobeSurfaceShader(numberOfDayTextures, flags, shaderProgram) {
Expand All @@ -31,6 +33,7 @@ define([

this._shadersByTexturesFlags = [];
this._pickShaderPrograms = [];
this._shadowCastPrograms = [];
}

function getPositionMode(sceneMode) {
Expand Down Expand Up @@ -65,7 +68,7 @@ define([
return useWebMercatorProjection ? get2DYPositionFractionMercatorProjection : get2DYPositionFractionGeographicProjection;
}

GlobeSurfaceShaderSet.prototype.getShaderProgram = function(frameState, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves, enableLighting, hasVertexNormals, useWebMercatorProjection, enableFog) {
GlobeSurfaceShaderSet.prototype.getShaderProgram = function(frameState, surfaceTile, numberOfDayTextures, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha, showReflectiveOcean, showOceanWaves, enableLighting, hasVertexNormals, useWebMercatorProjection, enableFog, receiveShadows) {
var quantization = 0;
var quantizationDefine = '';

Expand All @@ -90,7 +93,8 @@ define([
(hasVertexNormals << 11) |
(useWebMercatorProjection << 12) |
(enableFog << 13) |
(quantization << 14);
(quantization << 14) |
(receiveShadows << 15);

var surfaceShader = surfaceTile.surfaceShader;
if (defined(surfaceShader) &&
Expand Down Expand Up @@ -187,6 +191,11 @@ define([
vs.sources.push(getPositionMode(sceneMode));
vs.sources.push(get2DYPositionFraction(useWebMercatorProjection));

if (receiveShadows) {
vs.sources[1] = ShadowMapShader.createReceiveShadowsVertexShader(vs.sources[1]);
fs.sources[0] = ShadowMapShader.createReceiveShadowsFragmentShader(fs.sources[0], frameState.context);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was the easiest way for me to modify the Globe shader to support shadows. I am open to other ideas.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine for now - standard for the globe shader.

Longer-term (which could be soon), we need a better low-level material system (that still allows dynamically recompiling the shader for when the shadow flags are enabled/disabled). Perhaps we will beef up ShaderProgram to know about shadows, and add flags to commands for if they cast/receive shadows.


var shader = ShaderProgram.fromCache({
context : frameState.context,
vertexShaderSource : vs,
Expand Down Expand Up @@ -240,7 +249,51 @@ define([
return pickShader;
};

GlobeSurfaceShaderSet.prototype.getShadowCastProgram = function(frameState, surfaceTile, useWebMercatorProjection, castShadows) {
if (!castShadows) {
return undefined;
}

var quantization = 0;
var quantizationDefine = '';

var terrainEncoding = surfaceTile.pickTerrain.mesh.encoding;
var quantizationMode = terrainEncoding.quantization;
if (quantizationMode === TerrainQuantization.BITS12) {
quantization = 1;
quantizationDefine = 'QUANTIZATION_BITS12';
}

var sceneMode = frameState.mode;
var flags = sceneMode | (useWebMercatorProjection << 2) | (quantization << 3);
var shadowCastShader = this._shadowCastPrograms[flags];

if (!defined(shadowCastShader)) {
var vs = this.baseVertexShaderSource.clone();
var fs = this.baseFragmentShaderSource.clone();

vs.defines.push(quantizationDefine);
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.context, true);

shadowCastShader = this._shadowCastPrograms[flags] = ShaderProgram.fromCache({
context : frameState.context,
vertexShaderSource : vs,
fragmentShaderSource : fs,
attributeLocations : terrainEncoding.getAttributeLocations()
});
}

return shadowCastShader;
};

GlobeSurfaceShaderSet.prototype.destroy = function() {
var flags;
var shader;

var shadersByTexturesFlags = this._shadersByTexturesFlags;
for (var textureCount in shadersByTexturesFlags) {
if (shadersByTexturesFlags.hasOwnProperty(textureCount)) {
Expand All @@ -249,9 +302,9 @@ define([
continue;
}

for (var flags in shadersByFlags) {
for (flags in shadersByFlags) {
if (shadersByFlags.hasOwnProperty(flags)) {
var shader = shadersByFlags[flags];
shader = shadersByFlags[flags];
if (defined(shader)) {
shader.shaderProgram.destroy();
}
Expand All @@ -260,8 +313,24 @@ define([
}
}

var pickShaderPrograms = this._pickShaderPrograms;
for (flags in pickShaderPrograms) {
if (pickShaderPrograms.hasOwnProperty(flags)) {
shader = pickShaderPrograms[flags];
shader.destroy();
}
}

var shadowCastPrograms = this._shadowCastPrograms;
for (flags in shadowCastPrograms) {
if (shadowCastPrograms.hasOwnProperty(flags)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Also longer-term, the pick shader can likely be the same as the shadow cast shader since all it wants to do is render depth for opaque geometry.

shader = shadowCastPrograms[flags];
shader.destroy();
}
}

return destroyObject(this);
};

return GlobeSurfaceShaderSet;
});
});
Loading