Skip to content

Commit

Permalink
Merge pull request #12160 from CesiumGS/hdr-tonemap-options
Browse files Browse the repository at this point in the history
Allow changing the HDR tonemap
  • Loading branch information
ggetz authored Sep 3, 2024
2 parents 1669679 + a108a5a commit c191e08
Show file tree
Hide file tree
Showing 19 changed files with 602 additions and 218 deletions.
155 changes: 136 additions & 19 deletions Apps/Sandcastle/gallery/High Dynamic Range.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,120 @@
>
<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;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar"></div>
<div id="toolbar">
<div id="hdr-toggle"></div>
<span>Tonemap</span>
<span id="tonemap-select"></span>
<br />
<span>Exposure</span>
<input
type="range"
min="0.1"
max="10.0"
step="0.1"
data-bind="value: exposure, valueUpdate: 'input'"
/>
<input type="text" size="5" data-bind="value: exposure" />
</div>
<script id="cesium_sandcastle_script">
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
terrain: Cesium.Terrain.fromWorldTerrain(),
shadows: true,
timeline: false,
animation: false,
geocoder: false,
sceneModePicker: false,
baseLayerPicker: false,
});

if (!viewer.scene.highDynamicRangeSupported) {
window.alert("This browser does not support high dynamic range.");
}

viewer.scene.camera.setView({
destination: new Cesium.Cartesian3(
-1915097.7863741855,
-4783356.851539908,
3748887.43462683
),
orientation: new Cesium.HeadingPitchRoll(
6.166004548388564,
-0.043242401760068994,
0.002179961955988574
),
endTransform: Cesium.Matrix4.IDENTITY,
});

viewer.scene.highDynamicRange = true;

Sandcastle.addToggleButton("HDR", true, function (checked) {
viewer.scene.highDynamicRange = checked;
});
Sandcastle.addToggleButton(
"HDR",
true,
function (checked) {
viewer.scene.highDynamicRange = checked;
},
"hdr-toggle"
);

const toneMapOptions = [
{
text: "PBR Neutral",
onselect: function () {
viewer.scene.postProcessStages.tonemapper =
Cesium.Tonemapper.PBR_NEUTRAL;
},
},
{
text: "Aces",
onselect: function () {
viewer.scene.postProcessStages.tonemapper =
Cesium.Tonemapper.ACES;
},
},
{
text: "Reinhard",
onselect: function () {
viewer.scene.postProcessStages.tonemapper =
Cesium.Tonemapper.REINHARD;
},
},
{
text: "Modified_Reinhard",
onselect: function () {
viewer.scene.postProcessStages.tonemapper =
Cesium.Tonemapper.MODIFIED_REINHARD;
},
},
{
text: "Filmic",
onselect: function () {
viewer.scene.postProcessStages.tonemapper =
Cesium.Tonemapper.FILMIC;
},
},
];
Sandcastle.addDefaultToolbarMenu(toneMapOptions, "tonemap-select");

const viewModel = {
exposure: 1,
};
// 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.
const toolbar = document.getElementById("toolbar");
Cesium.knockout.applyBindings(viewModel, toolbar);

Cesium.knockout
.getObservable(viewModel, "exposure")
.subscribe(function (newValue) {
viewer.scene.postProcessStages.exposure = Number.parseFloat(
newValue
);
});

const url =
"../../SampleData/models/DracoCompressed/CesiumMilkTruck.gltf";
Expand All @@ -86,7 +164,46 @@
uri: url,
scale: scale,
},
}); //Sandcastle_End
});

// set up canyon view
viewer.scene.camera.setView({
destination: new Cesium.Cartesian3(
-1915097.7863741855,
-4783356.851539908,
3748887.43462683
),
orientation: new Cesium.HeadingPitchRoll(
6.166004548388564,
-0.043242401760068994,
0.002179961955988574
),
endTransform: Cesium.Matrix4.IDENTITY,
});
// set time so the sun is overhead
viewer.clock.currentTime = new Cesium.JulianDate(2460550, 21637);

viewer.scene.debugShowFramesPerSecond = true;
// override the default home location to return to the start
viewer.homeButton.viewModel.command.beforeExecute.addEventListener(
(e) => {
e.cancel = true;
viewer.scene.camera.setView({
destination: new Cesium.Cartesian3(
-1915097.7863741855,
-4783356.851539908,
3748887.43462683
),
orientation: new Cesium.HeadingPitchRoll(
6.166004548388564,
-0.043242401760068994,
0.002179961955988574
),
endTransform: Cesium.Matrix4.IDENTITY,
});
}
);
//Sandcastle_End
};
if (typeof Cesium !== "undefined") {
window.startupCalled = true;
Expand Down
8 changes: 5 additions & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
- Enable MSAA by default with 4 samples. To turn MSAA off set `scene.msaaSamples = 1` [#12158](https://github.com/CesiumGS/cesium/pull/12158)
- Made the `time` parameter optional for `Property`, using `JulianDate.now()` as default. [#12099](https://github.com/CesiumGS/cesium/pull/12099)
- Exposes `ScreenSpaceCameraController.zoomFactor` to allow adjusting the zoom factor (speed). [#9145](https://github.com/CesiumGS/cesium/pull/9145)
- Expose the `tonemapper` property of `PostProcessStageCollection` to allow changing the tonemap used when HDR is turned on. This defaults to the [PBR Neutral Tonemap from Khronos](https://github.com/KhronosGroup/ToneMapping/tree/main/PBR_Neutral) [#12160](https://github.com/CesiumGS/cesium/pull/12160)
- The enum `Tonemapper` contains the list of valid tonemap options to use with the `tonemapper` setting
- Expose the `exposure` property of `PostProcessStageCollection` to allow changing the exposure used for the current HDR tonemap [#12160](https://github.com/CesiumGS/cesium/pull/12160)
- Added `WaterMask` globe material, which visualizes areas of water or land based on the terrain's water mask. [#12149](https://github.com/CesiumGS/cesium/pull/12149)

##### Fixes :wrench:
Expand All @@ -23,13 +26,12 @@

##### Breaking Changes :mega:

- Switched the default (non-HDR) tonemapping for models, atmosphere, and globe from ACES to [PBR Neutral Tonemap from Khronos](https://github.com/KhronosGroup/ToneMapping/tree/main/PBR_Neutral). The widens the gamut of possible colors, and provides more consistent color appearances across renderers. [#12160](https://github.com/CesiumGS/cesium/pull/12160)
- Switched the default tonemapper when HDR is turned _on_ from ACES to [PBR Neutral Tonemap from Khronos](https://github.com/KhronosGroup/ToneMapping/tree/main/PBR_Neutral). To preserve the previous behavior set `viewer.scene.postProcessStages.tonemapper = Cesium.Tonemapper.ACES;` [#12160](https://github.com/CesiumGS/cesium/pull/12160)
- `SceneTransforms.wgs84ToWindowCoordinates` has been removed. Use `SceneTransforms.worldToWindowCoordinates` instead.
- `SceneTransforms.wgs84ToDrawingBufferCoordinates` has been removed. Use `SceneTransforms.worldToDrawingBufferCoordinates` instead.

- Removed `jitter` option from `VoxelPrimitive.js`, `VoxelRenderResources.js`, and related test code in `VoxelPrimitiveSpec.js`. [#11913](https://github.com/CesiumGS/cesium/issues/11913)

- Custom specular environment maps in `ImageBasedLighting` now require either a WebGL2 context or a WebGL1 context that supports the [`EXT_shader_texture_lod` extension](https://registry.khronos.org/webgl/extensions/EXT_shader_texture_lod/).

- `ScreenSpaceCameraController._zoomFactor` replaced with public zoomFactor attribute.

### 1.120 - 2024-08-01
Expand Down
7 changes: 7 additions & 0 deletions packages/engine/Source/Renderer/ShaderProgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,13 @@ ShaderProgram.prototype._setUniforms = function (
len = manualUniforms.length;
for (i = 0; i < len; ++i) {
const mu = manualUniforms[i];

//>>includeStart('debug', pragmas.debug);
if (!defined(uniformMap[mu.name])) {
throw new DeveloperError(`Unknown uniform: ${mu.name}`);
}
//>>includeEnd('debug');

mu.value = uniformMap[mu.name]();
}
}
Expand Down
60 changes: 45 additions & 15 deletions packages/engine/Source/Scene/PostProcessStageCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import TextureWrap from "../Renderer/TextureWrap.js";
import PassThrough from "../Shaders/PostProcessStages/PassThrough.js";
import PostProcessStageLibrary from "./PostProcessStageLibrary.js";
import PostProcessStageTextureCache from "./PostProcessStageTextureCache.js";
import Tonemapper from "./Tonemapper.js";
import Tonemapper, { validateTonemapper } from "./Tonemapper.js";

const stackScratch = [];

Expand Down Expand Up @@ -41,11 +41,12 @@ function PostProcessStageCollection() {
// Some shaders, such as the atmosphere and ground atmosphere, output values slightly over 1.0.
this._autoExposureEnabled = false;
this._autoExposure = PostProcessStageLibrary.createAutoExposureStage();
this._exposure = 1.0;
this._tonemapping = undefined;
this._tonemapper = undefined;

// set tonemapper and tonemapping
this.tonemapper = Tonemapper.ACES;
// set tonemapper and tonemapping using the setter
this.tonemapper = Tonemapper.PBR_NEUTRAL;

const tonemapping = this._tonemapping;

Expand Down Expand Up @@ -313,11 +314,14 @@ Object.defineProperties(PostProcessStageCollection.prototype, {
},

/**
* Gets and sets the tonemapping algorithm used when rendering with high dynamic range.
* Specifies the tonemapping algorithm used when rendering with high dynamic range.
* {@link https://sandcastle.cesium.com/?src=High%20Dynamic%20Range.html|Sandcastle Demo}
*
* @example viewer.scene.postProcessStages.tonemapper = Cesium.Tonemapper.ACES;
*
* @default Tonemapper.PBR_NEUTRAL
* @memberof PostProcessStageCollection.prototype
* @type {Tonemapper}
* @private
*/
tonemapper: {
get: function () {
Expand All @@ -328,7 +332,7 @@ Object.defineProperties(PostProcessStageCollection.prototype, {
return;
}
//>>includeStart('debug', pragmas.debug);
if (!Tonemapper.validate(value)) {
if (!validateTonemapper(value)) {
throw new DeveloperError("tonemapper was set to an invalid value.");
}
//>>includeEnd('debug');
Expand All @@ -339,49 +343,75 @@ Object.defineProperties(PostProcessStageCollection.prototype, {
}

const useAutoExposure = this._autoExposureEnabled;
let tonemapper;
let tonemapping;

switch (value) {
case Tonemapper.REINHARD:
tonemapper = PostProcessStageLibrary.createReinhardTonemappingStage(
tonemapping = PostProcessStageLibrary.createReinhardTonemappingStage(
useAutoExposure
);
break;
case Tonemapper.MODIFIED_REINHARD:
tonemapper = PostProcessStageLibrary.createModifiedReinhardTonemappingStage(
tonemapping = PostProcessStageLibrary.createModifiedReinhardTonemappingStage(
useAutoExposure
);
break;
case Tonemapper.FILMIC:
tonemapper = PostProcessStageLibrary.createFilmicTonemappingStage(
tonemapping = PostProcessStageLibrary.createFilmicTonemappingStage(
useAutoExposure
);
break;
case Tonemapper.PBR_NEUTRAL:
tonemapping = PostProcessStageLibrary.createPbrNeutralTonemappingStage(
useAutoExposure
);
break;
default:
tonemapper = PostProcessStageLibrary.createAcesTonemappingStage(
tonemapping = PostProcessStageLibrary.createAcesTonemappingStage(
useAutoExposure
);
break;
}

if (useAutoExposure) {
const autoexposure = this._autoExposure;
tonemapper.uniforms.autoExposure = function () {
tonemapping.uniforms.autoExposure = function () {
return autoexposure.outputTexture;
};
} else {
tonemapping.uniforms.exposure = this._exposure;
}

this._tonemapper = value;
this._tonemapping = tonemapper;
this._tonemapping = tonemapping;

if (defined(this._stageNames)) {
this._stageNames[tonemapper.name] = tonemapper;
tonemapper._textureCache = this._textureCache;
this._stageNames[tonemapping.name] = tonemapping;
tonemapping._textureCache = this._textureCache;
}

this._textureCacheDirty = true;
},
},

/**
* Control the exposure when HDR is on. Less than 1.0 makes the tonemapping darker while greater than 1.0 makes it brighter.
*
* @example viewer.scene.postProcessStages.exposure = 1.0;
*
* @default 1.0
* @memberof PostProcessStageCollection.prototype
* @type {number}
*/
exposure: {
get: function () {
return this._exposure;
},
set: function (value) {
this._tonemapping.uniforms.exposure = value;
this._exposure = value;
},
},
});

function removeStages(collection) {
Expand Down
Loading

0 comments on commit c191e08

Please sign in to comment.