| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- import Cartesian2 from "../Core/Cartesian2.js";
- import Check from "../Core/Check.js";
- import defined from "../Core/defined.js";
- import Frozen from "../Core/Frozen.js";
- import destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import SpecularEnvironmentCubeMap from "./SpecularEnvironmentCubeMap.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 {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.
- */
- function ImageBasedLighting(options) {
- options = options ?? Frozen.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 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._specularEnvironmentCubeMap = undefined;
- this._specularEnvironmentCubeMapDirty = 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;
- this._previousFrameContext = undefined;
-
- // Keeps track of the last values for use during update logic
- this._previousImageBasedLightingFactor = Cartesian2.clone(
- imageBasedLightingFactor,
- );
- this._previousSphericalHarmonicCoefficients = sphericalHarmonicCoefficients;
- this._removeErrorListener = undefined;
- }
-
- 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 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}.
- * Be sure to use the <code>--no-mirror</code> option in <code>cmgen</code>.
- *
- * @memberof ImageBasedLighting.prototype
- *
- * @type {Cartesian3[]}
- * @demo {@link https://sandcastle.cesium.com/index.html?id=image-based-lighting|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?id=image-based-lighting|Sandcastle Image Based Lighting Demo}
- * @type {string}
- * @see ImageBasedLighting#sphericalHarmonicCoefficients
- */
- specularEnvironmentMaps: {
- get: function () {
- return this._specularEnvironmentMaps;
- },
- set: function (value) {
- if (value !== this._specularEnvironmentMaps) {
- this._specularEnvironmentCubeMapDirty =
- this._specularEnvironmentCubeMapDirty ||
- 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;
- },
- },
-
- /**
- * The texture atlas for the specular environment maps.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {SpecularEnvironmentCubeMap}
- *
- * @private
- */
- specularEnvironmentCubeMap: {
- get: function () {
- return this._specularEnvironmentCubeMap;
- },
- },
-
- /**
- * Whether or not to use the default spherical harmonics coefficients.
- *
- * @memberof ImageBasedLighting.prototype
- * @type {boolean}
- *
- * @private
- */
- useDefaultSphericalHarmonics: {
- get: function () {
- return this._useDefaultSphericalHarmonics;
- },
- },
-
- /**
- * 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._specularEnvironmentCubeMap) &&
- this._specularEnvironmentCubeMap.ready) ||
- this._useDefaultSpecularMaps
- );
- },
- },
- });
-
- function createSpecularEnvironmentCubeMap(imageBasedLighting, context) {
- if (!SpecularEnvironmentCubeMap.isSupported(context)) {
- return;
- }
-
- imageBasedLighting._specularEnvironmentCubeMap =
- imageBasedLighting._specularEnvironmentCubeMap &&
- imageBasedLighting._specularEnvironmentCubeMap.destroy();
-
- if (defined(imageBasedLighting._specularEnvironmentMaps)) {
- const cubeMap = new SpecularEnvironmentCubeMap(
- imageBasedLighting._specularEnvironmentMaps,
- );
- imageBasedLighting._specularEnvironmentCubeMap = cubeMap;
-
- imageBasedLighting._removeErrorListener =
- cubeMap.errorEvent.addEventListener((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 &&
- frameState.context === this._previousFrameContext
- ) {
- return;
- }
-
- this._previousFrameNumber = frameState.frameNumber;
- const context = (this._previousFrameContext = 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._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._specularEnvironmentCubeMapDirty) {
- createSpecularEnvironmentCubeMap(this, context);
- this._specularEnvironmentCubeMapDirty = false;
- }
-
- if (defined(this._specularEnvironmentCubeMap)) {
- this._specularEnvironmentCubeMap.update(frameState);
- if (this._specularEnvironmentCubeMap.ready) {
- this._specularEnvironmentMapLoaded = true;
- }
- }
-
- const recompileWithDefaultCubeMap =
- !defined(this._specularEnvironmentCubeMap) &&
- defined(frameState.specularEnvironmentMaps) &&
- !this._useDefaultSpecularMaps;
- const recompileWithoutDefaultCubeMap =
- !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 ||
- recompileWithDefaultCubeMap ||
- recompileWithoutDefaultCubeMap ||
- recompileWithDefaultSHCoeffs ||
- recompileWithoutDefaultSHCoeffs;
-
- this._useDefaultSpecularMaps =
- !defined(this._specularEnvironmentCubeMap) &&
- 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._specularEnvironmentCubeMap =
- this._specularEnvironmentCubeMap &&
- this._specularEnvironmentCubeMap.destroy();
- this._removeErrorListener =
- this._removeErrorListener && this._removeErrorListener();
- return destroyObject(this);
- };
-
- export default ImageBasedLighting;
|