diff --git a/Apps/Sandcastle/gallery/AEC Architectural Design.html b/Apps/Sandcastle/gallery/AEC Architectural Design.html index 00e9e76cf14d..a1aff825bb57 100644 --- a/Apps/Sandcastle/gallery/AEC Architectural Design.html +++ b/Apps/Sandcastle/gallery/AEC Architectural Design.html @@ -50,6 +50,17 @@ // Enable rendering the sky viewer.scene.skyAtmosphere.show = true; + // Configure Ambient Occlusion + if (Cesium.PostProcessStageLibrary.isAmbientOcclusionSupported(viewer.scene)) { + const ambientOcclusion = viewer.scene.postProcessStages.ambientOcclusion; + ambientOcclusion.enabled = true; + ambientOcclusion.uniforms.intensity = 2.0; + ambientOcclusion.uniforms.bias = 0.1; + ambientOcclusion.uniforms.lengthCap = 0.5; + ambientOcclusion.uniforms.directionCount = 16; + ambientOcclusion.uniforms.stepCount = 32; + } + // Set to 1 PM Philadelphia time in UTC viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2024-11-22T18:00:00Z"); @@ -90,24 +101,29 @@ // The Architectural Design is comprised of multiple tilesets const tilesetData = [ - { title: "Architecture", assetId: 2864367 }, - { title: "Facade", assetId: 2864370 }, - { title: "Structural", assetId: 2864375 }, - { title: "Electrical", assetId: 2864368 }, - { title: "HVAC", assetId: 2864372 }, - { title: "Plumbing", assetId: 2864373 }, - { title: "Site", assetId: 2864374 }, + { title: "Architecture", assetId: 2887123, visible: true }, + { title: "Facade", assetId: 2887125, visible: true }, + { title: "Structural", assetId: 2887130, visible: false }, + { title: "Electrical", assetId: 2887124, visible: true }, + { title: "HVAC", assetId: 2887126, visible: true }, + { title: "Plumbing", assetId: 2887127, visible: true }, + { title: "Site", assetId: 2887129, visible: true }, ]; // Load each tileset and create a corresponding visibility toggle button - for (const { title, assetId } of tilesetData) { + for (const { title, assetId, visible } of tilesetData) { try { const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(assetId); viewer.scene.primitives.add(tileset); - - Sandcastle.addToggleButton(title, true, function (checked) { - tileset.show = checked; - }); + tileset.show = visible; + + const toggleBtn = Sandcastle.addToggleButton( + title, + visible, + function (checked) { + tileset.show = checked; + }, + ); } catch (error) { console.log(`Error loading tileset (${title}): ${error}`); } diff --git a/Apps/Sandcastle/gallery/AEC Isolate by Category.html b/Apps/Sandcastle/gallery/AEC Isolate by Category.html index 12c67bd69f8c..70170c1c7fc6 100644 --- a/Apps/Sandcastle/gallery/AEC Isolate by Category.html +++ b/Apps/Sandcastle/gallery/AEC Isolate by Category.html @@ -49,6 +49,17 @@ // Enable rendering the sky viewer.scene.skyAtmosphere.show = true; + // Configure Ambient Occlusion + if (Cesium.PostProcessStageLibrary.isAmbientOcclusionSupported(viewer.scene)) { + const ambientOcclusion = viewer.scene.postProcessStages.ambientOcclusion; + ambientOcclusion.enabled = true; + ambientOcclusion.uniforms.intensity = 2.0; + ambientOcclusion.uniforms.bias = 0.1; + ambientOcclusion.uniforms.lengthCap = 0.5; + ambientOcclusion.uniforms.directionCount = 16; + ambientOcclusion.uniforms.stepCount = 32; + } + // Set to 1 PM Philadelphia time in UTC viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2024-11-22T18:00:00Z"); @@ -87,10 +98,10 @@ googleTileset.clippingPolygons = polygons; - // Add the architectural tileset as a semi-transparent ghost + // Add the architectural tileset let archTileset; try { - archTileset = await Cesium.Cesium3DTileset.fromIonAssetId(2864367); + archTileset = await Cesium.Cesium3DTileset.fromIonAssetId(2887123); viewer.scene.primitives.add(archTileset); } catch (error) { console.log(`Error loading tileset: ${error}`); @@ -98,7 +109,7 @@ // Add the site tileset try { - const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2864374); + const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2887129); viewer.scene.primitives.add(tileset); } catch (error) { console.log(`Error loading tileset: ${error}`); diff --git a/Apps/Sandcastle/gallery/AEC Metadata Styling.html b/Apps/Sandcastle/gallery/AEC Metadata Styling.html index 2cc67f49c20d..ff8dd8fe0cc6 100644 --- a/Apps/Sandcastle/gallery/AEC Metadata Styling.html +++ b/Apps/Sandcastle/gallery/AEC Metadata Styling.html @@ -71,6 +71,17 @@ // Enable rendering the sky viewer.scene.skyAtmosphere.show = true; + // Configure Ambient Occlusion + if (Cesium.PostProcessStageLibrary.isAmbientOcclusionSupported(viewer.scene)) { + const ambientOcclusion = viewer.scene.postProcessStages.ambientOcclusion; + ambientOcclusion.enabled = true; + ambientOcclusion.uniforms.intensity = 2.0; + ambientOcclusion.uniforms.bias = 0.1; + ambientOcclusion.uniforms.lengthCap = 0.5; + ambientOcclusion.uniforms.directionCount = 16; + ambientOcclusion.uniforms.stepCount = 32; + } + // Set to 1 PM Philadelphia time in UTC viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2024-11-22T18:00:00Z"); @@ -111,7 +122,7 @@ // Add the architectural tileset as a semi-transparent ghost try { - const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2864367); + const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2887123); viewer.scene.primitives.add(tileset); tileset.style = new Cesium.Cesium3DTileStyle({ color: "color('lightblue', 0.05)", @@ -122,16 +133,16 @@ // Add the site tileset try { - const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2864374); + const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2887129); viewer.scene.primitives.add(tileset); } catch (error) { console.log(`Error loading tileset: ${error}`); } - // Add the pipe tileset which will be styled by metadata + // Add the HVAC tileset which will be styled by metadata let pipeTileset; try { - pipeTileset = await Cesium.Cesium3DTileset.fromIonAssetId(2864372); + pipeTileset = await Cesium.Cesium3DTileset.fromIonAssetId(2887126); pipeTileset.maximumScreenSpaceError = 4; viewer.scene.primitives.add(pipeTileset); } catch (error) { diff --git a/Apps/Sandcastle/gallery/Ambient Occlusion.html b/Apps/Sandcastle/gallery/Ambient Occlusion.html index c6017addff93..4737974cff33 100644 --- a/Apps/Sandcastle/gallery/Ambient Occlusion.html +++ b/Apps/Sandcastle/gallery/Ambient Occlusion.html @@ -62,38 +62,38 @@ - Step Size + Step Count - Bias + Direction Count - Blur Step Size + Bias @@ -106,7 +106,11 @@ //Sandcastle_Begin const viewer = new Cesium.Viewer("cesiumContainer"); - viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2022-08-01T00:00:00Z"); + const { canvas, camera, clock, scene } = viewer; + camera.frustum.near = 1.0; + scene.debugShowFramesPerSecond = true; + + clock.currentTime = Cesium.JulianDate.fromIso8601("2022-08-01T00:00:00Z"); if (!Cesium.PostProcessStageLibrary.isAmbientOcclusionSupported(viewer.scene)) { window.alert( @@ -119,9 +123,9 @@ ambientOcclusionOnly: false, intensity: 3.0, bias: 0.1, - lengthCap: 0.03, - stepSize: 1.0, - blurStepSize: 0.86, + lengthCap: 0.26, + directionCount: 8, + stepCount: 32, }; Cesium.knockout.track(viewModel); @@ -134,7 +138,7 @@ } function updatePostProcess() { - const ambientOcclusion = viewer.scene.postProcessStages.ambientOcclusion; + const ambientOcclusion = scene.postProcessStages.ambientOcclusion; ambientOcclusion.enabled = Boolean(viewModel.show) || Boolean(viewModel.ambientOcclusionOnly); ambientOcclusion.uniforms.ambientOcclusionOnly = Boolean( @@ -143,12 +147,11 @@ ambientOcclusion.uniforms.intensity = Number(viewModel.intensity); ambientOcclusion.uniforms.bias = Number(viewModel.bias); ambientOcclusion.uniforms.lengthCap = Number(viewModel.lengthCap); - ambientOcclusion.uniforms.stepSize = Number(viewModel.stepSize); - ambientOcclusion.uniforms.blurStepSize = Number(viewModel.blurStepSize); + ambientOcclusion.uniforms.directionCount = Number(viewModel.directionCount); + ambientOcclusion.uniforms.stepCount = Number(viewModel.stepCount); } updatePostProcess(); - const camera = viewer.scene.camera; camera.position = new Cesium.Cartesian3( 1234127.2294710164, -5086011.666443127, @@ -173,7 +176,7 @@ try { // Power Plant design model provided by Bentley Systems const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2464651); - viewer.scene.primitives.add(tileset); + scene.primitives.add(tileset); } catch (error) { console.log(`Error loading tileset: ${error}`); } //Sandcastle_End diff --git a/Apps/Sandcastle/gallery/Clipping Regions.html b/Apps/Sandcastle/gallery/Clipping Regions.html index b7116fc29f17..f56a5c886b27 100644 --- a/Apps/Sandcastle/gallery/Clipping Regions.html +++ b/Apps/Sandcastle/gallery/Clipping Regions.html @@ -43,7 +43,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocoderProviderType.GOOGLE, + geocoder: Cesium.IonGeocodeProviderType.GOOGLE, }); const scene = viewer.scene; diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iModel Mesh Export Service.html similarity index 99% rename from Apps/Sandcastle/gallery/iTwin Demo.html rename to Apps/Sandcastle/gallery/iModel Mesh Export Service.html index e78ab95f4c15..b445ff763ad7 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iModel Mesh Export Service.html @@ -12,7 +12,7 @@ content="Demonstrate loading iModel data from the iTwin platform." /> - iTwin iModel demo + iModel Mesh Export Service diff --git a/Apps/Sandcastle/gallery/iTwin Demo.jpg b/Apps/Sandcastle/gallery/iModel Mesh Export Service.jpg similarity index 100% rename from Apps/Sandcastle/gallery/iTwin Demo.jpg rename to Apps/Sandcastle/gallery/iModel Mesh Export Service.jpg diff --git a/CHANGES.md b/CHANGES.md index 61808466ebdc..698829a9cbd4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,7 +10,7 @@ - Added an integration with the [iTwin Platform](https://developer.bentley.com/) to load Reality Data terrain meshes and GeoJSON. Use `ITwinPlatform.defaultAccessToken` to set the access token. Then use `ITwinData.createTilesetForRealityDataId(iTwinId, dataId)` to load terrain meshes as a `Cesium3DTileset` or `ITwinData.createDataSourceForRealityDataId(iTwinId, dataId)` to load GeoJSON or KML files as data sources. [#12344](https://github.com/CesiumGS/cesium/pull/12344) - Added `getSample` to `SampledProperty` to get the time of samples. [#12253](https://github.com/CesiumGS/cesium/pull/12253) - Added `Entity.trackingReferenceFrame` property to allow tracking entities in various reference frames. [#12194](https://github.com/CesiumGS/cesium/pull/12194), [#12314](https://github.com/CesiumGS/cesium/pull/12314) - - `TrackingReferenceFrame.AUTODETECT` (default): uses either VVLH or ENU dependeding on entity's dynamic. Use `TrackingReferenceFrame.ENU` if your camera orientation flips abruptly from time to time. + - `TrackingReferenceFrame.AUTODETECT` (default): uses either VVLH or ENU depending on entity's dynamic. Use `TrackingReferenceFrame.ENU` if your camera orientation flips abruptly from time to time. - `TrackingReferenceFrame.ENU`: uses the entity's local East-North-Up reference frame. - `TrackingReferenceFrame.INERTIAL`: uses the entity's inertial reference frame. - `TrackingReferenceFrame.VELOCITY`: uses entity's `VelocityOrientationProperty` as orientation. @@ -18,15 +18,16 @@ ##### Breaking Changes :mega: +- `PostProcessStageCollection.ambientOcclusion` has been updated with a new algorithm to provide better results at all scales, with tunable performance cost. To approximate the appearance and performance of the old algorithm, set the following values for `scene.postProcessStages.ambientOcclusion.uniforms`: `{ lengthCap: 0.02, directionCount: 6, stepCount: 8 }`. For best results at long distances, consider setting `Viewer.camera.frustum.near` to `1.0` or more, to improve precision in the depth buffer. [#12316](https://github.com/CesiumGS/cesium/pull/12316) - `Rectangle.validate` has been removed. ##### Fixes :wrench: - Fixed bug where shared external textures from glTF files were not accounted for in resource statistics. [#12331](https://github.com/CesiumGS/cesium/pull/12331) -- Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) - Fixed lag or crashes when loading many models in the same frame. [#12320](https://github.com/CesiumGS/cesium/pull/12320) -- Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323) - Fix point cloud filtering performance on certain hardware [#12317](https://github.com/CesiumGS/cesium/pull/12317) +- Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) +- Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323) ##### Deprecated :hourglass_flowing_sand: diff --git a/ThirdParty.json b/ThirdParty.json index 10a55aa1d64e..ed72a070a4c1 100644 --- a/ThirdParty.json +++ b/ThirdParty.json @@ -44,7 +44,7 @@ "license": [ "Apache-2.0" ], - "version": "3.1.7", + "version": "3.2.2", "url": "https://www.npmjs.com/package/dompurify", "notes": "dompurify is available as both MPL-2.0 OR Apache-2.0" }, diff --git a/package.json b/package.json index 41eb18d38f80..5f1ed19eb0bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium", - "version": "1.123.1", + "version": "1.124.0", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "homepage": "http://cesium.com/cesiumjs/", "license": "Apache-2.0", @@ -51,8 +51,8 @@ "./Specs/**/*" ], "dependencies": { - "@cesium/engine": "^12.0.1", - "@cesium/widgets": "^9.0.1" + "@cesium/engine": "^13.0.0", + "@cesium/widgets": "^10.0.0" }, "devDependencies": { "@playwright/test": "^1.41.1", diff --git a/packages/engine/Source/Core/Ion.js b/packages/engine/Source/Core/Ion.js index 56c3b1d900d0..a0a75ec33486 100644 --- a/packages/engine/Source/Core/Ion.js +++ b/packages/engine/Source/Core/Ion.js @@ -4,7 +4,7 @@ import Resource from "./Resource.js"; let defaultTokenCredit; const defaultAccessToken = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiZjBmMDE4Ny05M2JlLTRlMzgtYjIxYi05YmJjM2QwMzJkYWMiLCJpZCI6MjU5LCJpYXQiOjE3MzA0NjY3MDl9.t-7gCGPUe-oGCyCoeXPtYmlMVdgqUQD9mn-Da23yUoI"; + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZDdmNWJiNy0wMmNlLTQ1MWUtODM2YS02NGM1MTBlOGMwMWQiLCJpZCI6MjU5LCJpYXQiOjE3MzMxNTc4OTV9.B3URHf0VdHDtGckb-hv7uqATdn8KfvkiuoAFZUq8tAo"; /** * Default settings for accessing the Cesium ion API. * diff --git a/packages/engine/Source/Scene/ArcGisMapService.js b/packages/engine/Source/Scene/ArcGisMapService.js index e2f65714ba21..693004b42cd1 100644 --- a/packages/engine/Source/Scene/ArcGisMapService.js +++ b/packages/engine/Source/Scene/ArcGisMapService.js @@ -4,7 +4,7 @@ import Resource from "../Core/Resource.js"; let defaultTokenCredit; const defaultAccessToken = - "AAPTxy8BH1VEsoebNVZXo8HurEOF051kAEKlhkOhBEc9BmQpcUfxe1Yndhf82d5oKkQJ4_7VPaBQGYSISOMaRew7Sy-eTX1JQ4XwaC8v5aCvV72O6LCPs5Ss1pXXH-0uEw6bSRhTeQYHOmikutC2OMyZt6lu0VfT7FA-jVMO_UsunWNTf2cycP2O4IeDN_UV9G-VNmUu2jRvCHioi8o72ua4238s2219cYLEmcoGRJGVJTA.AT1_PjLvyih0"; + "AAPTxy8BH1VEsoebNVZXo8HurEOF051kAEKlhkOhBEc9BmSrZYLHFXe7j_lQcsSJKc8-7rwh0IFSNWLGZErkzXRnYjMjURTz-hGiKMEeAJIZBG7uiYEn0Mt1rrwlJGIpirZQC4iO428519DlO3QC9DnRBqLXGTBhirgoU7-Z2209sy87s49kw6NOC8_Eew6nCLf-pZ883DRPRyAYH7LC8cvRLInud0EdndtUFa4y83TamrA.AT1_ahjrWDrq"; /** * Default options for accessing the ArcGIS image tile service. * diff --git a/packages/engine/Source/Scene/PostProcessStage.js b/packages/engine/Source/Scene/PostProcessStage.js index 44012ca77b56..454822ca4e8f 100644 --- a/packages/engine/Source/Scene/PostProcessStage.js +++ b/packages/engine/Source/Scene/PostProcessStage.js @@ -94,9 +94,16 @@ import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js"; function PostProcessStage(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); const { + name = createGuid(), fragmentShader, + uniforms, textureScale = 1.0, + forcePowerOfTwo = false, + sampleMode = PostProcessStageSampleMode.NEAREST, pixelFormat = PixelFormat.RGBA, + pixelDatatype = PixelDatatype.UNSIGNED_BYTE, + clearColor = Color.BLACK, + scissorRectangle, } = options; //>>includeStart('debug', pragmas.debug); @@ -113,19 +120,13 @@ function PostProcessStage(options) { //>>includeEnd('debug'); this._fragmentShader = fragmentShader; - this._uniforms = options.uniforms; + this._uniforms = uniforms; this._textureScale = textureScale; - this._forcePowerOfTwo = defaultValue(options.forcePowerOfTwo, false); - this._sampleMode = defaultValue( - options.sampleMode, - PostProcessStageSampleMode.NEAREST, - ); + this._forcePowerOfTwo = forcePowerOfTwo; + this._sampleMode = sampleMode; this._pixelFormat = pixelFormat; - this._pixelDatatype = defaultValue( - options.pixelDatatype, - PixelDatatype.UNSIGNED_BYTE, - ); - this._clearColor = defaultValue(options.clearColor, Color.BLACK); + this._pixelDatatype = pixelDatatype; + this._clearColor = clearColor; this._uniformMap = undefined; this._command = undefined; @@ -143,18 +144,14 @@ function PostProcessStage(options) { const passState = new PassState(); passState.scissorTest = { enabled: true, - rectangle: defined(options.scissorRectangle) - ? BoundingRectangle.clone(options.scissorRectangle) + rectangle: defined(scissorRectangle) + ? BoundingRectangle.clone(scissorRectangle) : new BoundingRectangle(), }; this._passState = passState; this._ready = false; - let name = options.name; - if (!defined(name)) { - name = createGuid(); - } this._name = name; this._logDepthChanged = undefined; diff --git a/packages/engine/Source/Scene/PostProcessStageCollection.js b/packages/engine/Source/Scene/PostProcessStageCollection.js index 59ca0e293923..20019fa840cd 100644 --- a/packages/engine/Source/Scene/PostProcessStageCollection.js +++ b/packages/engine/Source/Scene/PostProcessStageCollection.js @@ -155,32 +155,20 @@ Object.defineProperties(PostProcessStageCollection.prototype, { * surface receives light and regardless of the light's position. *

*

- * The uniforms have the following properties: intensity, bias, lengthCap, - * stepSize, frustumLength, ambientOcclusionOnly, - * delta, sigma, and blurStepSize. - *

+ * The uniforms have the following properties: * *

- * delta, sigma, and blurStepSize are the same properties as {@link PostProcessStageLibrary#createBlurStage}. - * The blur is applied to the shadows generated from the image to make them smoother. - *

- *

* When enabled, this stage will execute before all others. *

* diff --git a/packages/engine/Source/Scene/PostProcessStageLibrary.js b/packages/engine/Source/Scene/PostProcessStageLibrary.js index f4329daee77f..9168c068da54 100644 --- a/packages/engine/Source/Scene/PostProcessStageLibrary.js +++ b/packages/engine/Source/Scene/PostProcessStageLibrary.js @@ -476,27 +476,19 @@ PostProcessStageLibrary.createBloomStage = function () { * surface receives light and regardless of the light's position. *

*

- * The uniforms have the following properties: intensity, bias, lengthCap, - * stepSize, frustumLength, randomTexture, ambientOcclusionOnly, - * delta, sigma, and blurStepSize. - *

+ * The uniforms have the following properties: * - *

- * delta, sigma, and blurStepSize are the same properties as {@link PostProcessStageLibrary#createBlurStage}. - * The blur is applied to the shadows generated from the image to make them smoother. - *

* @return {PostProcessStageComposite} A post-process stage that applies an ambient occlusion effect. * * @private @@ -509,24 +501,18 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () { intensity: 3.0, bias: 0.1, lengthCap: 0.26, - stepSize: 1.95, - frustumLength: 1000.0, + directionCount: 8, + stepCount: 32, randomTexture: undefined, }, }); - const blur = createBlur("czm_ambient_occlusion_blur"); - blur.uniforms.stepSize = 0.86; - const generateAndBlur = new PostProcessStageComposite({ - name: "czm_ambient_occlusion_generate_blur", - stages: [generate, blur], - }); const ambientOcclusionModulate = new PostProcessStage({ name: "czm_ambient_occlusion_composite", fragmentShader: AmbientOcclusionModulate, uniforms: { ambientOcclusionOnly: false, - ambientOcclusionTexture: generateAndBlur.name, + ambientOcclusionTexture: generate.name, }, }); @@ -556,20 +542,20 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () { generate.uniforms.lengthCap = value; }, }, - stepSize: { + directionCount: { get: function () { - return generate.uniforms.stepSize; + return generate.uniforms.directionCount; }, set: function (value) { - generate.uniforms.stepSize = value; + generate.uniforms.directionCount = value; }, }, - frustumLength: { + stepCount: { get: function () { - return generate.uniforms.frustumLength; + return generate.uniforms.stepCount; }, set: function (value) { - generate.uniforms.frustumLength = value; + generate.uniforms.stepCount = value; }, }, randomTexture: { @@ -580,30 +566,6 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () { generate.uniforms.randomTexture = value; }, }, - delta: { - get: function () { - return blur.uniforms.delta; - }, - set: function (value) { - blur.uniforms.delta = value; - }, - }, - sigma: { - get: function () { - return blur.uniforms.sigma; - }, - set: function (value) { - blur.uniforms.sigma = value; - }, - }, - blurStepSize: { - get: function () { - return blur.uniforms.stepSize; - }, - set: function (value) { - blur.uniforms.stepSize = value; - }, - }, ambientOcclusionOnly: { get: function () { return ambientOcclusionModulate.uniforms.ambientOcclusionOnly; @@ -616,7 +578,7 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () { return new PostProcessStageComposite({ name: "czm_ambient_occlusion", - stages: [generateAndBlur, ambientOcclusionModulate], + stages: [generate, ambientOcclusionModulate], inputPreviousStageTexture: false, uniforms: uniforms, }); diff --git a/packages/engine/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl b/packages/engine/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl index d28f45494104..41bdfd7a0072 100644 --- a/packages/engine/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl +++ b/packages/engine/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl @@ -4,7 +4,7 @@ float czm_reverseLogDepth(float logZ) float near = czm_currentFrustum.x; float far = czm_currentFrustum.y; float log2Depth = logZ * czm_log2FarDepthFromNearPlusOne; - float depthFromNear = pow(2.0, log2Depth) - 1.0; + float depthFromNear = exp2(log2Depth) - 1.0; return far * (1.0 - near / (depthFromNear + near)) / (far - near); #endif return logZ; diff --git a/packages/engine/Source/Shaders/ConvolveSpecularMapFS.glsl b/packages/engine/Source/Shaders/ConvolveSpecularMapFS.glsl index 5eb290ecf3e1..5bb664548a11 100644 --- a/packages/engine/Source/Shaders/ConvolveSpecularMapFS.glsl +++ b/packages/engine/Source/Shaders/ConvolveSpecularMapFS.glsl @@ -62,7 +62,7 @@ void main() { float NdotL = max(dot(V, L), 0.0); if (NdotL > 0.0) { - color += vec4(texture(u_radianceTexture, L).rgb, 1.0) * NdotL; + color += vec4(czm_textureCube(u_radianceTexture, L).rgb, 1.0) * NdotL; weight += NdotL; } } diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 42535230cbdc..149de02fa560 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -5,16 +5,21 @@ uniform sampler2D depthTexture; uniform float intensity; uniform float bias; uniform float lengthCap; -uniform float stepSize; -uniform float frustumLength; +uniform int stepCount; +uniform int directionCount; -vec3 pixelToEye(vec2 screenCoordinate) +vec4 pixelToEye(vec2 screenCoordinate) { vec2 uv = screenCoordinate / czm_viewport.zw; float depth = czm_readDepth(depthTexture, uv); vec2 xy = 2.0 * uv - vec2(1.0); vec4 posEC = czm_inverseProjection * vec4(xy, depth, 1.0); - return posEC.xyz / posEC.w; + posEC = posEC / posEC.w; + // Avoid numerical error at far plane + if (depth >= 1.0) { + posEC.z = czm_currentFrustum.y; + } + return posEC; } // Reconstruct surface normal in eye coordinates, avoiding edges @@ -22,10 +27,10 @@ vec3 getNormalXEdge(vec3 positionEC) { // Find the 3D surface positions at adjacent screen pixels vec2 centerCoord = gl_FragCoord.xy; - vec3 positionLeft = pixelToEye(centerCoord + vec2(-1.0, 0.0)); - vec3 positionRight = pixelToEye(centerCoord + vec2(1.0, 0.0)); - vec3 positionUp = pixelToEye(centerCoord + vec2(0.0, 1.0)); - vec3 positionDown = pixelToEye(centerCoord + vec2(0.0, -1.0)); + vec3 positionLeft = pixelToEye(centerCoord + vec2(-1.0, 0.0)).xyz; + vec3 positionRight = pixelToEye(centerCoord + vec2(1.0, 0.0)).xyz; + vec3 positionUp = pixelToEye(centerCoord + vec2(0.0, 1.0)).xyz; + vec3 positionDown = pixelToEye(centerCoord + vec2(0.0, -1.0)).xyz; // Compute potential tangent vectors vec3 dx0 = positionEC - positionLeft; @@ -40,22 +45,35 @@ vec3 getNormalXEdge(vec3 positionEC) return normalize(cross(dx, dy)); } +const float sqrtTwoPi = sqrt(czm_twoPi); + +float gaussian(float x, float standardDeviation) { + float argument = x / standardDeviation; + return exp(-0.5 * argument * argument) / (sqrtTwoPi * standardDeviation); +} + void main(void) { - vec3 positionEC = pixelToEye(gl_FragCoord.xy); + vec4 positionEC = pixelToEye(gl_FragCoord.xy); - if (positionEC.z > frustumLength) + // Exit if we are too close to the back of the frustum, where the depth value is invalid. + float maxValidDepth = czm_currentFrustum.y - lengthCap; + if (-positionEC.z > maxValidDepth) { out_FragColor = vec4(1.0); return; } - vec3 normalEC = getNormalXEdge(positionEC); + vec3 normalEC = getNormalXEdge(positionEC.xyz); + float gaussianVariance = lengthCap * sqrt(-positionEC.z); + // Choose a step length such that the marching stops just before 3 * variance. + float stepLength = 3.0 * gaussianVariance / (float(stepCount) + 1.0); + float metersPerPixel = czm_metersPerPixel(positionEC, 1.0); + // Minimum step is 1 pixel to avoid double sampling + float pixelsPerStep = max(stepLength / metersPerPixel, 1.0); + stepLength = pixelsPerStep * metersPerPixel; - float ao = 0.0; - - const int ANGLE_STEPS = 4; - float angleStepScale = 1.0 / float(ANGLE_STEPS); + float angleStepScale = 1.0 / float(directionCount); float angleStep = angleStepScale * czm_twoPi; float cosStep = cos(angleStep); float sinStep = sin(angleStep); @@ -67,48 +85,59 @@ void main(void) float randomVal = texture(randomTexture, randomTexCoord).x; vec2 sampleDirection = vec2(cos(angleStep * randomVal), sin(angleStep * randomVal)); + float ao = 0.0; // Loop over sampling directions - for (int i = 0; i < ANGLE_STEPS; i++) +#if __VERSION__ == 300 + for (int i = 0; i < directionCount; i++) + { +#else + for (int i = 0; i < 16; i++) { + if (i >= directionCount) { + break; + } +#endif sampleDirection = rotateStep * sampleDirection; float localAO = 0.0; - vec2 radialStep = stepSize * sampleDirection; + vec2 radialStep = pixelsPerStep * sampleDirection; - for (int j = 0; j < 6; j++) +#if __VERSION__ == 300 + for (int j = 0; j < stepCount; j++) + { +#else + for (int j = 0; j < 64; j++) { + if (j >= stepCount) { + break; + } +#endif // Step along sampling direction, away from output pixel - vec2 newCoords = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5); + vec2 samplePixel = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5); // Exit if we stepped off the screen - if (clamp(newCoords, vec2(0.0), czm_viewport.zw) != newCoords) - { + if (clamp(samplePixel, vec2(0.0), czm_viewport.zw) != samplePixel) { break; } - vec3 stepPositionEC = pixelToEye(newCoords); - vec3 stepVector = stepPositionEC - positionEC; - float stepLength = length(stepVector); - - if (stepLength > lengthCap) - { - break; - } + // Compute step vector from output point to sampled point + vec4 samplePositionEC = pixelToEye(samplePixel); + vec3 stepVector = samplePositionEC.xyz - positionEC.xyz; + // Estimate the angle from the surface normal. float dotVal = clamp(dot(normalEC, normalize(stepVector)), 0.0, 1.0); - if (dotVal < bias) - { - dotVal = 0.0; - } + dotVal = czm_branchFreeTernary(dotVal > bias, dotVal, 0.0); + dotVal = czm_branchFreeTernary(-samplePositionEC.z <= maxValidDepth, dotVal, 0.0); - float weight = stepLength / lengthCap; - weight = 1.0 - weight * weight; - localAO = max(localAO, dotVal * weight); + // Weight contribution based on the distance from the output point + float sampleDistance = length(stepVector); + float weight = gaussian(sampleDistance, gaussianVariance); + localAO += weight * dotVal; } ao += localAO; } - ao *= angleStepScale; + ao *= angleStepScale * stepLength; ao = 1.0 - clamp(ao, 0.0, 1.0); ao = pow(ao, intensity); out_FragColor = vec4(vec3(ao), 1.0); diff --git a/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js b/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js index 3350a5a2eb54..5b6f4d040389 100644 --- a/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js +++ b/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js @@ -392,32 +392,23 @@ describe( expect(ao.uniforms.intensity).toEqual(3.0); expect(ao.uniforms.bias).toEqual(0.1); expect(ao.uniforms.lengthCap).toEqual(0.26); - expect(ao.uniforms.stepSize).toEqual(1.95); - expect(ao.uniforms.frustumLength).toEqual(1000.0); + expect(ao.uniforms.directionCount).toEqual(8); + expect(ao.uniforms.stepCount).toEqual(32); expect(ao.uniforms.randomTexture).not.toBeDefined(); - expect(ao.uniforms.delta).toEqual(1.0); - expect(ao.uniforms.sigma).toEqual(2.0); - expect(ao.uniforms.blurStepSize).toEqual(0.86); ao.uniforms.ambientOcclusionOnly = true; ao.uniforms.intensity = 4.0; ao.uniforms.bias = 0.2; ao.uniforms.lengthCap = 0.3; - ao.uniforms.stepSize = 2.0; - ao.uniforms.frustumLength = 1001.0; - ao.uniforms.delta = 2.0; - ao.uniforms.sigma = 3.0; - ao.uniforms.blurStepSize = 2.0; + ao.uniforms.directionCount = 9; + ao.uniforms.stepCount = 33; expect(ao.uniforms.ambientOcclusionOnly).toEqual(true); expect(ao.uniforms.intensity).toEqual(4.0); expect(ao.uniforms.bias).toEqual(0.2); expect(ao.uniforms.lengthCap).toEqual(0.3); - expect(ao.uniforms.stepSize).toEqual(2.0); - expect(ao.uniforms.frustumLength).toEqual(1001.0); - expect(ao.uniforms.delta).toEqual(2.0); - expect(ao.uniforms.sigma).toEqual(3.0); - expect(ao.uniforms.blurStepSize).toEqual(2.0); + expect(ao.uniforms.directionCount).toEqual(9); + expect(ao.uniforms.stepCount).toEqual(33); }); it("bloom", function () { diff --git a/packages/engine/package.json b/packages/engine/package.json index 8d242b4e4350..f95090f5176f 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/engine", - "version": "12.0.1", + "version": "13.0.0", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", diff --git a/packages/widgets/package.json b/packages/widgets/package.json index f980eee7f6db..eecf87e2728d 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/widgets", - "version": "9.0.1", + "version": "10.0.0", "description": "A widgets library for use with CesiumJS. CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", @@ -28,7 +28,7 @@ "node": ">=14.0.0" }, "dependencies": { - "@cesium/engine": "^12.0.1", + "@cesium/engine": "^13.0.0", "nosleep.js": "^0.12.0" }, "type": "module",