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

Geometric-error based point cloud attenuation and eye dome lighting #6069

Merged
merged 17 commits into from
Jan 30, 2018
Merged
Show file tree
Hide file tree
Changes from 14 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
275 changes: 275 additions & 0 deletions Apps/Sandcastle/gallery/3D Tiles Point Cloud Shading.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
<!DOCTYPE html>
Copy link
Contributor

Choose a reason for hiding this comment

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

This demo might look better with terrain on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I gave it a quick try, but it looks like Chappes church is below terrain, and the terrain also doesn't perfectly align with the Mt. St. Helens points, it punches through in some views.

<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="Point Cloud Attenuation and Eye Dome Lighting example.">
<meta name="cesium-sandcastle-labels" content="3D Tiles">
<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">
if(typeof require === "function") {
require.config({
baseUrl : '../../../Source',
waitSeconds : 120
});
}
</script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
#toolbar {
background: rgba(42, 42, 42, 0.8);
padding: 4px;
border-radius: 4px;
}
#toolbar input {
vertical-align: middle;
padding-top: 2px;
padding-bottom: 2px;
}
#toolbar .header {
font-weight: bold;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<select data-bind="options: exampleTypes, value: currentExampleType"></select>
<table><tbody>
<tr>
<td>Maximum Screen Space Error</td>
<td>
<input type="range" min="0.0" max="64.0" step="0.1" data-bind="value: maximumScreenSpaceError, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: maximumScreenSpaceError">
</td>
</tr>
<tr><td class="header">Attenuation</td></tr>
<tr>
<td>Geometric Error Scale</td>
<td>
<input type="range" min="0.0" max="2.0" step="0.1" data-bind="value: geometricErrorScale, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: geometricErrorScale">
</td>
</tr>
<tr>
<td>Maximum Attenuation</td>
<td>
<input type="range" min="0.0" max="32.0" step="1.0" data-bind="value: maximumAttenuation, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: maximumAttenuation">
</td>
</tr>
<tr>
<td>Base Resolution</td>
<td>
<input type="range" min="0.0" max="10.0" step="0.01" data-bind="value: baseResolution, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: baseResolution">
</td>
</tr>
<tr><td class="header">Eye Dome Lighting</td></tr>
<tr>
<td>Eye Dome Lighting Strength</td>
<td>
<input type="range" min="0.0" max="10.0" step="0.1" data-bind="value: eyeDomeLightingStrength, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: eyeDomeLightingStrength">
</td>
</tr>
<tr>
<td>Eye Dome Lighting Radius</td>
<td>
<input type="range" min="0.0" max="10.0" step="0.1" data-bind="value: eyeDomeLightingRadius, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: eyeDomeLightingRadius">
</td>
</tr>
</tbody></table>
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
'use strict';
//Sandcastle_Begin
var viewer = new Cesium.Viewer('cesiumContainer');

var scene = viewer.scene;
var viewModelTileset;

function reset() {
viewer.entities.removeAll();
viewer.scene.primitives.removeAll();
viewModelTileset = undefined;
}

// The viewModel tracks the state of our mini application.
var pointClouds = ['St. Helens', 'Church'];
var viewModel = {
exampleTypes : pointClouds,
currentExampleType : pointClouds[0],
maximumScreenSpaceError : 8.0,
geometricErrorScale : 1.0,
maximumAttenuation : 0, // Equivalent to undefined
baseResolution : 0, // Equivalent to undefined
eyeDomeLightingStrength : 1.0,
eyeDomeLightingRadius : 1.0
};

function tilesetToViewModel(tileset) {
viewModelTileset = tileset;

var pointCloudShading = tileset.pointCloudShading;
viewModel.maximumScreenSpaceError = tileset.maximumScreenSpaceError;
viewModel.geometricErrorScale = pointCloudShading.geometricErrorScale;
viewModel.maximumAttenuation = pointCloudShading.maximumAttenuation ? pointCloudShading.maximumAttenuation : 0;
viewModel.baseResolution = pointCloudShading.baseResolution ? pointCloudShading.baseResolution : 0;
viewModel.eyeDomeLightingStrength = pointCloudShading.eyeDomeLightingStrength;
viewModel.eyeDomeLightingRadius = pointCloudShading.eyeDomeLightingRadius;
}

function loadStHelens() {
// Set the initial camera view to look at Mt. St. Helens
var initialPosition = Cesium.Cartesian3.fromRadians(-2.1344873183780484, 0.8071380277370774, 5743.394497982162);
var initialOrientation = new Cesium.HeadingPitchRoll.fromDegrees(112.99596671210358, -21.34390550872461, 0.0716951918898415);
viewer.scene.camera.setView({
destination: initialPosition,
orientation: initialOrientation,
endTransform: Cesium.Matrix4.IDENTITY
});

// Mt. St. Helens 3D Tileset generated from LAS provided by https://www.liblas.org/samples/
// This tileset uses replacement refinement and has geometric error approximately equal to
// the average interpoint distance in each tile.
Cesium.CesiumIon.create3DTileset(3742, { accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxOGZjMjlhZC03MDE1LTQ3ZTAtODEyNy05YTU3M2MwYzQ0YmEiLCJpZCI6NDQsImFzc2V0cyI6WzM3NDJdLCJpYXQiOjE1MTcyNDI3NDJ9.TJAJctFXC1UyFMpxkA3cyKVAmnh72cLtfY1yKbaQsyk' })
.then(function(tileset) {
viewer.scene.primitives.add(tileset);

tileset.maximumScreenSpaceError = 8.0;
tileset.pointCloudShading.maximumAttenuation = undefined; // Will be based on maximumScreenSpaceError instead
tileset.pointCloudShading.baseResolution = undefined;
tileset.pointCloudShading.geometricErrorScale = 1.0;
tileset.pointCloudShading.attenuation = true;
tileset.pointCloudShading.eyeDomeLighting = true;

tilesetToViewModel(tileset);
})
.otherwise(function(error) {
console.log(error);
});
}

function loadChurch() {
// Point Cloud by Prof. Peter Allen, Columbia University Robotics Lab. Scanning by Alejandro Troccoli and Matei Ciocarlie.
// This tileset uses additive refinement and has geometric error based on the bounding box size for each tile.
Cesium.CesiumIon.create3DTileset(1460, { accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyMzk2YzJiOS1jZGFmLTRlZmYtYmQ4MS00NTA3NjEwMzViZTkiLCJpZCI6NDQsImFzc2V0cyI6WzE0NjBdLCJpYXQiOjE0OTkyNjQ3NTV9.oWjvN52CRQ-dk3xtvD4e8ZnOHZhoWSpJLlw115mbQJM' })
.then(function(tileset) {
viewer.scene.primitives.add(tileset);

tileset.maximumScreenSpaceError = 1024.0; // For better performance, due to how this tileset treats geometric error.
tileset.pointCloudShading.maximumAttenuation = 8.0; // Don't allow points larger than 8 pixels.
tileset.pointCloudShading.baseResolution = 0.05; // Assume an original capture resolution of 5 centimeters between neighboring points.
tileset.pointCloudShading.geometricErrorScale = 1.0; // Applies to both geometric error and the base resolution.
tileset.pointCloudShading.attenuation = true;
tileset.pointCloudShading.eyeDomeLighting = true;

tilesetToViewModel(tileset);
viewer.zoomTo(tileset);
})
.otherwise(function(error) {
console.log(error);
});
}

function checkZero(newValue) {
var newValueFloat = parseFloat(newValue);
return (newValueFloat === 0.0) ? undefined : newValueFloat;
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to be sure, does the code still work without this function?

For example, if a developer sets maximumAttenuation or baseResolution to 0.0 will everything work correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All computations will be done with 0.0 instead of with the "defaults." For maximumAttenuation this causes what looks like point size of 1 across the whole tileset, and for baseResolution this forces all tiles with geometricError === 0 to draw with point size of 1.

}

loadStHelens();

// Convert the viewModel members into knockout observables.
Cesium.knockout.track(viewModel);

// Bind the viewModel to the DOM elements of the UI that call for it.
var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);

Cesium.knockout.getObservable(viewModel, 'currentExampleType').subscribe(function(newValue) {
reset();
if (newValue === pointClouds[0]) {
loadStHelens();
} else if (newValue === pointClouds[1]) {
loadChurch();
}
});

Cesium.knockout.getObservable(viewModel, 'maximumScreenSpaceError').subscribe(
function(newValue) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.maximumScreenSpaceError = parseFloat(newValue);
}
}
);

Cesium.knockout.getObservable(viewModel, 'geometricErrorScale').subscribe(
function(newValue) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.pointCloudShading.geometricErrorScale = parseFloat(newValue);
}
}
);

Cesium.knockout.getObservable(viewModel, 'maximumAttenuation').subscribe(
function(newValue) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.pointCloudShading.maximumAttenuation = checkZero(newValue);
}
}
);

Cesium.knockout.getObservable(viewModel, 'baseResolution').subscribe(
function(newValue) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.pointCloudShading.baseResolution = checkZero(newValue);
}
}
);

Cesium.knockout.getObservable(viewModel, 'eyeDomeLightingStrength').subscribe(
function(newValue) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.pointCloudShading.eyeDomeLightingStrength = parseFloat(newValue);
}
}
);

Cesium.knockout.getObservable(viewModel, 'eyeDomeLightingRadius').subscribe(
function(newValue) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.pointCloudShading.eyeDomeLightingRadius = parseFloat(newValue);
}
}
);

Sandcastle.addToggleButton('Enable Attenuation', true, function(checked) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.pointCloudShading.attenuation = checked;
}
});

Sandcastle.addToggleButton('Enable Eye Dome Lighting', true, function(checked) {
if (Cesium.defined(viewModelTileset)) {
viewModelTileset.pointCloudShading.eyeDomeLighting = checked;
}
});

//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Change Log
* Fixed camera movement and look functions for 2D mode [#5884](https://github.com/AnalyticalGraphicsInc/cesium/issues/5884)
* Fixed discrepancy between default value used and commented value for default value for halfAxes of OrientedBoundingBox. [#6147](https://github.com/AnalyticalGraphicsInc/cesium/pull/6147)
* Added `Cartographic.toCartesian` to convert from Cartographic to Cartesian3. [#6163](https://github.com/AnalyticalGraphicsInc/cesium/pull/6163)
* Added geometric-error-based point cloud attenuation and eye dome lighting for point clouds using replacement refinement. [#6069](https://github.com/AnalyticalGraphicsInc/cesium/pull/6069)
* Added `BoundingSphere.volume` for computing the volume of a `BoundingSphere`. [#6069](https://github.com/AnalyticalGraphicsInc/cesium/pull/6069)

### 1.41 - 2018-01-02

Expand Down
12 changes: 12 additions & 0 deletions Source/Core/BoundingSphere.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
define([
'./Cartesian3',
'./Cartographic',
'./Math',
'./Check',
'./defaultValue',
'./defined',
Expand All @@ -14,6 +15,7 @@ define([
], function(
Cartesian3,
Cartographic,
CesiumMath,
Check,
defaultValue,
defined,
Expand Down Expand Up @@ -66,6 +68,7 @@ define([
var fromPointsMinBoxPt = new Cartesian3();
var fromPointsMaxBoxPt = new Cartesian3();
var fromPointsNaiveCenterScratch = new Cartesian3();
var volumeConstant = (4.0 / 3.0) * CesiumMath.PI;

/**
* Computes a tight-fitting bounding sphere enclosing a list of 3D Cartesian points.
Expand Down Expand Up @@ -1302,5 +1305,14 @@ define([
return BoundingSphere.clone(this, result);
};

/**
* Computes the radius of the BoundingSphere.
* @returns {Number} The radius of the BoundingSphere.
*/
BoundingSphere.prototype.volume = function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add this to CHANGES.md.

var radius = this.radius;
return volumeConstant * radius * radius * radius;
};

return BoundingSphere;
});
22 changes: 21 additions & 1 deletion Source/Scene/Cesium3DTileset.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ define([
'./Cesium3DTileStyleEngine',
'./ClassificationType',
'./LabelCollection',
'./PointCloudShading',
'./PointCloudEyeDomeLighting',
'./SceneMode',
'./ShadowMode',
'./TileBoundingRegion',
Expand Down Expand Up @@ -79,6 +81,8 @@ define([
Cesium3DTileStyleEngine,
ClassificationType,
LabelCollection,
PointCloudShading,
PointCloudEyeDomeLighting,
SceneMode,
ShadowMode,
TileBoundingRegion,
Expand Down Expand Up @@ -124,6 +128,7 @@ define([
* @param {Boolean} [options.debugShowRenderingStatistics=false] For debugging only. When true, draws labels to indicate the number of commands, points, triangles and features for each tile.
* @param {Boolean} [options.debugShowMemoryUsage=false] For debugging only. When true, draws labels to indicate the texture and geometry memory in megabytes used by each tile.
* @param {Boolean} [options.debugShowUrl=false] For debugging only. When true, draws labels to indicate the url of each tile.
* @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting.
*
* @exception {DeveloperError} The tileset must be 3D Tiles version 0.0 or 1.0. See {@link https://github.com/AnalyticalGraphicsInc/3d-tiles#spec-status}
*
Expand Down Expand Up @@ -334,6 +339,14 @@ define([
*/
this.colorBlendAmount = 0.5;

/**
* Options for controlling point size based on geometric error and eye dome lighting.
* @type {PointCloudShading}
*/
this.pointCloudShading = new PointCloudShading(options.pointCloudShading);

this._pointCloudEyeDomeLighting = new PointCloudEyeDomeLighting();

/**
* The event fired to indicate progress of loading new tiles. This event is fired when a new tile
* is requested, when a requested tile is finished downloading, and when a downloaded tile has been
Expand Down Expand Up @@ -1632,6 +1645,7 @@ define([
}
}
var lengthAfterUpdate = commandList.length;
var addedCommandsLength = lengthAfterUpdate - lengthBeforeUpdate;

tileset._backfaceCommands.trim();

Expand Down Expand Up @@ -1661,7 +1675,6 @@ define([
*/

var backfaceCommands = tileset._backfaceCommands.values;
var addedCommandsLength = (lengthAfterUpdate - lengthBeforeUpdate);
var backfaceCommandsLength = backfaceCommands.length;

commandList.length += backfaceCommandsLength;
Expand All @@ -1680,6 +1693,13 @@ define([
// Number of commands added by each update above
statistics.numberOfCommands = (commandList.length - numberOfInitialCommands);

// Only run EDL if simple attenuation is on
if (tileset.pointCloudShading.attenuation &&
tileset.pointCloudShading.eyeDomeLighting &&
(addedCommandsLength > 0)) {
tileset._pointCloudEyeDomeLighting.update(frameState, numberOfInitialCommands, tileset);
}

if (tileset.debugShowGeometricError || tileset.debugShowRenderingStatistics || tileset.debugShowMemoryUsage || tileset.debugShowUrl) {
if (!defined(tileset._tileDebugLabels)) {
tileset._tileDebugLabels = new LabelCollection();
Expand Down
Loading