Skip to content

Commit

Permalink
allow changing the hdr tonemap, add pbr neutral tonemap
Browse files Browse the repository at this point in the history
  • Loading branch information
jjspace committed Aug 27, 2024
1 parent 088c4a2 commit f75cc7a
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 145 deletions.
99 changes: 84 additions & 15 deletions Apps/Sandcastle/gallery/High Dynamic Range.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,62 @@
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;
});

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);

const url =
"../../SampleData/models/DracoCompressed/CesiumMilkTruck.gltf";
const position = Cesium.Cartesian3.fromRadians(
Expand All @@ -86,7 +116,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
34 changes: 19 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 @@ -44,8 +44,8 @@ function PostProcessStageCollection() {
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 +313,10 @@ 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. Defaults to `Tonemapper.PBR_NEUTRAL`
*
* @memberof PostProcessStageCollection.prototype
* @type {Tonemapper}
* @private
*/
tonemapper: {
get: function () {
Expand All @@ -328,7 +327,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,44 +338,49 @@ 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;
};
}

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;
Expand Down
21 changes: 21 additions & 0 deletions packages/engine/Source/Scene/PostProcessStageLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import DepthOfField from "../Shaders/PostProcessStages/DepthOfField.js";
import DepthView from "../Shaders/PostProcessStages/DepthView.js";
import EdgeDetection from "../Shaders/PostProcessStages/EdgeDetection.js";
import FilmicTonemapping from "../Shaders/PostProcessStages/FilmicTonemapping.js";
import PBRNeutralTonemapping from "../Shaders/PostProcessStages/PBRNeutralTonemapping.js";
import FXAA from "../Shaders/PostProcessStages/FXAA.js";
import GaussianBlur1D from "../Shaders/PostProcessStages/GaussianBlur1D.js";
import LensFlare from "../Shaders/PostProcessStages/LensFlare.js";
Expand Down Expand Up @@ -693,6 +694,26 @@ PostProcessStageLibrary.createFilmicTonemappingStage = function (
});
};

/**
* Creates a post-process stage that applies filmic tonemapping operator.
* @param {boolean} useAutoExposure Whether or not to use auto-exposure.
* @return {PostProcessStage} A post-process stage that applies filmic tonemapping operator.
* @private
*/
PostProcessStageLibrary.createPBRNeutralTonemappingStage = function (
useAutoExposure
) {
let fs = useAutoExposure ? "#define AUTO_EXPOSURE\n" : "";
fs += PBRNeutralTonemapping;
return new PostProcessStage({
name: "czm_pbr_neutral",
fragmentShader: fs,
uniforms: {
autoExposure: undefined,
},
});
};

/**
* Creates a post-process stage that applies Reinhard tonemapping operator.
* @param {boolean} useAutoExposure Whether or not to use auto-exposure.
Expand Down
50 changes: 31 additions & 19 deletions packages/engine/Source/Scene/Tonemapper.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,64 @@
/**
* A tonemapping algorithm when rendering with high dynamic range.
*
* @enum {number}
* @private
* @enum {string}
*/
const Tonemapper = {
/**
* Use the Reinhard tonemapping operator.
*
* @type {number}
* @type {string}
* @constant
*/
REINHARD: 0,
REINHARD: "REINHARD",

/**
* Use the modified Reinhard tonemapping operator.
*
* @type {number}
* @type {string}
* @constant
*/
MODIFIED_REINHARD: 1,
MODIFIED_REINHARD: "MODIFIED_REINHARD",

/**
* Use the Filmic tonemapping operator.
*
* @type {number}
* @type {string}
* @constant
*/
FILMIC: 2,
FILMIC: "FILMIC",

/**
* Use the ACES tonemapping operator.
*
* @type {number}
* @type {string}
* @constant
*/
ACES: 3,
ACES: "ACES",

/**
* @private
* Use the PBRNeutral tonemapping operator.
*
* @type {string}
* @constant
*/
validate: function (tonemapper) {
return (
tonemapper === Tonemapper.REINHARD ||
tonemapper === Tonemapper.MODIFIED_REINHARD ||
tonemapper === Tonemapper.FILMIC ||
tonemapper === Tonemapper.ACES
);
},
PBR_NEUTRAL: "PBR_NEUTRAL",
};

/**
* Validate whether the provided value is a known Tonemapper type
* @private
*
* @param {string} tonemapper
*/
export function validateToneMapper(tonemapper) {
return (
tonemapper === Tonemapper.REINHARD ||
tonemapper === Tonemapper.MODIFIED_REINHARD ||
tonemapper === Tonemapper.FILMIC ||
tonemapper === Tonemapper.ACES ||
tonemapper === Tonemapper.PBR_NEUTRAL
);
}

export default Object.freeze(Tonemapper);
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// KhronosGroup https://github.com/KhronosGroup/ToneMapping/tree/main/PBR_Neutral

// Input color is non-negative and resides in the Linear Rec. 709 color space.
// Output color is also Linear Rec. 709, but in the [0, 1] range.

vec3 PBRNeutralToneMapping( vec3 color ) {
const float startCompression = 0.8 - 0.04;
const float desaturation = 0.15;

float x = min(color.r, min(color.g, color.b));
float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
color -= offset;

float peak = max(color.r, max(color.g, color.b));
if (peak < startCompression) return color;

const float d = 1.0 - startCompression;
float newPeak = 1.0 - d * d / (peak + d - startCompression);
color *= newPeak / peak;

float g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);
return mix(color, newPeak * vec3(1, 1, 1), g);
}

uniform sampler2D colorTexture;

in vec2 v_textureCoordinates;

#ifdef AUTO_EXPOSURE
uniform sampler2D autoExposure;
#endif

void main()
{
vec4 fragmentColor = texture(colorTexture, v_textureCoordinates);
vec3 color = fragmentColor.rgb;

#ifdef AUTO_EXPOSURE
color /= texture(autoExposure, vec2(0.5)).r;
#endif
color = PBRNeutralToneMapping(color);
color = czm_inverseGamma(color);

out_FragColor = vec4(color, fragmentColor.a);
}
Loading

0 comments on commit f75cc7a

Please sign in to comment.