123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- import Cartesian2 from "../Core/Cartesian2.js";
- import Check from "../Core/Check.js";
- import defined from "../Core/defined.js";
- import defaultValue from "../Core/defaultValue.js";
- import destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import OctahedralProjectedCubeMap from "./OctahedralProjectedCubeMap.js";
- /**
- * Properties for managing image-based lighting on tilesets and models.
- * Also manages the necessary resources and textures.
- * <p>
- * If specular environment maps are used, {@link ImageBasedLighting#destroy} must be called
- * when the image-based lighting is no longer needed to clean up GPU resources properly.
- * If a model or tileset creates an instance of ImageBasedLighting, it will handle this.
- * Otherwise, the application is responsible for calling destroy().
- *</p>
- *
- * @alias ImageBasedLighting
- * @constructor
- *
- * @param {Cartesian2} [options.imageBasedLightingFactor=Cartesian2(1.0, 1.0)] Scales diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox.
- * @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.
- * @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting.
- * @param {String} [options.specularEnvironmentMaps] A URL to a KTX2 file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
- */
- export default function ImageBasedLighting(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- const imageBasedLightingFactor = defined(options.imageBasedLightingFactor)
- ? Cartesian2.clone(options.imageBasedLightingFactor)
- : new Cartesian2(1.0, 1.0);
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object(
- "options.imageBasedLightingFactor",
- imageBasedLightingFactor
- );
- Check.typeOf.number.greaterThanOrEquals(
- "options.imageBasedLightingFactor.x",
- imageBasedLightingFactor.x,
- 0.0
- );
- Check.typeOf.number.lessThanOrEquals(
- "options.imageBasedLightingFactor.x",
- imageBasedLightingFactor.x,
- 1.0
- );
- Check.typeOf.number.greaterThanOrEquals(
- "options.imageBasedLightingFactor.y",
- imageBasedLightingFactor.y,
- 0.0
- );
- Check.typeOf.number.lessThanOrEquals(
- "options.imageBasedLightingFactor.y",
- imageBasedLightingFactor.y,
- 1.0
- );
- //>>includeEnd('debug');
- this._imageBasedLightingFactor = imageBasedLightingFactor;
- const luminanceAtZenith = defaultValue(options.luminanceAtZenith, 0.2);
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number("options.luminanceAtZenith", luminanceAtZenith);
- //>>includeEnd('debug');
- this._luminanceAtZenith = luminanceAtZenith;
- const sphericalHarmonicCoefficients = options.sphericalHarmonicCoefficients;
- //>>includeStart('debug', pragmas.debug);
- if (
- defined(sphericalHarmonicCoefficients) &&
- (!Array.isArray(sphericalHarmonicCoefficients) ||
- sphericalHarmonicCoefficients.length !== 9)
- ) {
- throw new DeveloperError(
- "options.sphericalHarmonicCoefficients must be an array of 9 Cartesian3 values."
- );
- }
- //>>includeEnd('debug');
- this._sphericalHarmonicCoefficients = sphericalHarmonicCoefficients;
- // The specular environment map texture is created in update();
- this._specularEnvironmentMaps = options.specularEnvironmentMaps;
- this._specularEnvironmentMapAtlas = undefined;
- this._specularEnvironmentMapAtlasDirty = true;
- this._specularEnvironmentMapLoaded = false;
- this._previousSpecularEnvironmentMapLoaded = false;
- this._useDefaultSpecularMaps = false;
- this._useDefaultSphericalHarmonics = false;
- this._shouldRegenerateShaders = false;
- // Store the previous frame number to prevent redundant update calls
- this._previousFrameNumber = undefined;
- // Keeps track of the last values for use during update logic
- this._previousImageBasedLightingFactor = Cartesian2.clone(
- imageBasedLightingFactor
- );
- this._previousLuminanceAtZenith = luminanceAtZenith;
- this._previousSphericalHarmonicCoefficients = sphericalHarmonicCoefficients;
- }
- Object.defineProperties(ImageBasedLighting.prototype, {
- /**
- * Cesium adds lighting from the earth, sky, atmosphere, and star skybox.
- * This cartesian is used to scale the final diffuse and specular lighting
- * contribution from those sources to the final color. A value of 0.0 will
- * disable those light sources.
- *
- * @memberof ImageBasedLighting.prototype
- *
- * @type {Cartesian2}
- * @default Cartesian2(1.0, 1.0)
- */
- imageBasedLightingFactor: {
- get: function () {
- return this._imageBasedLightingFactor;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("imageBasedLightingFactor", value);
- Check.typeOf.number.greaterThanOrEquals(
- "imageBasedLightingFactor.x",
- value.x,
- 0.0
- );
- Check.typeOf.number.lessThanOrEquals(
- "imageBasedLightingFactor.x",
- value.x,
- 1.0
- );
- Check.typeOf.number.greaterThanOrEquals(
- "imageBasedLightingFactor.y",
- value.y,
- 0.0
- );
- Check.typeOf.number.lessThanOrEquals(
- "imageBasedLightingFactor.y",
- value.y,
- 1.0
- );
- //>>includeEnd('debug');
- this._previousImageBasedLightingFactor = Cartesian2.clone(
- this._imageBasedLightingFactor,
- this._previousImageBasedLightingFactor
- );
- this._imageBasedLightingFactor = Cartesian2.clone(
- value,
- this._imageBasedLightingFactor
- );
- },
- },
- /**
- * The sun's luminance at the zenith in kilo candela per meter squared
- * to use for this model's procedural environment map. This is used when
- * {@link ImageBasedLighting#specularEnvironmentMaps} and {@link ImageBasedLighting#sphericalHarmonicCoefficients}
- * are not defined.
- *
- * @memberof ImageBasedLighting.prototype
- *
- * @type {Number}
- * @default 0.2
- */
- luminanceAtZenith: {
- get: function () {
- return this._luminanceAtZenith;
- },
- set: function (value) {
- this._previousLuminanceAtZenith = this._luminanceAtZenith;
- this._luminanceAtZenith = value;
- },
- },
- /**
- * The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. When <code>undefined</code>, a diffuse irradiance
- * computed from the atmosphere color is used.
- * <p>
- * There are nine <code>Cartesian3</code> coefficients.
- * The order of the coefficients is: L<sub>0,0</sub>, L<sub>1,-1</sub>, L<sub>1,0</sub>, L<sub>1,1</sub>, L<sub>2,-2</sub>, L<sub>2,-1</sub>, L<sub>2,0</sub>, L<sub>2,1</sub>, L<sub>2,2</sub>
- * </p>
- *
- * These values can be obtained by preprocessing the environment map using the <code>cmgen</code> tool of
- * {@link https://github.com/google/filament/releases|Google's Filament project}. This will also generate a KTX file that can be
- * supplied to {@link Model#specularEnvironmentMaps}.
- *
- * @memberof ImageBasedLighting.prototype
- *
- * @type {Cartesian3[]}
- * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
- * @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps}
- */
- sphericalHarmonicCoefficients: {
- get: function () {
- return this._sphericalHarmonicCoefficients;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- if (defined(value) && (!Array.isArray(value) || value.length !== 9)) {
- throw new DeveloperError(
- "sphericalHarmonicCoefficients must be an array of 9 Cartesian3 values."
- );
- }
- //>>includeEnd('debug');
- this._previousSphericalHarmonicCoefficients = this._sphericalHarmonicCoefficients;
- this._sphericalHarmonicCoefficients = value;
- },
- },
- /**
- * A URL to a KTX2 file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
- *
- * @memberof ImageBasedLighting.prototype
- * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
- * @type {String}
- * @see ImageBasedLighting#sphericalHarmonicCoefficients
- */
- specularEnvironmentMaps: {
- get: function () {
- return this._specularEnvironmentMaps;
- },
- set: function (value) {
- if (value !== this._specularEnvironmentMaps) {
- this._specularEnvironmentMapAtlasDirty =
- this._specularEnvironmentMapAtlasDirty ||
- value !== this._specularEnvironmentMaps;
- this._specularEnvironmentMapLoaded = false;
- }
- this._specularEnvironmentMaps = value;
- },
- },
- /**
- * Whether or not image-based lighting is enabled.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {Boolean}
- *
- * @private
- */
- enabled: {
- get: function () {
- return (
- this._imageBasedLightingFactor.x > 0.0 ||
- this._imageBasedLightingFactor.y > 0.0
- );
- },
- },
- /**
- * Whether or not the models that use this lighting should regenerate their shaders,
- * based on the properties and resources have changed.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {Boolean}
- *
- * @private
- */
- shouldRegenerateShaders: {
- get: function () {
- return this._shouldRegenerateShaders;
- },
- },
- /**
- * Whether or not to use the default spherical harmonic coefficients.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {Boolean}
- *
- * @private
- */
- useDefaultSphericalHarmonics: {
- get: function () {
- return this._useDefaultSphericalHarmonics;
- },
- },
- /**
- * Whether or not the image-based lighting settings use spherical harmonic coefficients.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {Boolean}
- *
- * @private
- */
- useSphericalHarmonicCoefficients: {
- get: function () {
- return (
- defined(this._sphericalHarmonicCoefficients) ||
- this._useDefaultSphericalHarmonics
- );
- },
- },
- /**
- * The texture atlas for the specular environment maps.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {OctahedralProjectedCubeMap}
- *
- * @private
- */
- specularEnvironmentMapAtlas: {
- get: function () {
- return this._specularEnvironmentMapAtlas;
- },
- },
- /**
- * Whether or not to use the default specular environment maps.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {Boolean}
- *
- * @private
- */
- useDefaultSpecularMaps: {
- get: function () {
- return this._useDefaultSpecularMaps;
- },
- },
- /**
- * Whether or not the image-based lighting settings use specular environment maps.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {Boolean}
- *
- * @private
- */
- useSpecularEnvironmentMaps: {
- get: function () {
- return (
- (defined(this._specularEnvironmentMapAtlas) &&
- this._specularEnvironmentMapAtlas.ready) ||
- this._useDefaultSpecularMaps
- );
- },
- },
- });
- function createSpecularEnvironmentMapAtlas(imageBasedLighting, context) {
- if (!OctahedralProjectedCubeMap.isSupported(context)) {
- return;
- }
- imageBasedLighting._specularEnvironmentMapAtlas =
- imageBasedLighting._specularEnvironmentMapAtlas &&
- imageBasedLighting._specularEnvironmentMapAtlas.destroy();
- if (defined(imageBasedLighting._specularEnvironmentMaps)) {
- const atlas = new OctahedralProjectedCubeMap(
- imageBasedLighting._specularEnvironmentMaps
- );
- imageBasedLighting._specularEnvironmentMapAtlas = atlas;
- atlas.readyPromise
- .then(function () {
- imageBasedLighting._specularEnvironmentMapLoaded = true;
- })
- .catch(function (error) {
- console.error(`Error loading specularEnvironmentMaps: ${error}`);
- });
- }
- // Regenerate shaders so they do not use an environment map.
- // Will be set to true again if there was a new environment map and it is ready.
- imageBasedLighting._shouldRegenerateShaders = true;
- }
- ImageBasedLighting.prototype.update = function (frameState) {
- if (frameState.frameNumber === this._previousFrameNumber) {
- return;
- }
- this._previousFrameNumber = frameState.frameNumber;
- const context = frameState.context;
- frameState.brdfLutGenerator.update(frameState);
- this._shouldRegenerateShaders = false;
- const iblFactor = this._imageBasedLightingFactor;
- const previousIBLFactor = this._previousImageBasedLightingFactor;
- if (!Cartesian2.equals(iblFactor, previousIBLFactor)) {
- this._shouldRegenerateShaders =
- (iblFactor.x > 0.0 && previousIBLFactor.x === 0.0) ||
- (iblFactor.x === 0.0 && previousIBLFactor.x > 0.0);
- this._shouldRegenerateShaders =
- this._shouldRegenerateShaders ||
- (iblFactor.y > 0.0 && previousIBLFactor.y === 0.0) ||
- (iblFactor.y === 0.0 && previousIBLFactor.y > 0.0);
- this._previousImageBasedLightingFactor = Cartesian2.clone(
- this._imageBasedLightingFactor,
- this._previousImageBasedLightingFactor
- );
- }
- if (this._luminanceAtZenith !== this._previousLuminanceAtZenith) {
- this._shouldRegenerateShaders =
- this._shouldRegenerateShaders ||
- defined(this._luminanceAtZenith) !==
- defined(this._previousLuminanceAtZenith);
- this._previousLuminanceAtZenith = this._luminanceAtZenith;
- }
- if (
- this._previousSphericalHarmonicCoefficients !==
- this._sphericalHarmonicCoefficients
- ) {
- this._shouldRegenerateShaders =
- this._shouldRegenerateShaders ||
- defined(this._previousSphericalHarmonicCoefficients) !==
- defined(this._sphericalHarmonicCoefficients);
- this._previousSphericalHarmonicCoefficients = this._sphericalHarmonicCoefficients;
- }
- this._shouldRegenerateShaders =
- this._shouldRegenerateShaders ||
- this._previousSpecularEnvironmentMapLoaded !==
- this._specularEnvironmentMapLoaded;
- this._previousSpecularEnvironmentMapLoaded = this._specularEnvironmentMapLoaded;
- if (this._specularEnvironmentMapAtlasDirty) {
- createSpecularEnvironmentMapAtlas(this, context);
- this._specularEnvironmentMapAtlasDirty = false;
- }
- if (defined(this._specularEnvironmentMapAtlas)) {
- this._specularEnvironmentMapAtlas.update(frameState);
- }
- const recompileWithDefaultAtlas =
- !defined(this._specularEnvironmentMapAtlas) &&
- defined(frameState.specularEnvironmentMaps) &&
- !this._useDefaultSpecularMaps;
- const recompileWithoutDefaultAtlas =
- !defined(frameState.specularEnvironmentMaps) &&
- this._useDefaultSpecularMaps;
- const recompileWithDefaultSHCoeffs =
- !defined(this._sphericalHarmonicCoefficients) &&
- defined(frameState.sphericalHarmonicCoefficients) &&
- !this._useDefaultSphericalHarmonics;
- const recompileWithoutDefaultSHCoeffs =
- !defined(frameState.sphericalHarmonicCoefficients) &&
- this._useDefaultSphericalHarmonics;
- this._shouldRegenerateShaders =
- this._shouldRegenerateShaders ||
- recompileWithDefaultAtlas ||
- recompileWithoutDefaultAtlas ||
- recompileWithDefaultSHCoeffs ||
- recompileWithoutDefaultSHCoeffs;
- this._useDefaultSpecularMaps =
- !defined(this._specularEnvironmentMapAtlas) &&
- defined(frameState.specularEnvironmentMaps);
- this._useDefaultSphericalHarmonics =
- !defined(this._sphericalHarmonicCoefficients) &&
- defined(frameState.sphericalHarmonicCoefficients);
- };
- /**
- * Returns true if this object was destroyed; otherwise, false.
- * <br /><br />
- * If this object was destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
- *
- * @returns {Boolean} True if this object was destroyed; otherwise, false.
- *
- * @see ImageBasedLighting#destroy
- * @private
- */
- ImageBasedLighting.prototype.isDestroyed = function () {
- return false;
- };
- /**
- * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
- * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
- * <br /><br />
- * Once an object is destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
- * assign the return value (<code>undefined</code>) to the object as done in the example.
- *
- * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
- *
- * @example
- * imageBasedLighting = imageBasedLighting && imageBasedLighting.destroy();
- *
- * @see ImageBasedLighting#isDestroyed
- * @private
- */
- ImageBasedLighting.prototype.destroy = function () {
- this._specularEnvironmentMapAtlas =
- this._specularEnvironmentMapAtlas &&
- this._specularEnvironmentMapAtlas.destroy();
- return destroyObject(this);
- };
|