Skip to content

Commit

Permalink
Add fallback spherical harmonics when dynamic environment lighting is…
Browse files Browse the repository at this point in the history
… not supported
  • Loading branch information
ggetz committed Nov 7, 2024
1 parent 57857b0 commit 730c419
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 63 deletions.
44 changes: 43 additions & 1 deletion packages/engine/Source/Scene/DynamicEnvironmentMapManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ function DynamicEnvironmentMapManager(options) {
this._radianceCubeMap = undefined;
this._irradianceMapTexture = undefined;

this._sphericalHarmonicCoefficients = new Array(9);
this._sphericalHarmonicCoefficients =
DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS.slice();

this._lastTime = new JulianDate();
const width = Math.pow(2, mipmapLevels - 1);
Expand Down Expand Up @@ -727,8 +728,11 @@ function updateSphericalHarmonicCoefficients(manager, frameState) {
*/
DynamicEnvironmentMapManager.prototype.update = function (frameState) {
const mode = frameState.mode;
const isSupported =
DynamicEnvironmentMapManager.isDynamicUpdateSupported(frameState);

if (
!isSupported ||
!this.enabled ||
!this.shouldUpdate ||
!defined(this._position) ||
Expand Down Expand Up @@ -841,12 +845,50 @@ DynamicEnvironmentMapManager.prototype.destroy = function () {
return destroyObject(this);
};

/**
* Returns <code>true</code> if dynamic updates are supported in the current WebGL rendering context.
* Dynamic updates requires the EXT_color_buffer_float or EXT_color_buffer_half_float extension.
*
* @param {Scene|FrameState} contextOption The object containing the rendering context
* @returns {boolean} true if supported
*/
DynamicEnvironmentMapManager.isDynamicUpdateSupported = function (
contextOption,
) {
const context = contextOption.context;
return context.halfFloatingPointTexture || context.colorBufferFloat;
};

/**
* Average hue of ground color on earth, a warm green-gray.
* @type {Color}
* @readonly
*/
DynamicEnvironmentMapManager.AVERAGE_EARTH_GROUND_COLOR = Object.freeze(
Color.fromCssColorString("#717145"),
);

/**
* The default third order spherical harmonic coefficients used for the diffuse color of image-based lighting, a white ambient light with low intensity.
* <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>
* @readonly
* @type {Cartesian3[]}
* @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps}
*/
DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS =
Object.freeze([
Object.freeze(new Cartesian3(0.35449, 0.35449, 0.35449)),
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
]);

export default DynamicEnvironmentMapManager;
3 changes: 0 additions & 3 deletions packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,6 @@ describe(

options = {
cullRequestsWhileMoving: false,
environmentMapOptions: {
enabled: scene.highDynamicRangeSupported,
},
};
});

Expand Down
150 changes: 124 additions & 26 deletions packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,40 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
expect(manager.groundAlbedo).toBe(0.31);
});

it("constructs with options", function () {
const manager = new DynamicEnvironmentMapManager({
enabled: false,
maximumSecondsDifference: 0,
maximumPositionEpsilon: 0,
atmosphereScatteringIntensity: 0,
gamma: 0,
brightness: 0,
saturation: 0,
groundColor: Color.BLUE,
groundAlbedo: 0,
});

expect(manager.enabled).toBeFalse();
expect(manager.shouldUpdate).toBeTrue();
expect(manager.maximumSecondsDifference).toBe(0);
expect(manager.maximumPositionEpsilon).toBe(0);
expect(manager.atmosphereScatteringIntensity).toBe(0);
expect(manager.gamma).toBe(0);
expect(manager.brightness).toBe(0);
expect(manager.saturation).toBe(0);
expect(manager.groundColor).toEqual(Color.BLUE);
expect(manager.groundAlbedo).toBe(0);
});

it("uses default spherical harmonic coefficients", () => {
const manager = new DynamicEnvironmentMapManager();

expect(manager.sphericalHarmonicCoefficients.length).toBe(9);
expect(manager.sphericalHarmonicCoefficients).toEqual(
DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS,
);
});

describe(
"render tests",
() => {
Expand All @@ -53,7 +87,7 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
scene.atmosphere = new Atmosphere();
});

// Allows the compute commands to be added to the command list at the right point in the pipeline
// A pared-down Primitive. Allows the compute commands to be added to the command list at the right point in the pipeline.
function EnvironmentMockPrimitive(manager) {
this.update = function (frameState) {
manager.update(frameState);
Expand All @@ -64,8 +98,74 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
};
}

it("does not update if position is undefined", async function () {
const manager = new DynamicEnvironmentMapManager();

const primitive = new EnvironmentMockPrimitive(manager);
scene.primitives.add(primitive);

scene.renderForSpecs();

expect(manager.radianceCubeMap).toBeUndefined();

scene.renderForSpecs();

expect(manager.sphericalHarmonicCoefficients).toEqual(
DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS,
);
});

it("does not update if enabled is false", async function () {
const manager = new DynamicEnvironmentMapManager();

const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583);
manager.position =
Ellipsoid.WGS84.cartographicToCartesian(cartographic);
manager.enabled = false;

const primitive = new EnvironmentMockPrimitive(manager);
scene.primitives.add(primitive);

scene.renderForSpecs();

expect(manager.radianceCubeMap).toBeUndefined();

scene.renderForSpecs();

expect(manager.sphericalHarmonicCoefficients).toEqual(
DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS,
);
});

it("does not update if requires extensions are not available", async function () {
spyOn(
DynamicEnvironmentMapManager,
"isDynamicUpdateSupported",
).and.returnValue(false);

const manager = new DynamicEnvironmentMapManager();

const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583);
manager.position =
Ellipsoid.WGS84.cartographicToCartesian(cartographic);

const primitive = new EnvironmentMockPrimitive(manager);
scene.primitives.add(primitive);

scene.renderForSpecs();

expect(manager.radianceCubeMap).toBeUndefined();

scene.renderForSpecs();

expect(manager.sphericalHarmonicCoefficients).toEqual(
DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS,
);
});

it("creates environment map and spherical harmonics at surface in Philadelphia with static lighting", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -146,7 +246,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("creates environment map and spherical harmonics at altitude in Philadelphia with static lighting", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -231,7 +332,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("creates environment map and spherical harmonics above Earth's atmosphere with static lighting", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -317,7 +419,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("creates environment map and spherical harmonics at surface in Philadelphia with dynamic lighting", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -401,7 +504,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("creates environment map and spherical harmonics at surface in Sydney with dynamic lighting", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -485,7 +589,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("lighting uses atmosphere properties", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -572,10 +677,10 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("lighting uses atmosphereScatteringIntensity value", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

const manager = new DynamicEnvironmentMapManager();
manager.atmosphereScatteringIntensity = 1.0;

Expand Down Expand Up @@ -654,7 +759,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("lighting uses gamma value", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -736,7 +842,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("lighting uses brightness value", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -818,7 +925,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("lighting uses saturation value", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -900,7 +1008,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("lighting uses ground color value", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -982,7 +1091,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("lighting uses ground albedo value", async function () {
if (!scene.highDynamicRangeSupported) {
// Skip if required WebGL extensions are not supported
if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) {
return;
}

Expand Down Expand Up @@ -1064,18 +1174,6 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
});

it("destroys", function () {
if (!scene.highDynamicRangeSupported) {
const manager = new DynamicEnvironmentMapManager();
const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583);
manager.position =
Ellipsoid.WGS84.cartographicToCartesian(cartographic);

manager.destroy();

expect(manager.isDestroyed()).toBe(true);
return;
}

const manager = new DynamicEnvironmentMapManager();
const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583);
manager.position =
Expand Down
18 changes: 1 addition & 17 deletions packages/engine/Specs/Scene/Model/loadAndZoomToModelAsync.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
import { Model, ImageBasedLighting, Cartesian3 } from "../../../index.js";
import { Model } from "../../../index.js";
import pollToPromise from "../../../../../Specs/pollToPromise.js";

// A white ambient light with low intensity
const defaultIbl = new ImageBasedLighting({
sphericalHarmonicCoefficients: [
new Cartesian3(0.35449, 0.35449, 0.35449),
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
Cartesian3.ZERO,
],
});

async function loadAndZoomToModelAsync(options, scene) {
options = {
environmentMapOptions: {
enabled: false, // disable other diffuse lighting by default
...options.environmentMapOptions,
},
imageBasedLighting: defaultIbl,
...options,
};

Expand Down
Loading

0 comments on commit 730c419

Please sign in to comment.