From 73146e09e7512dc279a7dcb2c65a03c9af313329 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 7 Dec 2023 11:19:38 -0500
Subject: [PATCH 01/55] Set up scaffolding for FogPipelineStage
---
.../Source/Scene/Model/FogPipelineStage.js | 18 ++++++++++++++++++
.../Scene/Model/ModelRuntimePrimitive.js | 2 ++
.../Source/Shaders/Model/FogStageFS.glsl | 17 +++++++++++++++++
.../Shaders/Model/ModelColorStageFS.glsl | 2 +-
.../engine/Source/Shaders/Model/ModelFS.glsl | 2 ++
5 files changed, 40 insertions(+), 1 deletion(-)
create mode 100644 packages/engine/Source/Scene/Model/FogPipelineStage.js
create mode 100644 packages/engine/Source/Shaders/Model/FogStageFS.glsl
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
new file mode 100644
index 000000000000..5f32ea71fe51
--- /dev/null
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -0,0 +1,18 @@
+import FogStageFS from "../../Shaders/Model/FogStageFS.js";
+
+/**
+ * The fog color pipeline stage is responsible for applying fog to tiles in the distance in horizon views.
+ *
+ * @namespace FogColorPipelineStage
+ *
+ * @private
+ */
+const FogColorPipelineStage = {
+ name: "FogColorPipelineStage", // Helps with debugging
+};
+
+FogColorPipelineStage.process = function (renderResources, model, frameState) {
+ renderResources.shaderBuilder.addFragmentLines(FogStageFS);
+};
+
+export default FogColorPipelineStage;
diff --git a/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js b/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
index 5593643409ea..824c22153be0 100644
--- a/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
+++ b/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
@@ -11,6 +11,7 @@ import CustomShaderMode from "./CustomShaderMode.js";
import CustomShaderPipelineStage from "./CustomShaderPipelineStage.js";
import DequantizationPipelineStage from "./DequantizationPipelineStage.js";
import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js";
+import FogPipelineStage from "./FogPipelineStage.js";
import GeometryPipelineStage from "./GeometryPipelineStage.js";
import LightingPipelineStage from "./LightingPipelineStage.js";
import MaterialPipelineStage from "./MaterialPipelineStage.js";
@@ -296,6 +297,7 @@ ModelRuntimePrimitive.prototype.configurePipeline = function (frameState) {
}
pipelineStages.push(AlphaPipelineStage);
+ pipelineStages.push(FogPipelineStage);
pipelineStages.push(PrimitiveStatisticsPipelineStage);
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
new file mode 100644
index 000000000000..188d979c5544
--- /dev/null
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -0,0 +1,17 @@
+void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
+ const vec4 FOG_COLOR = vec4(0.5, 0.0, 1.0, 1.0);
+
+ // Note: camera is far away (distance > nightFadeOutDistance), scattering is computed in the fragment shader.
+ // otherwise in the vertex shader. but for prototyping, I'll do everything in the FS for simplicity
+
+
+
+ // Matches the constant in GlobeFS.glsl. This makes the fog falloff
+ // more gradual.
+ const float fogModifier = 0.15;
+ float distanceToCamera = attributes.positionEC.z;
+ // where to get distance?
+ vec3 withFog = czm_fog(distanceToCamera, color.rgb, FOG_COLOR.rgb, fogModifier);
+
+ color = vec4(withFog, color.a);
+}
diff --git a/packages/engine/Source/Shaders/Model/ModelColorStageFS.glsl b/packages/engine/Source/Shaders/Model/ModelColorStageFS.glsl
index ad03772237e2..099fc0f6fcb6 100644
--- a/packages/engine/Source/Shaders/Model/ModelColorStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/ModelColorStageFS.glsl
@@ -4,4 +4,4 @@ void modelColorStage(inout czm_modelMaterial material)
float highlight = ceil(model_colorBlend);
material.diffuse *= mix(model_color.rgb, vec3(1.0), highlight);
material.alpha *= model_color.a;
-}
\ No newline at end of file
+}
diff --git a/packages/engine/Source/Shaders/Model/ModelFS.glsl b/packages/engine/Source/Shaders/Model/ModelFS.glsl
index f56da251646a..565013f1bc63 100644
--- a/packages/engine/Source/Shaders/Model/ModelFS.glsl
+++ b/packages/engine/Source/Shaders/Model/ModelFS.glsl
@@ -79,5 +79,7 @@ void main()
silhouetteStage(color);
#endif
+ fogStage(color, attributes);
+
out_FragColor = color;
}
From 35c7d9d797e0c70962f5748d91216a025d5d1dca Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 7 Dec 2023 16:56:41 -0500
Subject: [PATCH 02/55] Add atmosphere settings to FrameState and UniformState
---
.../engine/Source/Renderer/UniformState.js | 97 +++++++++++++++++++
packages/engine/Source/Scene/FrameState.js | 26 +++++
.../Source/Scene/Model/FogPipelineStage.js | 9 +-
.../Source/Shaders/Model/FogStageFS.glsl | 10 +-
4 files changed, 139 insertions(+), 3 deletions(-)
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index ffe2b972ecaa..dd9d3241624a 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -158,6 +158,14 @@ function UniformState() {
this._fogDensity = undefined;
+ this._atmosphereHsbShift = undefined;
+ this._atmosphereLightIntensity = undefined;
+ this._atmosphereRayleighCoefficient = new Cartesian3();
+ this._atmosphereRayleighScaleHeight = new Cartesian3();
+ this._atmosphereMieCoefficient = new Cartesian3();
+ this._atmosphereMieScaleHeight = undefined;
+ this._atmosphereMieAnisotropy = undefined;
+
this._invertClassificationColor = undefined;
this._splitPosition = 0.0;
@@ -865,6 +873,77 @@ Object.defineProperties(UniformState.prototype, {
},
},
+ /**
+ * A color shift to apply to the atmosphere color in HSB.
+ * @memberof UniformState.prototype
+ * @type {Cartesian3}
+ */
+ atmosphereHsbShift: {
+ get: function () {
+ return this._atmosphereHsbShift;
+ },
+ },
+ /**
+ * The intensity of the light that is used for computing the atmosphere color
+ * @memberof UniformState.prototype
+ * @type {number}
+ */
+ atmosphereLightIntensity: {
+ get: function () {
+ return this._atmosphereLightIntensity;
+ },
+ },
+ /**
+ * The Rayleigh scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
+ * @memberof UniformState.prototype
+ * @type {Cartesian3}
+ */
+ atmosphereRayleighCoefficient: {
+ get: function () {
+ return this._atmosphereRayleighCoefficient;
+ },
+ },
+ /**
+ * The Rayleigh scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
+ * @memberof UniformState.prototype
+ * @type {number}
+ */
+ atmosphereRayleighScaleHeight: {
+ get: function () {
+ return this._atmosphereRayleighScaleHeight;
+ },
+ },
+ /**
+ * The Mie scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
+ * @memberof UniformState.prototype
+ * @type {Cartesian3}
+ */
+ atmosphereMieCoefficient: {
+ get: function () {
+ return this._atmosphereMieCoefficient;
+ },
+ },
+ /**
+ * The Mie scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
+ * @memberof UniformState.prototype
+ * @type {number}
+ */
+ atmosphereMieScaleHeight: {
+ get: function () {
+ return this._atmosphereMieScaleHeight;
+ },
+ },
+ /**
+ * The anisotropy of the medium to consider for Mie scattering.
+ * @memberof UniformState.prototype
+ * @type {number}
+ */
+ atmosphereAnisotropy: {
+ get: function () {
+ return this._atmosphereAnisotropy;
+ },
+ },
+
/**
* A scalar that represents the geometric tolerance per meter
* @memberof UniformState.prototype
@@ -1294,6 +1373,24 @@ UniformState.prototype.update = function (frameState) {
this._fogDensity = frameState.fog.density;
+ this._atmosphereHsbShift = Cartesian3.clone(
+ frameState.atmosphere.hsbShift,
+ this._atmosphereHsbShift
+ );
+ this._atmosphereRayleighCoefficient = Cartesian3.clone(
+ frameState.atmosphere.rayleighCoefficient,
+ this._atmosphereRayleighCoefficient
+ );
+ this._atmosphereMieCoefficient = Cartesian3.clone(
+ frameState.atmosphere.mieCoefficient,
+ this._atmosphereMieCoefficient
+ );
+ this._atmospherelightIntensity = frameState.atmosphere.lightIntensity;
+ this._atmosphereRayleighScaleHeight =
+ frameState.atmosphere.rayleighScaleHeight;
+ this._atmosphereMieScaleHeight = frameState.atmosphere.mieScaleHeight;
+ this._atmosphereMieAnisotropy = frameState.atmosphere.mieAnisotropy;
+
this._invertClassificationColor = frameState.invertClassificationColor;
this._frameState = frameState;
diff --git a/packages/engine/Source/Scene/FrameState.js b/packages/engine/Source/Scene/FrameState.js
index 14b50cefe1d7..31172ab4b696 100644
--- a/packages/engine/Source/Scene/FrameState.js
+++ b/packages/engine/Source/Scene/FrameState.js
@@ -1,4 +1,5 @@
import SceneMode from "./SceneMode.js";
+import Cartesian3 from "../Core/Cartesian3.js";
/**
* State information about the current frame. An instance of this class
@@ -274,6 +275,31 @@ function FrameState(context, creditDisplay, jobScheduler) {
minimumBrightness: undefined,
};
+ /**
+ * @typedef FrameState.Atmosphere
+ * @type {object}
+ * @property {Cartesian3} hsbShift A color shift to apply to the atmosphere color in HSB.
+ * @property {number} lightIntensity The intensity of the light that is used for computing the atmosphere color
+ * @property {Cartesian3} rayleighCoefficient The Rayleigh scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
+ * @property {number} rayleighScaleHeight The Rayleigh scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
+ * @property {Cartesian3} mieCoefficient The Mie scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
+ * @property {number} mieScaleHeight The Mie scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
+ * @property {number} mieAnisotropy The anisotropy of the medium to consider for Mie scattering.
+ */
+
+ /**
+ * @type {FrameState.Atmosphere}
+ */
+ this.atmosphere = {
+ hsbShift: new Cartesian3(),
+ lightIntensity: undefined,
+ rayleighCoefficient: new Cartesian3(),
+ rayleighScaleHeight: undefined,
+ mieCoefficient: new Cartesian3(),
+ mieScaleHeight: undefined,
+ mieAnisotropy: undefined,
+ };
+
/**
* A scalar used to exaggerate the terrain.
* @type {number}
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
index 5f32ea71fe51..5471c143e76e 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -1,3 +1,4 @@
+import AtmosphereCommon from "../../Shaders/AtmosphereCommon.js";
import FogStageFS from "../../Shaders/Model/FogStageFS.js";
/**
@@ -12,7 +13,13 @@ const FogColorPipelineStage = {
};
FogColorPipelineStage.process = function (renderResources, model, frameState) {
- renderResources.shaderBuilder.addFragmentLines(FogStageFS);
+ // TODO: AtmosphereCommon.glsl includes uniforms that really should be
+ // added separately to match the Model pipeline paradigm... Maybe that file could
+ // be split into multiple files.
+ renderResources.shaderBuilder.addFragmentLines([
+ AtmosphereCommon,
+ FogStageFS,
+ ]);
};
export default FogColorPipelineStage;
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 188d979c5544..10e72cbee969 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -1,11 +1,17 @@
+vec3 computeFogColor() {
+ //vec4 groundAtmosphereColor = computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
+ //vec3 fogColor = groundAtmosphereColor.rgb;
+ return vec3(1.0);
+}
+
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
const vec4 FOG_COLOR = vec4(0.5, 0.0, 1.0, 1.0);
+ //vec3 fogColor = computeFogColor;
+
// Note: camera is far away (distance > nightFadeOutDistance), scattering is computed in the fragment shader.
// otherwise in the vertex shader. but for prototyping, I'll do everything in the FS for simplicity
-
-
// Matches the constant in GlobeFS.glsl. This makes the fog falloff
// more gradual.
const float fogModifier = 0.15;
From 8723127f45107de814b1bf2df314d1873a31cfe3 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 18 Dec 2023 11:40:36 -0500
Subject: [PATCH 03/55] Make builtins for common atmosphere functions
---
.../Source/Shaders/AtmosphereCommon.glsl | 2 +-
.../Builtin/Functions/approximateTanh.glsl | 10 ++
.../Functions/computeAtmosphereColor.glsl | 44 ++++++
.../Functions/computeEllipsoidPosition.glsl | 22 +++
.../computeGroundAtmosphereScattering.glsl | 30 ++++
.../Builtin/Functions/computeScattering.glsl | 149 ++++++++++++++++++
packages/engine/Source/Shaders/GlobeFS.glsl | 18 +--
.../Source/Shaders/Model/FogStageFS.glsl | 44 +++++-
8 files changed, 306 insertions(+), 13 deletions(-)
create mode 100644 packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl
create mode 100644 packages/engine/Source/Shaders/Builtin/Functions/computeAtmosphereColor.glsl
create mode 100644 packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
create mode 100644 packages/engine/Source/Shaders/Builtin/Functions/computeGroundAtmosphereScattering.glsl
create mode 100644 packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
diff --git a/packages/engine/Source/Shaders/AtmosphereCommon.glsl b/packages/engine/Source/Shaders/AtmosphereCommon.glsl
index 84acc5255659..e0e23eab78cb 100644
--- a/packages/engine/Source/Shaders/AtmosphereCommon.glsl
+++ b/packages/engine/Source/Shaders/AtmosphereCommon.glsl
@@ -66,7 +66,7 @@ void computeScattering(
// Value close to 0.0: close to the horizon
// Value close to 1.0: above in the sky
float w_stop_gt_lprl = 0.5 * (1.0 + approximateTanh(x));
-
+
// The ray should start from the first intersection with the outer atmopshere, or from the camera position, if it is inside the atmosphere.
float start_0 = primaryRayAtmosphereIntersect.start;
primaryRayAtmosphereIntersect.start = max(primaryRayAtmosphereIntersect.start, 0.0);
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl b/packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl
new file mode 100644
index 000000000000..f956b14274c3
--- /dev/null
+++ b/packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl
@@ -0,0 +1,10 @@
+/**
+ * Compute a rational approximation to tanh(x)
+ *
+ * @param {float} x A real number input
+ * @returns {float} An approximation for tanh(x)
+*/
+float czm_approximateTanh(float x) {
+ float x2 = x * x;
+ return max(-1.0, min(+1.0, x * (27.0 + x2) / (27.0 + 9.0 * x2)));
+}
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeAtmosphereColor.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeAtmosphereColor.glsl
new file mode 100644
index 000000000000..d4cc21971e7a
--- /dev/null
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeAtmosphereColor.glsl
@@ -0,0 +1,44 @@
+/**
+ * Compute the atmosphere color, applying Rayleigh and Mie scattering. This
+ * builtin uses automatic uniforms so the atmophere settings are synced with the
+ * state of the Scene, even in other contexts like Model.
+ *
+ * @name czm_computeAtmosphereColor
+ * @glslFunction
+ *
+ * @param {vec3} positionWC Position of the fragment in world coords (low precision)
+ * @param {vec3} lightDirection Light direction from the sun or other light source.
+ * @param {vec3} rayleighColor The Rayleigh scattering color computed by a scattering function
+ * @param {vec3} mieColor The Mie scattering color computed by a scattering function
+ * @param {float} opacity The opacity computed by a scattering function.
+ */
+vec4 czm_computeAtmosphereColor(
+ vec3 positionWC,
+ vec3 lightDirection,
+ vec3 rayleighColor,
+ vec3 mieColor,
+ float opacity
+) {
+ // Setup the primary ray: from the camera position to the vertex position.
+ vec3 cameraToPositionWC = positionWC - czm_viewerPositionWC;
+ vec3 cameraToPositionWCDirection = normalize(cameraToPositionWC);
+
+ float cosAngle = dot(cameraToPositionWCDirection, lightDirection);
+ float cosAngleSq = cosAngle * cosAngle;
+
+ float G = czm_atmosphereMieAnisotropy;
+ float GSq = G * G;
+
+ // The Rayleigh phase function.
+ float rayleighPhase = 3.0 / (50.2654824574) * (1.0 + cosAngleSq);
+ // The Mie phase function.
+ float miePhase = 3.0 / (25.1327412287) * ((1.0 - GSq) * (cosAngleSq + 1.0)) / (pow(1.0 + GSq - 2.0 * cosAngle * G, 1.5) * (2.0 + GSq));
+
+ // The final color is generated by combining the effects of the Rayleigh and Mie scattering.
+ vec3 rayleigh = rayleighPhase * rayleighColor;
+ vec3 mie = miePhase * mieColor;
+
+ vec3 color = (rayleigh + mie) * czm_atmosphereLightIntensity;
+
+ return vec4(color, opacity);
+}
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
new file mode 100644
index 000000000000..f11eea6caa73
--- /dev/null
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
@@ -0,0 +1,22 @@
+/**
+ * Compute the WC position on the elipsoid of the current fragment. The result
+ * is low-precision due to use of 32-bit floats.
+ *
+ * @return {vec3} The position in world coordinates.
+ */
+vec3 czm_computeEllipsoidPosition()
+{
+ float mpp = czm_metersPerPixel(vec4(0.0, 0.0, -czm_currentFrustum.x, 1.0), 1.0);
+ vec2 xy = gl_FragCoord.xy / czm_viewport.zw * 2.0 - vec2(1.0);
+ xy *= czm_viewport.zw * mpp * 0.5;
+
+ vec3 direction = normalize(vec3(xy, -czm_currentFrustum.x));
+ czm_ray ray = czm_ray(vec3(0.0), direction);
+
+ vec3 ellipsoid_center = czm_view[3].xyz;
+
+ czm_raySegment intersection = czm_rayEllipsoidIntersectionInterval(ray, ellipsoid_center, czm_ellipsoidInverseRadii);
+
+ vec3 ellipsoidPosition = czm_pointAlongRay(ray, intersection.start);
+ return (czm_inverseView * vec4(ellipsoidPosition, 1.0)).xyz;
+}
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeGroundAtmosphereScattering.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeGroundAtmosphereScattering.glsl
new file mode 100644
index 000000000000..d8b172e3b259
--- /dev/null
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeGroundAtmosphereScattering.glsl
@@ -0,0 +1,30 @@
+/**
+ * Compute atmosphere scattering for the ground atmosphere and fog. This method
+ * uses automatic uniforms so it is always synced with the scene settings.
+ *
+ * @name czm_computeGroundAtmosphereScattering
+ * @glslfunction
+ *
+ * @param {vec3} positionWC The position of the fragment in world coordinates.
+ * @param {vec3} lightDirection The direction of the light to calculate the scattering from.
+ * @param {vec3} rayleighColor The variable the Rayleigh scattering will be written to.
+ * @param {vec3} mieColor The variable the Mie scattering will be written to.
+ * @param {float} opacity The variable the transmittance will be written to.
+ */
+void czm_computeGroundAtmosphereScattering(vec3 positionWC, vec3 lightDirection, out vec3 rayleighColor, out vec3 mieColor, out float opacity) {
+ vec3 cameraToPositionWC = positionWC - czm_viewerPositionWC;
+ vec3 cameraToPositionWCDirection = normalize(cameraToPositionWC);
+ czm_ray primaryRay = czm_ray(czm_viewerPositionWC, cameraToPositionWCDirection);
+
+ float atmosphereInnerRadius = length(positionWC);
+
+ czm_computeScattering(
+ primaryRay,
+ length(cameraToPositionWC),
+ lightDirection,
+ atmosphereInnerRadius,
+ rayleighColor,
+ mieColor,
+ opacity
+ );
+}
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
new file mode 100644
index 000000000000..46dee64c07cc
--- /dev/null
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
@@ -0,0 +1,149 @@
+/**
+ * This function computes the colors contributed by Rayliegh and Mie scattering on a given ray, as well as
+ * the transmittance value for the ray. This function uses automatic uniforms
+ * so the atmosphere settings are always synced with the current scene.
+ *
+ * @name czm_computeScattering
+ * @glslfunction
+ *
+ * @param {czm_ray} primaryRay The ray from the camera to the position.
+ * @param {float} primaryRayLength The length of the primary ray.
+ * @param {vec3} lightDirection The direction of the light to calculate the scattering from.
+ * @param {vec3} rayleighColor The variable the Rayleigh scattering will be written to.
+ * @param {vec3} mieColor The variable the Mie scattering will be written to.
+ * @param {float} opacity The variable the transmittance will be written to.
+ */
+void czm_computeScattering(
+ czm_ray primaryRay,
+ float primaryRayLength,
+ vec3 lightDirection,
+ float atmosphereInnerRadius,
+ out vec3 rayleighColor,
+ out vec3 mieColor,
+ out float opacity
+) {
+ const float ATMOSPHERE_THICKNESS = 111e3; // The thickness of the atmosphere in meters.
+ const int PRIMARY_STEPS_MAX = 16; // Maximum number of times the ray from the camera to the world position (primary ray) is sampled.
+ const int LIGHT_STEPS_MAX = 4; // Maximum number of times the light is sampled from the light source's intersection with the atmosphere to a sample position on the primary ray.
+
+ // Initialize the default scattering amounts to 0.
+ rayleighColor = vec3(0.0);
+ mieColor = vec3(0.0);
+ opacity = 0.0;
+
+ float atmosphereOuterRadius = atmosphereInnerRadius + ATMOSPHERE_THICKNESS;
+
+ vec3 origin = vec3(0.0);
+
+ // Calculate intersection from the camera to the outer ring of the atmosphere.
+ czm_raySegment primaryRayAtmosphereIntersect = czm_raySphereIntersectionInterval(primaryRay, origin, atmosphereOuterRadius);
+
+ // Return empty colors if no intersection with the atmosphere geometry.
+ if (primaryRayAtmosphereIntersect == czm_emptyRaySegment) {
+ return;
+ }
+
+ // To deal with smaller values of PRIMARY_STEPS (e.g. 4)
+ // we implement a split strategy: sky or horizon.
+ // For performance reasons, instead of a if/else branch
+ // a soft choice is implemented through a weight 0.0 <= w_stop_gt_lprl <= 1.0
+ float x = 1e-7 * primaryRayAtmosphereIntersect.stop / length(primaryRayLength);
+ // Value close to 0.0: close to the horizon
+ // Value close to 1.0: above in the sky
+ float w_stop_gt_lprl = 0.5 * (1.0 + czm_approximateTanh(x));
+
+ // The ray should start from the first intersection with the outer atmopshere, or from the camera position, if it is inside the atmosphere.
+ float start_0 = primaryRayAtmosphereIntersect.start;
+ primaryRayAtmosphereIntersect.start = max(primaryRayAtmosphereIntersect.start, 0.0);
+ // The ray should end at the exit from the atmosphere or at the distance to the vertex, whichever is smaller.
+ primaryRayAtmosphereIntersect.stop = min(primaryRayAtmosphereIntersect.stop, length(primaryRayLength));
+
+ // For the number of ray steps, distinguish inside or outside atmosphere (outer space)
+ // (1) from outer space we have to use more ray steps to get a realistic rendering
+ // (2) within atmosphere we need fewer steps for faster rendering
+ float x_o_a = start_0 - ATMOSPHERE_THICKNESS; // ATMOSPHERE_THICKNESS used as an ad-hoc constant, no precise meaning here, only the order of magnitude matters
+ float w_inside_atmosphere = 1.0 - 0.5 * (1.0 + czm_approximateTanh(x_o_a));
+ int PRIMARY_STEPS = PRIMARY_STEPS_MAX - int(w_inside_atmosphere * 12.0); // Number of times the ray from the camera to the world position (primary ray) is sampled.
+ int LIGHT_STEPS = LIGHT_STEPS_MAX - int(w_inside_atmosphere * 2.0); // Number of times the light is sampled from the light source's intersection with the atmosphere to a sample position on the primary ray.
+
+ // Setup for sampling positions along the ray - starting from the intersection with the outer ring of the atmosphere.
+ float rayPositionLength = primaryRayAtmosphereIntersect.start;
+ // (1) Outside the atmosphere: constant rayStepLength
+ // (2) Inside atmosphere: variable rayStepLength to compensate the rough rendering of the smaller number of ray steps
+ float totalRayLength = primaryRayAtmosphereIntersect.stop - rayPositionLength;
+ float rayStepLengthIncrease = w_inside_atmosphere * ((1.0 - w_stop_gt_lprl) * totalRayLength / (float(PRIMARY_STEPS * (PRIMARY_STEPS + 1)) / 2.0));
+ float rayStepLength = max(1.0 - w_inside_atmosphere, w_stop_gt_lprl) * totalRayLength / max(7.0 * w_inside_atmosphere, float(PRIMARY_STEPS));
+
+ vec3 rayleighAccumulation = vec3(0.0);
+ vec3 mieAccumulation = vec3(0.0);
+ vec2 opticalDepth = vec2(0.0);
+ vec2 heightScale = vec2(czm_atmosphereRayleighScaleHeight, czm_atmosphereMieScaleHeight);
+
+ // Sample positions on the primary ray.
+ for (int i = 0; i < PRIMARY_STEPS_MAX; ++i) {
+
+ // The loop should be: for (int i = 0; i < PRIMARY_STEPS; ++i) {...} but WebGL1 cannot
+ // loop with non-constant condition, so it has to break early instead
+ if (i >= PRIMARY_STEPS) {
+ break;
+ }
+
+ // Calculate sample position along viewpoint ray.
+ vec3 samplePosition = primaryRay.origin + primaryRay.direction * (rayPositionLength + rayStepLength);
+
+ // Calculate height of sample position above ellipsoid.
+ float sampleHeight = length(samplePosition) - atmosphereInnerRadius;
+
+ // Calculate and accumulate density of particles at the sample position.
+ vec2 sampleDensity = exp(-sampleHeight / heightScale) * rayStepLength;
+ opticalDepth += sampleDensity;
+
+ // Generate ray from the sample position segment to the light source, up to the outer ring of the atmosphere.
+ czm_ray lightRay = czm_ray(samplePosition, lightDirection);
+ czm_raySegment lightRayAtmosphereIntersect = czm_raySphereIntersectionInterval(lightRay, origin, atmosphereOuterRadius);
+
+ float lightStepLength = lightRayAtmosphereIntersect.stop / float(LIGHT_STEPS);
+ float lightPositionLength = 0.0;
+
+ vec2 lightOpticalDepth = vec2(0.0);
+
+ // Sample positions along the light ray, to accumulate incidence of light on the latest sample segment.
+ for (int j = 0; j < LIGHT_STEPS_MAX; ++j) {
+
+ // The loop should be: for (int j = 0; i < LIGHT_STEPS; ++j) {...} but WebGL1 cannot
+ // loop with non-constant condition, so it has to break early instead
+ if (j >= LIGHT_STEPS) {
+ break;
+ }
+
+ // Calculate sample position along light ray.
+ vec3 lightPosition = samplePosition + lightDirection * (lightPositionLength + lightStepLength * 0.5);
+
+ // Calculate height of the light sample position above ellipsoid.
+ float lightHeight = length(lightPosition) - atmosphereInnerRadius;
+
+ // Calculate density of photons at the light sample position.
+ lightOpticalDepth += exp(-lightHeight / heightScale) * lightStepLength;
+
+ // Increment distance on light ray.
+ lightPositionLength += lightStepLength;
+ }
+
+ // Compute attenuation via the primary ray and the light ray.
+ vec3 attenuation = exp(-((czm_atmosphereMieCoefficient * (opticalDepth.y + lightOpticalDepth.y)) + (czm_atmosphereRayleighCoefficient * (opticalDepth.x + lightOpticalDepth.x))));
+
+ // Accumulate the scattering.
+ rayleighAccumulation += sampleDensity.x * attenuation;
+ mieAccumulation += sampleDensity.y * attenuation;
+
+ // Increment distance on primary ray.
+ rayPositionLength += (rayStepLength += rayStepLengthIncrease);
+ }
+
+ // Compute the scattering amount.
+ rayleighColor = czm_atmosphereRayleighCoefficient * rayleighAccumulation;
+ mieColor = czm_atmosphereMieCoefficient * mieAccumulation;
+
+ // Compute the transmittance i.e. how much light is passing through the atmosphere.
+ opacity = length(exp(-((czm_atmosphereMieCoefficient * opticalDepth.y) + (czm_atmosphereRayleighCoefficient * opticalDepth.x))));
+}
diff --git a/packages/engine/Source/Shaders/GlobeFS.glsl b/packages/engine/Source/Shaders/GlobeFS.glsl
index bde6ce15c82c..3d181e60ee0c 100644
--- a/packages/engine/Source/Shaders/GlobeFS.glsl
+++ b/packages/engine/Source/Shaders/GlobeFS.glsl
@@ -396,7 +396,7 @@ void main()
materialInput.st = v_textureCoordinates.st;
materialInput.normalEC = normalize(v_normalEC);
materialInput.positionToEyeEC = -v_positionEC;
- materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, normalize(v_normalEC));
+ materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, normalize(v_normalEC));
materialInput.slope = v_slope;
materialInput.height = v_height;
materialInput.aspect = v_aspect;
@@ -442,7 +442,7 @@ void main()
{
bool dynamicLighting = false;
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_DAYNIGHT_SHADING) || defined(ENABLE_VERTEX_LIGHTING))
- dynamicLighting = true;
+ dynamicLighting = true;
#endif
vec3 rayleighColor;
@@ -480,18 +480,18 @@ void main()
// Fog is applied to tiles selected for fog, close to the Earth.
#ifdef FOG
vec3 fogColor = groundAtmosphereColor.rgb;
-
+
// If there is lighting, apply that to the fog.
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_VERTEX_LIGHTING) || defined(ENABLE_DAYNIGHT_SHADING))
float darken = clamp(dot(normalize(czm_viewerPositionWC), atmosphereLightDirection), u_minimumBrightness, 1.0);
- fogColor *= darken;
+ fogColor *= darken;
#endif
#ifndef HDR
fogColor.rgb = czm_acesTonemapping(fogColor.rgb);
fogColor.rgb = czm_inverseGamma(fogColor.rgb);
#endif
-
+
const float modifier = 0.15;
finalColor = vec4(czm_fog(v_distance, finalColor.rgb, fogColor.rgb, modifier), finalColor.a);
@@ -507,20 +507,20 @@ void main()
#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_VERTEX_LIGHTING) || defined(ENABLE_DAYNIGHT_SHADING))
float fadeInDist = u_nightFadeDistance.x;
float fadeOutDist = u_nightFadeDistance.y;
-
+
float sunlitAtmosphereIntensity = clamp((cameraDist - fadeOutDist) / (fadeInDist - fadeOutDist), 0.05, 1.0);
float darken = clamp(dot(normalize(positionWC), atmosphereLightDirection), 0.0, 1.0);
vec3 darkenendGroundAtmosphereColor = mix(groundAtmosphereColor.rgb, finalAtmosphereColor.rgb, darken);
finalAtmosphereColor = mix(darkenendGroundAtmosphereColor, finalAtmosphereColor, sunlitAtmosphereIntensity);
#endif
-
+
#ifndef HDR
finalAtmosphereColor.rgb = vec3(1.0) - exp(-fExposure * finalAtmosphereColor.rgb);
#else
finalAtmosphereColor.rgb = czm_saturation(finalAtmosphereColor.rgb, 1.6);
#endif
-
+
finalColor.rgb = mix(finalColor.rgb, finalAtmosphereColor.rgb, fade);
#endif
}
@@ -544,7 +544,7 @@ void main()
finalColor.a *= interpolateByDistance(alphaByDistance, v_distance);
}
#endif
-
+
out_FragColor = finalColor;
}
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 10e72cbee969..b8d037ab61b1 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -1,13 +1,51 @@
vec3 computeFogColor() {
- //vec4 groundAtmosphereColor = computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
- //vec3 fogColor = groundAtmosphereColor.rgb;
+
+//#if defined(DYNAMIC_ATMOSPHERE_LIGHTING_FROM_SUN)
+ vec3 atmosphereLightDirection = czm_sunDirectionWC;
+//#else
+// vec3 atmosphereLightDirection = czm_lightDirectionWC;
+//#endif
+
+ vec3 rayleighColor;
+ vec3 mieColor;
+ float opacity;
+
+ vec3 positionWC;
+ vec3 lightDirection;
+
+ positionWC = computeEllipsoidPosition();
+ lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(positionWC));
+
+ // This is true when dynamic lighting is enabled in the scene.
+ bool dynamicLighting = false;
+
+ // The fog color is derived from the ground atmosphere color
+ czm_computeGroundAtmosphereScattering(
+ positionWC,
+ lightDirection,
+ rayleighColor,
+ mieColor,
+ opacity
+ );
+
+ vec4 groundAtmosphereColor = czm_computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
+ vec3 fogColor = groundAtmosphereColor.rgb;
+
+ // Darken the fog
+
+ // Tonemap if HDR rendering is disabled
+#ifndef HDR
+ fogColor.rgb = czm_acesTonemapping(fogColor.rgb);
+ fogColor.rgb = czm_inverseGamma(fogColor.rgb);
+#endif
+
return vec3(1.0);
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
const vec4 FOG_COLOR = vec4(0.5, 0.0, 1.0, 1.0);
- //vec3 fogColor = computeFogColor;
+ vec3 fogColor = computeFogColor();
// Note: camera is far away (distance > nightFadeOutDistance), scattering is computed in the fragment shader.
// otherwise in the vertex shader. but for prototyping, I'll do everything in the FS for simplicity
From bf1890cecd41bc18bb8213fb600c1848215be2ff Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 18 Dec 2023 12:40:41 -0500
Subject: [PATCH 04/55] Get a barebones atmosphere shader compiling
---
.../Source/Renderer/AutomaticUniforms.js | 92 +++++++++++++++++++
.../engine/Source/Renderer/UniformState.js | 4 +-
.../Source/Shaders/Model/FogStageFS.glsl | 10 +-
3 files changed, 98 insertions(+), 8 deletions(-)
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index a3304ee874b4..f9dabf17763b 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -1538,6 +1538,98 @@ const AutomaticUniforms = {
},
}),
+ /**
+ * An automatic uniform representing the color shift for the atmosphere in HSB color space
+ *
+ * @example
+ * uniform vec3 czm_atmosphereHsbShift;
+ */
+ czm_atmosphereHsbShift: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT_VEC3,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereHsbShift;
+ },
+ }),
+ /**
+ * An automatic uniform representing the intensity of the light that is used for computing the atmosphere color
+ *
+ * @example
+ * uniform float czm_atmosphereLightIntensity;
+ */
+ czm_atmosphereLightIntensity: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereLightIntensity;
+ },
+ }),
+ /**
+ * An automatic uniform representing the Rayleigh scattering coefficient used when computing the atmosphere scattering
+ *
+ * @example
+ * uniform vec3 czm_atmosphereRayleighCoefficient;
+ */
+ czm_atmosphereRayleighCoefficient: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT_VEC3,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereRayleighCoefficient;
+ },
+ }),
+ /**
+ * An automatic uniform representing the Rayleigh scale height in meters used for computing atmosphere scattering.
+ *
+ * @example
+ * uniform vec3 czm_atmosphereRayleighScaleHeight;
+ */
+ czm_atmosphereRayleighScaleHeight: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereRayleighScaleHeight;
+ },
+ }),
+ /**
+ * An automatic uniform representing the Mie scattering coefficient used when computing atmosphere scattering.
+ *
+ * @example
+ * uniform vec3 czm_atmosphereMieCoefficient;
+ */
+ czm_atmosphereMieCoefficient: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT_VEC3,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereMieCoefficient;
+ },
+ }),
+ /**
+ * An automatic uniform storign the Mie scale height used when computing atmosphere scattering.
+ *
+ * @example
+ * uniform float czm_atmosphereMieScaleHeight;
+ */
+ czm_atmosphereMieScaleHeight: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereMieScaleHeight;
+ },
+ }),
+ /**
+ * An automatic uniform representing the anisotropy of the medium to consider for Mie scattering.
+ *
+ * @example
+ * uniform float czm_atmosphereAnisotropy;
+ */
+ czm_atmosphereMieAnisotropy: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereAnisotropy;
+ },
+ }),
+
/**
* An automatic GLSL uniform representing the splitter position to use when rendering with a splitter.
* This will be in pixel coordinates relative to the canvas.
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index dd9d3241624a..d30837b3a89a 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -938,9 +938,9 @@ Object.defineProperties(UniformState.prototype, {
* @memberof UniformState.prototype
* @type {number}
*/
- atmosphereAnisotropy: {
+ atmosphereMieAnisotropy: {
get: function () {
- return this._atmosphereAnisotropy;
+ return this._atmosphereMieAnisotropy;
},
},
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index b8d037ab61b1..77160803c26e 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -13,12 +13,12 @@ vec3 computeFogColor() {
vec3 positionWC;
vec3 lightDirection;
- positionWC = computeEllipsoidPosition();
- lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(positionWC));
-
// This is true when dynamic lighting is enabled in the scene.
bool dynamicLighting = false;
+ positionWC = czm_computeEllipsoidPosition();
+ lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(positionWC));
+
// The fog color is derived from the ground atmosphere color
czm_computeGroundAtmosphereScattering(
positionWC,
@@ -43,8 +43,6 @@ vec3 computeFogColor() {
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
- const vec4 FOG_COLOR = vec4(0.5, 0.0, 1.0, 1.0);
-
vec3 fogColor = computeFogColor();
// Note: camera is far away (distance > nightFadeOutDistance), scattering is computed in the fragment shader.
@@ -55,7 +53,7 @@ void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
const float fogModifier = 0.15;
float distanceToCamera = attributes.positionEC.z;
// where to get distance?
- vec3 withFog = czm_fog(distanceToCamera, color.rgb, FOG_COLOR.rgb, fogModifier);
+ vec3 withFog = czm_fog(distanceToCamera, color.rgb, fogColor, fogModifier);
color = vec4(withFog, color.a);
}
From 0634b5309f019b39eafc6786a33cfc376575156a Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 18 Dec 2023 14:22:55 -0500
Subject: [PATCH 05/55] Create Atmosphere object to propagate uniforms
---
packages/engine/Source/Scene/Atmosphere.js | 98 +++++++++++++++++++
packages/engine/Source/Scene/Globe.js | 1 +
.../Source/Scene/Model/FogPipelineStage.js | 6 +-
packages/engine/Source/Scene/Scene.js | 4 +
.../Source/Shaders/Model/FogStageFS.glsl | 2 +-
5 files changed, 105 insertions(+), 6 deletions(-)
create mode 100644 packages/engine/Source/Scene/Atmosphere.js
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
new file mode 100644
index 000000000000..9d9c52c7b174
--- /dev/null
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -0,0 +1,98 @@
+import Cartesian3 from "../Core/Cartesian3";
+
+function Atmosphere() {
+ /**
+ * The intensity of the light that is used for computing the ground atmosphere color.
+ *
+ * @type {number}
+ * @default 10.0
+ */
+ this.lightIntensity = 10.0;
+
+ /**
+ * The Rayleigh scattering coefficient used in the atmospheric scattering equations for the ground atmosphere.
+ *
+ * @type {Cartesian3}
+ * @default Cartesian3(5.5e-6, 13.0e-6, 28.4e-6)
+ */
+ this.rayleighCoefficient = new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6);
+
+ /**
+ * The Mie scattering coefficient used in the atmospheric scattering equations for the ground atmosphere.
+ *
+ * @type {Cartesian3}
+ * @default Cartesian3(21e-6, 21e-6, 21e-6)
+ */
+ this.mieCoefficient = new Cartesian3(21e-6, 21e-6, 21e-6);
+
+ /**
+ * The Rayleigh scale height used in the atmospheric scattering equations for the ground atmosphere, in meters.
+ *
+ * @type {number}
+ * @default 10000.0
+ */
+ this.rayleighScaleHeight = 10000.0;
+
+ /**
+ * The Mie scale height used in the atmospheric scattering equations for the ground atmosphere, in meters.
+ *
+ * @type {number}
+ * @default 3200.0
+ */
+ this.mieScaleHeight = 3200.0;
+
+ /**
+ * The anisotropy of the medium to consider for Mie scattering.
+ *
+ * Valid values are between -1.0 and 1.0.
+ *
+ * @type {number}
+ * @default 0.9
+ */
+ this.mieAnisotropy = 0.9;
+
+ /**
+ * The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift).
+ * A hue shift of 1.0 indicates a complete rotation of the hues available.
+ * @type {number}
+ * @default 0.0
+ */
+ this.hueShift = 0.0;
+
+ /**
+ * The saturation shift to apply to the atmosphere. Defaults to 0.0 (no shift).
+ * A saturation shift of -1.0 is monochrome.
+ * @type {number}
+ * @default 0.0
+ */
+ this.saturationShift = 0.0;
+
+ /**
+ * The brightness shift to apply to the atmosphere. Defaults to 0.0 (no shift).
+ * A brightness shift of -1.0 is complete darkness, which will let space show through.
+ * @type {number}
+ * @default 0.0
+ */
+ this.brightnessShift = 0.0;
+}
+
+Atmosphere.prototype.update = function (frameState) {
+ const atmosphere = frameState.atmosphere;
+ atmosphere.hsbShift.x = this.hueShift;
+ atmosphere.hsbShift.y = this.saturationShift;
+ atmosphere.hsbShift.z = this.brightnessShift;
+ atmosphere.lightIntensity = this.lightIntensity;
+ atmosphere.rayleighCoefficient = Cartesian3.clone(
+ this.rayleighCoefficient,
+ atmosphere.rayleighCoefficient
+ );
+ atmosphere.rayleightScaleHeight = this.rayleighScaleHeight;
+ atmosphere.mieCoefficient = Cartesian3.clone(
+ this.mieCoefficient,
+ atmosphere.mieCoefficient
+ );
+ atmosphere.mieScaleHeight = this.mieScaleHeight;
+ atmosphere.mieAnisotropy = this.mieAnisotropy;
+};
+
+export default Atmosphere;
diff --git a/packages/engine/Source/Scene/Globe.js b/packages/engine/Source/Scene/Globe.js
index 35dd3ea498cf..898457eb1922 100644
--- a/packages/engine/Source/Scene/Globe.js
+++ b/packages/engine/Source/Scene/Globe.js
@@ -1059,6 +1059,7 @@ Globe.prototype.beginFrame = function (frameState) {
tileProvider.undergroundColor = this._undergroundColor;
tileProvider.undergroundColorAlphaByDistance = this._undergroundColorAlphaByDistance;
tileProvider.lambertDiffuseMultiplier = this.lambertDiffuseMultiplier;
+
surface.beginFrame(frameState);
}
};
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
index 5471c143e76e..c9d902e74b76 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -1,4 +1,3 @@
-import AtmosphereCommon from "../../Shaders/AtmosphereCommon.js";
import FogStageFS from "../../Shaders/Model/FogStageFS.js";
/**
@@ -16,10 +15,7 @@ FogColorPipelineStage.process = function (renderResources, model, frameState) {
// TODO: AtmosphereCommon.glsl includes uniforms that really should be
// added separately to match the Model pipeline paradigm... Maybe that file could
// be split into multiple files.
- renderResources.shaderBuilder.addFragmentLines([
- AtmosphereCommon,
- FogStageFS,
- ]);
+ renderResources.shaderBuilder.addFragmentLines([FogStageFS]);
};
export default FogColorPipelineStage;
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index 127c6aacfaad..70a98676b358 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -37,6 +37,7 @@ import Context from "../Renderer/Context.js";
import ContextLimits from "../Renderer/ContextLimits.js";
import Pass from "../Renderer/Pass.js";
import RenderState from "../Renderer/RenderState.js";
+import Atmosphere from "./Atmosphere.js";
import BrdfLutGenerator from "./BrdfLutGenerator.js";
import Camera from "./Camera.js";
import Cesium3DTilePass from "./Cesium3DTilePass.js";
@@ -494,6 +495,8 @@ function Scene(options) {
*/
this.cameraEventWaitTime = 500.0;
+ this.atmosphere = new Atmosphere();
+
/**
* Blends the atmosphere to geometry far from the camera for horizon views. Allows for additional
* performance improvements by rendering less geometry and dispatching less terrain requests.
@@ -3715,6 +3718,7 @@ function render(scene) {
}
frameState.backgroundColor = backgroundColor;
+ scene.atmosphere.update(frameState);
scene.fog.update(frameState);
us.update(frameState);
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 77160803c26e..c32c27f4ecd4 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -39,7 +39,7 @@ vec3 computeFogColor() {
fogColor.rgb = czm_inverseGamma(fogColor.rgb);
#endif
- return vec3(1.0);
+ return fogColor.rgb;
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
From d21e7bd1ee6555a40b21855eca818637cf1df119 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 18 Dec 2023 15:52:55 -0500
Subject: [PATCH 06/55] Fix typos in uniform names
---
packages/engine/Source/Renderer/AutomaticUniforms.js | 2 +-
packages/engine/Source/Renderer/UniformState.js | 6 +++---
packages/engine/Source/Scene/Atmosphere.js | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index f9dabf17763b..1662bf472ee3 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -1626,7 +1626,7 @@ const AutomaticUniforms = {
size: 1,
datatype: WebGLConstants.FLOAT,
getValue: function (uniformState) {
- return uniformState.atmosphereAnisotropy;
+ return uniformState.atmosphereMieAnisotropy;
},
}),
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index d30837b3a89a..b2f5ad412c9e 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -1377,17 +1377,17 @@ UniformState.prototype.update = function (frameState) {
frameState.atmosphere.hsbShift,
this._atmosphereHsbShift
);
+ this._atmosphereLightIntensity = frameState.atmosphere.lightIntensity;
this._atmosphereRayleighCoefficient = Cartesian3.clone(
frameState.atmosphere.rayleighCoefficient,
this._atmosphereRayleighCoefficient
);
+ this._atmosphereRayleighScaleHeight =
+ frameState.atmosphere.rayleighScaleHeight;
this._atmosphereMieCoefficient = Cartesian3.clone(
frameState.atmosphere.mieCoefficient,
this._atmosphereMieCoefficient
);
- this._atmospherelightIntensity = frameState.atmosphere.lightIntensity;
- this._atmosphereRayleighScaleHeight =
- frameState.atmosphere.rayleighScaleHeight;
this._atmosphereMieScaleHeight = frameState.atmosphere.mieScaleHeight;
this._atmosphereMieAnisotropy = frameState.atmosphere.mieAnisotropy;
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index 9d9c52c7b174..010904e97961 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -86,7 +86,7 @@ Atmosphere.prototype.update = function (frameState) {
this.rayleighCoefficient,
atmosphere.rayleighCoefficient
);
- atmosphere.rayleightScaleHeight = this.rayleighScaleHeight;
+ atmosphere.rayleighScaleHeight = this.rayleighScaleHeight;
atmosphere.mieCoefficient = Cartesian3.clone(
this.mieCoefficient,
atmosphere.mieCoefficient
From 092f91e5e09f88df321f5a1fefaf97bdb2ccf79d Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 20 Dec 2023 09:46:17 -0500
Subject: [PATCH 07/55] Temporary debugging code
---
.../Builtin/Functions/computeScattering.glsl | 12 +++++++++++
packages/engine/Source/Shaders/GlobeFS.glsl | 2 ++
.../Source/Shaders/Model/FogStageFS.glsl | 20 +++++++++++++++++--
3 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
index 46dee64c07cc..63cef0c248eb 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
@@ -40,6 +40,7 @@ void czm_computeScattering(
// Return empty colors if no intersection with the atmosphere geometry.
if (primaryRayAtmosphereIntersect == czm_emptyRaySegment) {
+ rayleighColor = vec3(1.0, 0.0, 1.0);
return;
}
@@ -79,6 +80,8 @@ void czm_computeScattering(
vec2 opticalDepth = vec2(0.0);
vec2 heightScale = vec2(czm_atmosphereRayleighScaleHeight, czm_atmosphereMieScaleHeight);
+ //vec3 lastVals = vec3(0.0);
+
// Sample positions on the primary ray.
for (int i = 0; i < PRIMARY_STEPS_MAX; ++i) {
@@ -127,11 +130,16 @@ void czm_computeScattering(
// Increment distance on light ray.
lightPositionLength += lightStepLength;
+
+ //lastVals = vec3(length(lightPosition));
+ //lastVals = vec3(float(lightHeight < 0.0), lightHeight / 1000.0, lightOpticalDepth);
}
// Compute attenuation via the primary ray and the light ray.
vec3 attenuation = exp(-((czm_atmosphereMieCoefficient * (opticalDepth.y + lightOpticalDepth.y)) + (czm_atmosphereRayleighCoefficient * (opticalDepth.x + lightOpticalDepth.x))));
+ //lastAttenuation = vec3(rayStepLength, lightStepLength);
+
// Accumulate the scattering.
rayleighAccumulation += sampleDensity.x * attenuation;
mieAccumulation += sampleDensity.y * attenuation;
@@ -146,4 +154,8 @@ void czm_computeScattering(
// Compute the transmittance i.e. how much light is passing through the atmosphere.
opacity = length(exp(-((czm_atmosphereMieCoefficient * opticalDepth.y) + (czm_atmosphereRayleighCoefficient * opticalDepth.x))));
+
+ //rayleighColor = vec3(atmosphereInnerRadius / 1.0e7, lastVals.x / 1.0e7, 0.0); //lastVals;
+ //vec3(float(PRIMARY_STEPS) / 16.0, float(LIGHT_STEPS) / 4.0, 0.0);//mieAccumulation; //rayleighAccumulation;
+ //rayleighColor = w_stop_gt_lprl /*w_inside_atmosphere*/ * vec3(1.0, 1.0, 0.0); //w_inside_atmosphere, 0.0);
}
diff --git a/packages/engine/Source/Shaders/GlobeFS.glsl b/packages/engine/Source/Shaders/GlobeFS.glsl
index 3d181e60ee0c..79d5960159ee 100644
--- a/packages/engine/Source/Shaders/GlobeFS.glsl
+++ b/packages/engine/Source/Shaders/GlobeFS.glsl
@@ -523,6 +523,8 @@ void main()
finalColor.rgb = mix(finalColor.rgb, finalAtmosphereColor.rgb, fade);
#endif
+
+ //finalColor.rgb = computeEllipsoidPosition() / 1e7;
}
#endif
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index c32c27f4ecd4..dd2da6c7d767 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -6,7 +6,7 @@ vec3 computeFogColor() {
// vec3 atmosphereLightDirection = czm_lightDirectionWC;
//#endif
- vec3 rayleighColor;
+ vec3 rayleighColor = vec3(0.0, 0.0, 1.0);
vec3 mieColor;
float opacity;
@@ -14,7 +14,7 @@ vec3 computeFogColor() {
vec3 lightDirection;
// This is true when dynamic lighting is enabled in the scene.
- bool dynamicLighting = false;
+ bool dynamicLighting = true;
positionWC = czm_computeEllipsoidPosition();
lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(positionWC));
@@ -28,18 +28,33 @@ vec3 computeFogColor() {
opacity
);
+ //rayleighColor = vec3(1.0, 0.0, 0.0);
+ //mieColor = vec3(0.0, 1.0, 0.0);
+
vec4 groundAtmosphereColor = czm_computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
vec3 fogColor = groundAtmosphereColor.rgb;
// Darken the fog
+ // If there is lighting, apply that to the fog.
+//#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_VERTEX_LIGHTING) || defined(ENABLE_DAYNIGHT_SHADING))
+ //const float u_minimumBrightness = 0.03; // TODO: pull this from the light shader
+ //float darken = clamp(dot(normalize(czm_viewerPositionWC), atmosphereLightDirection), u_minimumBrightness, 1.0);
+ //fogColor *= darken;
+//#endif
+
// Tonemap if HDR rendering is disabled
#ifndef HDR
fogColor.rgb = czm_acesTonemapping(fogColor.rgb);
fogColor.rgb = czm_inverseGamma(fogColor.rgb);
#endif
+ // TODO: fogColor.a is only used for ground atmosphere... is that needed?
+
+ //return positionWC / 1e7;
+ //return rayleighColor;
return fogColor.rgb;
+ //return mieColor;
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
@@ -56,4 +71,5 @@ void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
vec3 withFog = czm_fog(distanceToCamera, color.rgb, fogColor, fogModifier);
color = vec4(withFog, color.a);
+ //color = mix(color, vec4(fogColor, 1.0), 0.5);
}
From 6b7be7f9c51cf078c1f1970bc9247b95198b8068 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 20 Dec 2023 15:54:06 -0500
Subject: [PATCH 08/55] Use scene.fog to choose when fog stage is enabled
---
packages/engine/Source/Scene/FrameState.js | 4 +++-
.../Source/Scene/Model/FogPipelineStage.js | 15 ++++++++-------
packages/engine/Source/Scene/Model/Model.js | 17 ++++++++++++++---
.../Source/Scene/Model/ModelRuntimePrimitive.js | 6 +++++-
.../engine/Source/Shaders/Model/ModelFS.glsl | 2 ++
5 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/packages/engine/Source/Scene/FrameState.js b/packages/engine/Source/Scene/FrameState.js
index 31172ab4b696..98e4121f20a0 100644
--- a/packages/engine/Source/Scene/FrameState.js
+++ b/packages/engine/Source/Scene/FrameState.js
@@ -255,7 +255,8 @@ function FrameState(context, creditDisplay, jobScheduler) {
/**
* @typedef FrameState.Fog
* @type {object}
- * @property {boolean} enabled true
if fog is enabled, false
otherwise.
+ * @property {boolean} enabled true
if fog is enabled, false
otherwise. This affects both fog culling and rendering.
+ * @property {boolean} renderable true
if fog should be rendered, false
if not. This flag should be checked in combination with fog.enabled.
* @property {number} density A positive number used to mix the color and fog color based on camera distance.
* @property {number} sse A scalar used to modify the screen space error of geometry partially in fog.
* @property {number} minimumBrightness The minimum brightness of terrain with fog applied.
@@ -270,6 +271,7 @@ function FrameState(context, creditDisplay, jobScheduler) {
* @default false
*/
enabled: false,
+ renderable: false,
density: undefined,
sse: undefined,
minimumBrightness: undefined,
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
index c9d902e74b76..b23cb4654ae9 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -1,4 +1,5 @@
import FogStageFS from "../../Shaders/Model/FogStageFS.js";
+import ShaderDestination from "../../Renderer/ShaderDestination.js";
/**
* The fog color pipeline stage is responsible for applying fog to tiles in the distance in horizon views.
@@ -7,15 +8,15 @@ import FogStageFS from "../../Shaders/Model/FogStageFS.js";
*
* @private
*/
-const FogColorPipelineStage = {
+const FogPipelineStage = {
name: "FogColorPipelineStage", // Helps with debugging
};
-FogColorPipelineStage.process = function (renderResources, model, frameState) {
- // TODO: AtmosphereCommon.glsl includes uniforms that really should be
- // added separately to match the Model pipeline paradigm... Maybe that file could
- // be split into multiple files.
- renderResources.shaderBuilder.addFragmentLines([FogStageFS]);
+FogPipelineStage.process = function (renderResources, model, frameState) {
+ const shaderBuilder = renderResources.shaderBuilder;
+
+ shaderBuilder.addDefine("HAS_FOG", undefined, ShaderDestination.FRAGMENT);
+ shaderBuilder.addFragmentLines([FogStageFS]);
};
-export default FogColorPipelineStage;
+export default FogPipelineStage;
diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js
index 57c174ce725e..9659e97b45ef 100644
--- a/packages/engine/Source/Scene/Model/Model.js
+++ b/packages/engine/Source/Scene/Model/Model.js
@@ -112,7 +112,7 @@ import StyleCommandsNeeded from "./StyleCommandsNeeded.js";
* @internalConstructor
*
* @privateParam {ResourceLoader} options.loader The loader used to load resources for this model.
- * @privateParam {ModelType} options.type Type of this model, to distinguish individual glTF files from 3D Tiles internally.
+ * @privateParam {ModelType} options.type Type of this model, to distinguish individual glTF files from 3D Tiles internally.
* @privateParam {object} options Object with the following properties:
* @privateParam {Resource} options.resource The Resource to the 3D model.
* @privateParam {boolean} [options.show=true] Whether or not to render the model.
@@ -154,7 +154,7 @@ import StyleCommandsNeeded from "./StyleCommandsNeeded.js";
* @privateParam {string|number} [options.instanceFeatureIdLabel="instanceFeatureId_0"] Label of the instance feature ID set used for picking and styling. If instanceFeatureIdLabel is set to an integer N, it is converted to the string "instanceFeatureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
* @privateParam {object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting.
* @privateParam {ClassificationType} [options.classificationType] Determines whether terrain, 3D Tiles or both will be classified by this model. This cannot be set after the model has loaded.
-
+
*
* @see Model.fromGltfAsync
*
@@ -192,7 +192,7 @@ function Model(options) {
* When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's Cartesian WGS84 coordinates.
* Local reference frames can be used by providing a different transformation matrix, like that returned
* by {@link Transforms.eastNorthUpToFixedFrame}.
- *
+ *
* @type {Matrix4}
* @default {@link Matrix4.IDENTITY}
@@ -454,6 +454,8 @@ function Model(options) {
this._sceneMode = undefined;
this._projectTo2D = defaultValue(options.projectTo2D, false);
+ this._fogRenderable = undefined;
+
this._skipLevelOfDetail = false;
this._ignoreCommands = defaultValue(options.ignoreCommands, false);
@@ -1789,6 +1791,7 @@ Model.prototype.update = function (frameState) {
updateSkipLevelOfDetail(this, frameState);
updateClippingPlanes(this, frameState);
updateSceneMode(this, frameState);
+ updateFog(this, frameState);
this._defaultTexture = frameState.context.defaultTexture;
@@ -1983,6 +1986,14 @@ function updateSceneMode(model, frameState) {
}
}
+function updateFog(model, frameState) {
+ const fogRenderable = frameState.fog.enabled && frameState.fog.renderable;
+ if (fogRenderable !== model._fogRenderable) {
+ model.resetDrawCommands();
+ model._fogRenderable = fogRenderable;
+ }
+}
+
function buildDrawCommands(model, frameState) {
if (!model._drawCommandsBuilt) {
model.destroyPipelineResources();
diff --git a/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js b/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
index 824c22153be0..ac9529427fae 100644
--- a/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
+++ b/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
@@ -199,6 +199,7 @@ ModelRuntimePrimitive.prototype.configurePipeline = function (frameState) {
const mode = frameState.mode;
const use2D =
mode !== SceneMode.SCENE3D && !frameState.scene3DOnly && model._projectTo2D;
+ const fogRenderable = frameState.fog.enabled && frameState.fog.renderable;
const hasMorphTargets =
defined(primitive.morphTargets) && primitive.morphTargets.length > 0;
@@ -297,7 +298,10 @@ ModelRuntimePrimitive.prototype.configurePipeline = function (frameState) {
}
pipelineStages.push(AlphaPipelineStage);
- pipelineStages.push(FogPipelineStage);
+
+ if (fogRenderable) {
+ pipelineStages.push(FogPipelineStage);
+ }
pipelineStages.push(PrimitiveStatisticsPipelineStage);
diff --git a/packages/engine/Source/Shaders/Model/ModelFS.glsl b/packages/engine/Source/Shaders/Model/ModelFS.glsl
index 565013f1bc63..534c7f2e78f2 100644
--- a/packages/engine/Source/Shaders/Model/ModelFS.glsl
+++ b/packages/engine/Source/Shaders/Model/ModelFS.glsl
@@ -79,7 +79,9 @@ void main()
silhouetteStage(color);
#endif
+ #ifdef HAS_FOG
fogStage(color, attributes);
+ #endif
out_FragColor = color;
}
From 3027987ab0e1738d8e173a48819147dffa8aafd5 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 20 Dec 2023 16:53:35 -0500
Subject: [PATCH 09/55] Add a DynamicAtmosphereLightingType enum
---
.../Source/Renderer/AutomaticUniforms.js | 13 +++++++
.../engine/Source/Renderer/UniformState.js | 30 +++++++++++-----
packages/engine/Source/Scene/Atmosphere.js | 16 +++++++++
.../Scene/DynamicAtmosphereLightingType.js | 34 +++++++++++++++++++
packages/engine/Source/Scene/FrameState.js | 2 ++
.../getDynamicAtmosphereLightDirection.glsl | 23 +++++++++++++
.../Source/Shaders/Model/FogStageFS.glsl | 17 ++--------
7 files changed, 112 insertions(+), 23 deletions(-)
create mode 100644 packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
create mode 100644 packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index 1662bf472ee3..9158ca90963f 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -1629,6 +1629,19 @@ const AutomaticUniforms = {
return uniformState.atmosphereMieAnisotropy;
},
}),
+ /**
+ * An automatic uniform representing which light source to use for dynamic lighting
+ *
+ * @example
+ * uniform float czm_atmosphereDynamicLighting
+ */
+ czm_atmosphereDynamicLighting: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereDynamicLighting;
+ },
+ }),
/**
* An automatic GLSL uniform representing the splitter position to use when rendering with a splitter.
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index b2f5ad412c9e..aa50ac141a78 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -165,6 +165,7 @@ function UniformState() {
this._atmosphereMieCoefficient = new Cartesian3();
this._atmosphereMieScaleHeight = undefined;
this._atmosphereMieAnisotropy = undefined;
+ this._atmosphereDynamicLighting = undefined;
this._invertClassificationColor = undefined;
@@ -943,6 +944,17 @@ Object.defineProperties(UniformState.prototype, {
return this._atmosphereMieAnisotropy;
},
},
+ /**
+ * Which light source to use for dynamically lighting the atmosphere
+ *
+ * @memberof UniformState.prototype
+ * @type {DynamicAtmosphereLightingType}
+ */
+ atmosphereDynamicLighting: {
+ get: function () {
+ return this._atmosphereDynamicLighting;
+ },
+ },
/**
* A scalar that represents the geometric tolerance per meter
@@ -1373,23 +1385,25 @@ UniformState.prototype.update = function (frameState) {
this._fogDensity = frameState.fog.density;
+ const atmosphere = frameState.atmosphere;
+
this._atmosphereHsbShift = Cartesian3.clone(
- frameState.atmosphere.hsbShift,
+ atmosphere.hsbShift,
this._atmosphereHsbShift
);
- this._atmosphereLightIntensity = frameState.atmosphere.lightIntensity;
+ this._atmosphereLightIntensity = atmosphere.lightIntensity;
this._atmosphereRayleighCoefficient = Cartesian3.clone(
- frameState.atmosphere.rayleighCoefficient,
+ atmosphere.rayleighCoefficient,
this._atmosphereRayleighCoefficient
);
- this._atmosphereRayleighScaleHeight =
- frameState.atmosphere.rayleighScaleHeight;
+ this._atmosphereRayleighScaleHeight = atmosphere.rayleighScaleHeight;
this._atmosphereMieCoefficient = Cartesian3.clone(
- frameState.atmosphere.mieCoefficient,
+ atmosphere.mieCoefficient,
this._atmosphereMieCoefficient
);
- this._atmosphereMieScaleHeight = frameState.atmosphere.mieScaleHeight;
- this._atmosphereMieAnisotropy = frameState.atmosphere.mieAnisotropy;
+ this._atmosphereMieScaleHeight = atmosphere.mieScaleHeight;
+ this._atmosphereMieAnisotropy = atmosphere.mieAnisotropy;
+ this._atmosphereDynamicLighting = atmosphere.dynamicLighting;
this._invertClassificationColor = frameState.invertClassificationColor;
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index 010904e97961..d5b2c4321547 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -1,4 +1,5 @@
import Cartesian3 from "../Core/Cartesian3";
+import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType";
function Atmosphere() {
/**
@@ -46,6 +47,7 @@ function Atmosphere() {
*
* Valid values are between -1.0 and 1.0.
*
+ *
* @type {number}
* @default 0.9
*/
@@ -54,6 +56,7 @@ function Atmosphere() {
/**
* The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A hue shift of 1.0 indicates a complete rotation of the hues available.
+ *
* @type {number}
* @default 0.0
*/
@@ -62,6 +65,7 @@ function Atmosphere() {
/**
* The saturation shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A saturation shift of -1.0 is monochrome.
+ *
* @type {number}
* @default 0.0
*/
@@ -70,10 +74,20 @@ function Atmosphere() {
/**
* The brightness shift to apply to the atmosphere. Defaults to 0.0 (no shift).
* A brightness shift of -1.0 is complete darkness, which will let space show through.
+ *
* @type {number}
* @default 0.0
*/
this.brightnessShift = 0.0;
+
+ /**
+ * When not DynamicAtmosphereLightingType.OFF, the selected light source will
+ * be used for dynamically lighting all atmosphere-related rendering effects.
+ *
+ * @type {DynamicAtmosphereLightingType}
+ * @default DynamicAtmosphereLightingType.OFF
+ */
+ this.dynamicLighting = DynamicAtmosphereLightingType.OFF;
}
Atmosphere.prototype.update = function (frameState) {
@@ -93,6 +107,8 @@ Atmosphere.prototype.update = function (frameState) {
);
atmosphere.mieScaleHeight = this.mieScaleHeight;
atmosphere.mieAnisotropy = this.mieAnisotropy;
+
+ atmosphere.dynamicLighting = this.dynamicLighting;
};
export default Atmosphere;
diff --git a/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
new file mode 100644
index 000000000000..5078ac2b861d
--- /dev/null
+++ b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
@@ -0,0 +1,34 @@
+/**
+ * Atmosphere lighting effects (sky atmosphere, ground atmosphere, fog) can be
+ * further modified with dynamic lighting from the sun or other light source
+ * that changes over time. This enum determines which light source to use.
+ *
+ * @enum {number}
+ */
+const DynamicAtmosphereLightingType = {
+ /**
+ * Do not use dynamic atmosphere lighting. Anything that uses atmosphere
+ * lighting will be lit from directly above the vertex/fragment
+ *
+ * @type {number}
+ * @constant
+ */
+ OFF: 0,
+ /**
+ * Use the scene's current light source for dynamic atmosphere lighting.
+ *
+ * @type {number}
+ * @constant
+ */
+ SCENE_LIGHT: 1,
+ /**
+ * Force the dynamic atmosphere lighting to always use the sunlight direction,
+ * even if the scene uses a different light source.
+ *
+ * @type {number}
+ * @constant
+ */
+ SUNLIGHT: 2,
+};
+
+export default Object.freeze(DynamicAtmosphereLightingType);
diff --git a/packages/engine/Source/Scene/FrameState.js b/packages/engine/Source/Scene/FrameState.js
index 98e4121f20a0..566935667132 100644
--- a/packages/engine/Source/Scene/FrameState.js
+++ b/packages/engine/Source/Scene/FrameState.js
@@ -287,6 +287,7 @@ function FrameState(context, creditDisplay, jobScheduler) {
* @property {Cartesian3} mieCoefficient The Mie scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
* @property {number} mieScaleHeight The Mie scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
* @property {number} mieAnisotropy The anisotropy of the medium to consider for Mie scattering.
+ * @property {DynamicAtmosphereLightingType} dynamicLighting An enum value determining what light source to use for dynamic lighting the atmosphere (if enabled)
*/
/**
@@ -300,6 +301,7 @@ function FrameState(context, creditDisplay, jobScheduler) {
mieCoefficient: new Cartesian3(),
mieScaleHeight: undefined,
mieAnisotropy: undefined,
+ dynamicLighting: undefined,
};
/**
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl b/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
new file mode 100644
index 000000000000..2dd991d28026
--- /dev/null
+++ b/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
@@ -0,0 +1,23 @@
+/**
+ * Select which direction vector to use for dynamic atmosphere lighting based on the czm_atmosphereDynamicLighting enum.
+ *
+ * @name czm_getDynamicAtmosphereLightDirection
+ * @glslfunction
+ * @see DynamicAtmosphereLightingType.js
+ *
+ * @param {vec3} positionWC the position of the vertex/fragment in world coordinates. This is normalized and returned when dynamic lighting is turned off.
+ * @return {vec3} The normalized light direction vector. Depending on the enum value, it is either positionWC, czm_lightDirectionWC or czm_sunDirectionWC
+ */
+vec3 czm_getDynamicAtmosphereLightDirection(vec3 positionWC) {
+ float lightEnum = czm_atmosphereDynamicLighting;
+
+ const float OFF = 0.0;
+ const float SCENE_LIGHT = 1.0;
+ const float SUNLIGHT = 2.0;
+
+ vec3 lightDirection =
+ positionWC * float(lightEnum == OFF) +
+ czm_lightDirectionWC * float(lightEnum == SCENE_LIGHT) +
+ czm_sunDirectionWC * float(lightEnum == SUNLIGHT);
+ return normalize(lightDirection);
+}
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index dd2da6c7d767..4fd92a6e673c 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -1,23 +1,10 @@
vec3 computeFogColor() {
-
-//#if defined(DYNAMIC_ATMOSPHERE_LIGHTING_FROM_SUN)
- vec3 atmosphereLightDirection = czm_sunDirectionWC;
-//#else
-// vec3 atmosphereLightDirection = czm_lightDirectionWC;
-//#endif
-
vec3 rayleighColor = vec3(0.0, 0.0, 1.0);
vec3 mieColor;
float opacity;
- vec3 positionWC;
- vec3 lightDirection;
-
- // This is true when dynamic lighting is enabled in the scene.
- bool dynamicLighting = true;
-
- positionWC = czm_computeEllipsoidPosition();
- lightDirection = czm_branchFreeTernary(dynamicLighting, atmosphereLightDirection, normalize(positionWC));
+ vec3 positionWC = czm_computeEllipsoidPosition();
+ vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(positionWC);
// The fog color is derived from the ground atmosphere color
czm_computeGroundAtmosphereScattering(
From a1cb8e13f229654bdf10e7d8f89241f0de87f1c8 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 4 Jan 2024 15:40:35 -0500
Subject: [PATCH 10/55] Document curvature directions in more detail
---
packages/engine/Source/Renderer/AutomaticUniforms.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index 46879d0d7581..4d6beab2cf9b 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -954,7 +954,8 @@ const AutomaticUniforms = {
/**
* An automatic GLSL uniform containing the ellipsoid radii of curvature at the camera position.
- * The .x component is the prime vertical radius, .y is the meridional.
+ * The .x component is the prime vertical radius of curvature (east-west direction)
+ * .y is the meridional radius of curvature (north-south direction)
* This uniform is only valid when the {@link SceneMode} is SCENE3D
.
*/
czm_eyeEllipsoidCurvature: new AutomaticUniform({
From 9fb41108d6f19146eca38379ac2c21c95ac62ff8 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 4 Jan 2024 16:58:58 -0500
Subject: [PATCH 11/55] Use a different method to compute the world position
---
.../Functions/computeEllipsoidPosition.glsl | 8 ++++
.../Source/Shaders/Model/FogStageFS.glsl | 38 +++++++++++++++++--
2 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
index f11eea6caa73..ddeeee0df3a8 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
@@ -4,6 +4,8 @@
*
* @return {vec3} The position in world coordinates.
*/
+
+ /*
vec3 czm_computeEllipsoidPosition()
{
float mpp = czm_metersPerPixel(vec4(0.0, 0.0, -czm_currentFrustum.x, 1.0), 1.0);
@@ -20,3 +22,9 @@ vec3 czm_computeEllipsoidPosition()
vec3 ellipsoidPosition = czm_pointAlongRay(ray, intersection.start);
return (czm_inverseView * vec4(ellipsoidPosition, 1.0)).xyz;
}
+*/
+
+vec3 czm_computeEllipsoidPosition()
+{
+ return vec3(0.0);
+}
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 4fd92a6e673c..439551746d72 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -1,9 +1,41 @@
-vec3 computeFogColor() {
+vec3 computeEllipsoidPosition(vec3 positionMC) {
+ // Compute the distance from the camera to the local center of curvature.
+ vec4 vertexPositionENU = czm_modelToEnu * vec4(positionMC, 1.0);
+ vec2 vertexAzimuth = normalize(vertexPositionENU.xy);
+ // Curvature = 1 / radius of curvature.
+ float azimuthalCurvature = dot(vertexAzimuth * vertexAzimuth, czm_eyeEllipsoidCurvature);
+ float eyeToCenter = 1.0 / azimuthalCurvature + czm_eyeHeight;
+
+ // Compute the approximate ellipsoid normal at the vertex position.
+ // Uses a circular approximation for the Earth curvature along the geodesic.
+ vec3 vertexPositionEC = (czm_modelView * vec4(positionMC, 1.0)).xyz;
+ vec3 centerToVertex = eyeToCenter * czm_eyeEllipsoidNormalEC + vertexPositionEC;
+ vec3 vertexNormal = normalize(centerToVertex);
+
+ // Estimate the (sine of the) angle between the camera direction and the vertex normal
+ float verticalDistance = dot(vertexPositionEC, czm_eyeEllipsoidNormalEC);
+ float horizontalDistance = length(vertexPositionEC - verticalDistance * czm_eyeEllipsoidNormalEC);
+ float sinTheta = horizontalDistance / (eyeToCenter + verticalDistance);
+ bool isSmallAngle = clamp(sinTheta, 0.0, 0.05) == sinTheta;
+
+ // Approximate the change in height above the ellipsoid, from camera to vertex position.
+ float exactVersine = 1.0 - dot(czm_eyeEllipsoidNormalEC, vertexNormal);
+ float smallAngleVersine = 0.5 * sinTheta * sinTheta;
+ float versine = isSmallAngle ? smallAngleVersine : exactVersine;
+ float dHeight = dot(vertexPositionEC, vertexNormal) - eyeToCenter * versine;
+ float vertexHeight = czm_eyeHeight + dHeight;
+
+ vec3 ellipsoidPositionEC = vertexPositionEC - vertexHeight * vertexNormal;
+ return (czm_inverseView * vec4(ellipsoidPositionEC, 1.0)).xyz;
+
+}
+
+vec3 computeFogColor(vec3 positionMC) {
vec3 rayleighColor = vec3(0.0, 0.0, 1.0);
vec3 mieColor;
float opacity;
- vec3 positionWC = czm_computeEllipsoidPosition();
+ vec3 positionWC = computeEllipsoidPosition(positionMC);
vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(positionWC);
// The fog color is derived from the ground atmosphere color
@@ -45,7 +77,7 @@ vec3 computeFogColor() {
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
- vec3 fogColor = computeFogColor();
+ vec3 fogColor = computeFogColor(attributes.positionMC);
// Note: camera is far away (distance > nightFadeOutDistance), scattering is computed in the fragment shader.
// otherwise in the vertex shader. but for prototyping, I'll do everything in the FS for simplicity
From bc781be41745bc1742b66304bd5241483f89ece2 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Fri, 5 Jan 2024 13:23:15 -0500
Subject: [PATCH 12/55] Add unit test for Atmosphere
---
packages/engine/Specs/Scene/AtmosphereSpec.js | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 packages/engine/Specs/Scene/AtmosphereSpec.js
diff --git a/packages/engine/Specs/Scene/AtmosphereSpec.js b/packages/engine/Specs/Scene/AtmosphereSpec.js
new file mode 100644
index 000000000000..9758be52837c
--- /dev/null
+++ b/packages/engine/Specs/Scene/AtmosphereSpec.js
@@ -0,0 +1,69 @@
+import { Cartesian3, DynamicAtmosphereLightingType } from "../../index.js";
+
+import createScene from "../../../../Specs/createScene";
+
+describe("scene/Atmosphere", function () {
+ let scene;
+ beforeEach(function () {
+ scene = createScene();
+ });
+
+ afterEach(function () {
+ scene.destroyForSpecs();
+ });
+
+ it("updates frameState each frame", function () {
+ const atmosphere = scene.atmosphere;
+ const frameStateAtmosphere = scene.frameState.atmosphere;
+
+ // Render and check that scene.atmosphere updated
+ // frameState.atmosphere. For the first frame this should
+ // be the default settings.
+ scene.renderForSpecs();
+ expect(frameStateAtmosphere.hsbShift).toEqual(new Cartesian3());
+ expect(frameStateAtmosphere.lightIntensity).toEqual(10.0);
+ expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
+ new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6)
+ );
+ expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(10000.0);
+ expect(frameStateAtmosphere.mieCoefficient).toEqual(
+ new Cartesian3(21e-6, 21e-6, 21e-6)
+ );
+ expect(frameStateAtmosphere.mieScaleHeight).toEqual(3200.0);
+ expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.9);
+ expect(frameStateAtmosphere.dynamicLighting).toEqual(
+ DynamicAtmosphereLightingType.OFF
+ );
+
+ // Now change the settings, render again and check that
+ // the frame state was updated.
+ atmosphere.hueShift = 0.5;
+ atmosphere.saturationShift = -0.5;
+ atmosphere.brightnessShift = 0.25;
+ atmosphere.lightIntensity = 5.0;
+ atmosphere.rayleighCoefficient = new Cartesian3(1.0, 1.0, 1.0);
+ atmosphere.rayleighScaleHeight = 1000;
+ atmosphere.mieCoefficient = new Cartesian3(2.0, 2.0, 2.0);
+ atmosphere.mieScaleHeight = 100;
+ atmosphere.mieAnisotropy = 0.5;
+ atmosphere.dynamicLighting = DynamicAtmosphereLightingType.SUNLIGHT;
+
+ scene.renderForSpecs();
+ expect(frameStateAtmosphere.hsbShift).toEqual(
+ new Cartesian3(0.5, -0.5, 0.25)
+ );
+ expect(frameStateAtmosphere.lightIntensity).toEqual(5.0);
+ expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
+ new Cartesian3(1.0, 1.0, 1.0)
+ );
+ expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(1000);
+ expect(frameStateAtmosphere.mieCoefficient).toEqual(
+ new Cartesian3(2.0, 2.0, 2.0)
+ );
+ expect(frameStateAtmosphere.mieScaleHeight).toEqual(100.0);
+ expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.5);
+ expect(frameStateAtmosphere.dynamicLighting).toEqual(
+ DynamicAtmosphereLightingType.SUNLIGHT
+ );
+ });
+});
From 990b81560024d4f3ce6b51c74cf1722224a0a1f5 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Fri, 5 Jan 2024 14:15:22 -0500
Subject: [PATCH 13/55] Add unit tests for automatic uniforms
---
.../Specs/Renderer/AutomaticUniformSpec.js | 188 ++++++++++++++++++
1 file changed, 188 insertions(+)
diff --git a/packages/engine/Specs/Renderer/AutomaticUniformSpec.js b/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
index 55319d78300b..349687d5a1ba 100644
--- a/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
+++ b/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
@@ -1,11 +1,14 @@
import {
+ Atmosphere,
Cartesian2,
Cartesian3,
Cartographic,
Color,
defaultValue,
DirectionalLight,
+ DynamicAtmosphereLightingType,
Ellipsoid,
+ Fog,
GeographicProjection,
Matrix4,
OrthographicFrustum,
@@ -1783,6 +1786,191 @@ describe(
}).contextToRender();
});
+ it("has czm_fogDensity", function () {
+ const frameState = createFrameState(
+ context,
+ createMockCamera(
+ undefined,
+ undefined,
+ undefined,
+ // Explicit position and direction as the default position of (0, 0, 0)
+ // will lead to a divide by zero when updating fog below.
+ new Cartesian3(1.0, 0.0, 0.0),
+ new Cartesian3(0.0, 1.0, 0.0)
+ )
+ );
+ const fog = new Fog();
+ fog.density = 0.1;
+ fog.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_fogDensity != 0.0);" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereHsbShift", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ atmosphere.hueShift = 1.0;
+ atmosphere.saturationShift = 2.0;
+ atmosphere.brightnessShift = 3.0;
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_atmosphereHsbShift == vec3(1.0, 2.0, 3.0));" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereLightIntensity", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ atmosphere.lightIntensity = 2.0;
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_atmosphereLightIntensity == 2.0);" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereRayleighCoefficient", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ atmosphere.rayleighCoefficient = new Cartesian3(1.0, 2.0, 3.0);
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_atmosphereRayleighCoefficient == vec3(1.0, 2.0, 3.0));" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereRayleighScaleHeight", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ atmosphere.rayleighScaleHeight = 100.0;
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_atmosphereRayleighScaleHeight == 100.0);" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereMieCoefficient", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ atmosphere.mieCoefficient = new Cartesian3(1.0, 2.0, 3.0);
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_atmosphereMieCoefficient == vec3(1.0, 2.0, 3.0));" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereMieScaleHeight", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ atmosphere.mieScaleHeight = 100.0;
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_atmosphereMieScaleHeight == 100.0);" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereMieAnisotropy", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ atmosphere.mieAnisotropy = 100.0;
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_atmosphereMieAnisotropy == 100.0);" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
+ it("has czm_atmosphereDynamicLighting", function () {
+ const frameState = createFrameState(context, createMockCamera());
+ const atmosphere = new Atmosphere();
+ const enumValue = DynamicAtmosphereLightingType.SCENE_LIGHT;
+ atmosphere.dynamicLighting = enumValue;
+ atmosphere.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ ` out_FragColor = vec4(czm_atmosphereDynamicLighting == float(${enumValue}));` +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
it("has czm_pass and czm_passEnvironment", function () {
const us = context.uniformState;
us.updatePass(Pass.ENVIRONMENT);
From c4e1b373f57d9863880fb29cc490962cd075d34c Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Fri, 5 Jan 2024 16:43:14 -0500
Subject: [PATCH 14/55] Fix and update unit tests for ModelRuntimePrimitive
---
.../Scene/Model/ModelRuntimePrimitiveSpec.js | 165 ++++++++++++------
1 file changed, 116 insertions(+), 49 deletions(-)
diff --git a/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js b/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js
index dff8c900829f..8dd9ddb669b3 100644
--- a/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js
@@ -2,10 +2,12 @@ import {
AlphaPipelineStage,
BatchTexturePipelineStage,
Cesium3DTileStyle,
+ ClassificationPipelineStage,
CustomShader,
CustomShaderMode,
CustomShaderPipelineStage,
FeatureIdPipelineStage,
+ FogPipelineStage,
CPUStylingPipelineStage,
DequantizationPipelineStage,
GeometryPipelineStage,
@@ -30,7 +32,8 @@ import {
WireframePipelineStage,
ClassificationType,
} from "../../../index.js";
-import ClassificationPipelineStage from "../../../Source/Scene/Model/ClassificationPipelineStage.js";
+
+import createFrameState from "../../../../../Specs/createFrameState.js";
describe("Scene/Model/ModelRuntimePrimitive", function () {
const mockPrimitive = {
@@ -43,33 +46,21 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
allowPicking: true,
featureIdLabel: "featureId_0",
};
- const mockFrameState = {
- context: {
- webgl2: false,
- },
- mode: SceneMode.SCENE3D,
+ const mockWebgl1Context = {
+ webgl2: false,
};
-
- const mockFrameStateWebgl2 = {
- context: {
- webgl2: true,
- },
+ const mockWebgl2Context = {
+ webgl2: true,
};
- const mockFrameState2D = {
- context: {
- webgl2: false,
- },
- mode: SceneMode.SCENE2D,
- };
+ const mockFrameState = createFrameState(mockWebgl1Context);
+ const mockFrameStateWebgl2 = createFrameState(mockWebgl2Context);
- const mockFrameState3DOnly = {
- context: {
- webgl2: false,
- },
- mode: SceneMode.SCENE3D,
- scene3DOnly: true,
- };
+ const mockFrameState2D = createFrameState(mockWebgl1Context);
+ mockFrameState2D.mode = SceneMode.SCENE2D;
+
+ const mockFrameState3DOnly = createFrameState(mockWebgl1Context);
+ mockFrameState3DOnly.scene3DOnly = true;
const emptyVertexShader =
"void vertexMain(VertexInput vsInput, inout vec3 position) {}";
@@ -160,7 +151,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -203,7 +193,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
SelectedFeatureIdPipelineStage,
BatchTexturePipelineStage,
CPUStylingPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
PickingPipelineStage,
AlphaPipelineStage,
@@ -250,7 +239,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
SelectedFeatureIdPipelineStage,
BatchTexturePipelineStage,
CPUStylingPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
PickingPipelineStage,
AlphaPipelineStage,
@@ -307,7 +295,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
PickingPipelineStage,
AlphaPipelineStage,
@@ -335,7 +322,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
CustomShaderPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
@@ -366,7 +352,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
GeometryPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
CustomShaderPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
@@ -397,7 +382,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
CustomShaderPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
@@ -438,7 +422,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -473,7 +456,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -507,7 +489,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -538,7 +519,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -570,7 +550,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -600,7 +579,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -633,7 +611,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -675,7 +652,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -708,7 +684,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -740,7 +715,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -772,7 +746,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -803,7 +776,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -835,7 +807,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -866,7 +837,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -897,7 +867,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -929,7 +898,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
PrimitiveOutlinePipelineStage,
AlphaPipelineStage,
@@ -962,7 +930,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -993,7 +960,6 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
MaterialPipelineStage,
FeatureIdPipelineStage,
MetadataPipelineStage,
- VerticalExaggerationPipelineStage,
LightingPipelineStage,
AlphaPipelineStage,
PrimitiveStatisticsPipelineStage,
@@ -1002,4 +968,105 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
primitive.configurePipeline(mockFrameState);
verifyExpectedStages(primitive.pipelineStages, expectedStages);
});
+
+ it("configures pipeline stages for vertical exaggeration", function () {
+ const primitive = new ModelRuntimePrimitive({
+ primitive: mockPrimitive,
+ node: mockNode,
+ model: mockModel,
+ });
+ const frameState = createFrameState(mockWebgl2Context);
+ frameState.verticalExaggeration = 2.0;
+
+ const expectedStages = [
+ GeometryPipelineStage,
+ MaterialPipelineStage,
+ FeatureIdPipelineStage,
+ MetadataPipelineStage,
+ VerticalExaggerationPipelineStage,
+ LightingPipelineStage,
+ PickingPipelineStage,
+ AlphaPipelineStage,
+ PrimitiveStatisticsPipelineStage,
+ ];
+
+ primitive.configurePipeline(frameState);
+ verifyExpectedStages(primitive.pipelineStages, expectedStages);
+ });
+
+ it("does not add fog stage when fog is not enabled", function () {
+ const primitive = new ModelRuntimePrimitive({
+ primitive: mockPrimitive,
+ node: mockNode,
+ model: mockModel,
+ });
+ const frameState = createFrameState(mockWebgl2Context);
+ frameState.fog.enabled = false;
+ frameState.fog.renderable = false;
+
+ const expectedStages = [
+ GeometryPipelineStage,
+ MaterialPipelineStage,
+ FeatureIdPipelineStage,
+ MetadataPipelineStage,
+ LightingPipelineStage,
+ PickingPipelineStage,
+ AlphaPipelineStage,
+ PrimitiveStatisticsPipelineStage,
+ ];
+
+ primitive.configurePipeline(frameState);
+ verifyExpectedStages(primitive.pipelineStages, expectedStages);
+ });
+
+ it("does not add fog stage when fog is not renderable", function () {
+ const primitive = new ModelRuntimePrimitive({
+ primitive: mockPrimitive,
+ node: mockNode,
+ model: mockModel,
+ });
+ const frameState = createFrameState(mockWebgl2Context);
+ frameState.fog.enabled = true;
+ frameState.fog.renderable = false;
+
+ const expectedStages = [
+ GeometryPipelineStage,
+ MaterialPipelineStage,
+ FeatureIdPipelineStage,
+ MetadataPipelineStage,
+ LightingPipelineStage,
+ PickingPipelineStage,
+ AlphaPipelineStage,
+ PrimitiveStatisticsPipelineStage,
+ ];
+
+ primitive.configurePipeline(frameState);
+ verifyExpectedStages(primitive.pipelineStages, expectedStages);
+ });
+
+ it("configures pipeline stages when fog is enabled and renderable", function () {
+ const primitive = new ModelRuntimePrimitive({
+ primitive: mockPrimitive,
+ node: mockNode,
+ model: mockModel,
+ });
+ const frameState = createFrameState(mockWebgl2Context);
+ frameState.fog.enabled = true;
+ frameState.fog.renderable = true;
+
+ const expectedStages = [
+ GeometryPipelineStage,
+ MaterialPipelineStage,
+ FeatureIdPipelineStage,
+ MetadataPipelineStage,
+ LightingPipelineStage,
+ PickingPipelineStage,
+ AlphaPipelineStage,
+ FogPipelineStage,
+ PrimitiveStatisticsPipelineStage,
+ ];
+
+ primitive.configurePipeline(frameState);
+ verifyExpectedStages(primitive.pipelineStages, expectedStages);
+ });
});
From 046160c12ba33abbdd13ae5e9544d575d63ba6e8 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 8 Jan 2024 11:29:51 -0500
Subject: [PATCH 15/55] Implement the other method for computing the ellipsoid
position
---
.../Source/Shaders/Model/FogStageFS.glsl | 57 +++++++++++++++++--
1 file changed, 52 insertions(+), 5 deletions(-)
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 439551746d72..41c30da5a1a7 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -1,4 +1,4 @@
-vec3 computeEllipsoidPosition(vec3 positionMC) {
+vec3 computeEllipsoidPositionCurvature(vec3 positionMC) {
// Compute the distance from the camera to the local center of curvature.
vec4 vertexPositionENU = czm_modelToEnu * vec4(positionMC, 1.0);
vec2 vertexAzimuth = normalize(vertexPositionENU.xy);
@@ -30,17 +30,64 @@ vec3 computeEllipsoidPosition(vec3 positionMC) {
}
+// robust iterative solution without trig functions
+// https://github.com/0xfaded/ellipse_demo/issues/1
+// https://stackoverflow.com/questions/22959698/distance-from-given-point-to-given-ellipse
+vec2 nearestPointOnEllipse(vec2 pos, vec2 radii) {
+ vec2 p = abs(pos);
+ vec2 inverseRadii = 1.0 / radii;
+ vec2 evoluteScale = (radii.x * radii.x - radii.y * radii.y) * vec2(1.0, -1.0) * inverseRadii;
+
+ // We describe the ellipse parametrically: v = radii * vec2(cos(t), sin(t))
+ // but store the cos and sin of t in a vec2 for efficiency.
+ // Initial guess: t = cos(pi/4)
+ vec2 tTrigs = vec2(0.70710678118);
+ vec2 v = radii * tTrigs;
+
+ const int iterations = 3;
+ for (int i = 0; i < iterations; ++i) {
+ // Find the evolute of the ellipse (center of curvature) at v.
+ vec2 evolute = evoluteScale * tTrigs * tTrigs * tTrigs;
+ // Find the (approximate) intersection of p - evolute with the ellipsoid.
+ vec2 q = normalize(p - evolute) * length(v - evolute);
+ // Update the estimate of t.
+ tTrigs = (q + evolute) * inverseRadii;
+ tTrigs = normalize(clamp(tTrigs, 0.0, 1.0));
+ v = radii * tTrigs;
+ }
+
+ return v * sign(pos);
+}
+
+vec3 computeEllipsoidPositionIterative(vec3 positionMC) {
+ // Get the world-space position and project onto a meridian plane of
+ // the ellipsoid
+ vec3 positionWC = (czm_model * vec4(positionMC, 1.0)).xyz;
+
+ vec2 positionEllipse = vec2(length(positionWC.xy), positionWC.z);
+ vec2 nearestPoint = nearestPointOnEllipse(positionEllipse, czm_ellipsoidRadii.xz);
+
+ // Reconstruct a 3D point in world space
+ return vec3(nearestPoint.x * normalize(positionWC.xy), nearestPoint.y);
+}
+
+#define USE_ITERATIVE_METHOD
+
vec3 computeFogColor(vec3 positionMC) {
vec3 rayleighColor = vec3(0.0, 0.0, 1.0);
vec3 mieColor;
float opacity;
- vec3 positionWC = computeEllipsoidPosition(positionMC);
- vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(positionWC);
+#ifdef USE_ITERATIVE_METHOD
+ vec3 ellipsoidPositionWC = computeEllipsoidPositionIterative(positionMC);
+#else
+ vec3 ellipsoidPositionWC = computeEllipsoidPositionCurvature(positionMC);
+#endif
+ vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(ellipsoidPositionWC);
// The fog color is derived from the ground atmosphere color
czm_computeGroundAtmosphereScattering(
- positionWC,
+ ellipsoidPositionWC,
lightDirection,
rayleighColor,
mieColor,
@@ -50,7 +97,7 @@ vec3 computeFogColor(vec3 positionMC) {
//rayleighColor = vec3(1.0, 0.0, 0.0);
//mieColor = vec3(0.0, 1.0, 0.0);
- vec4 groundAtmosphereColor = czm_computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
+ vec4 groundAtmosphereColor = czm_computeAtmosphereColor(ellipsoidPositionWC, lightDirection, rayleighColor, mieColor, opacity);
vec3 fogColor = groundAtmosphereColor.rgb;
// Darken the fog
From 4255132a95901783fa0b27db189b0aa2bb126131 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 8 Jan 2024 16:13:26 -0500
Subject: [PATCH 16/55] Experiment with different ellipsoid calculations
---
.../Source/Renderer/AutomaticUniforms.js | 8 +++
.../engine/Source/Renderer/UniformState.js | 9 +++
packages/engine/Source/Scene/Atmosphere.js | 8 +++
packages/engine/Source/Scene/FrameState.js | 2 +
.../Source/Shaders/Model/FogStageFS.glsl | 69 +++++++++++++++++--
5 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index 4d6beab2cf9b..604f4c29915c 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -1697,6 +1697,14 @@ const AutomaticUniforms = {
},
}),
+ czm_atmosphereMethod: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT,
+ getValue: function (uniformState) {
+ return uniformState.atmosphereMethod;
+ },
+ }),
+
/**
* An automatic GLSL uniform representing the splitter position to use when rendering with a splitter.
* This will be in pixel coordinates relative to the canvas.
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index 75bfbde2d316..5eb6cca9f2f9 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -171,6 +171,8 @@ function UniformState() {
this._atmosphereMieAnisotropy = undefined;
this._atmosphereDynamicLighting = undefined;
+ this._atmosphereMethod = undefined;
+
this._invertClassificationColor = undefined;
this._splitPosition = 0.0;
@@ -1006,6 +1008,12 @@ Object.defineProperties(UniformState.prototype, {
},
},
+ atmosphereMethod: {
+ get: function () {
+ return this._atmosphereMethod;
+ },
+ },
+
/**
* A scalar that represents the geometric tolerance per meter
* @memberof UniformState.prototype
@@ -1522,6 +1530,7 @@ UniformState.prototype.update = function (frameState) {
this._atmosphereMieScaleHeight = atmosphere.mieScaleHeight;
this._atmosphereMieAnisotropy = atmosphere.mieAnisotropy;
this._atmosphereDynamicLighting = atmosphere.dynamicLighting;
+ this._atmosphereMethod = atmosphere.method;
this._invertClassificationColor = frameState.invertClassificationColor;
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index d5b2c4321547..1452494f3eda 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -88,6 +88,12 @@ function Atmosphere() {
* @default DynamicAtmosphereLightingType.OFF
*/
this.dynamicLighting = DynamicAtmosphereLightingType.OFF;
+
+ // TEMPORARY! Just for side-by-side-comparisons for the PR
+ // 0 = positionWC = czm_model * positionMC
+ // 1 = positionWC = computeEllipsoidPositionCurvature(positionMC)
+ // 2 = positionWC = computeEllipsoidPositionIterative(positionMC)
+ this.method = 1;
}
Atmosphere.prototype.update = function (frameState) {
@@ -109,6 +115,8 @@ Atmosphere.prototype.update = function (frameState) {
atmosphere.mieAnisotropy = this.mieAnisotropy;
atmosphere.dynamicLighting = this.dynamicLighting;
+
+ atmosphere.method = this.method;
};
export default Atmosphere;
diff --git a/packages/engine/Source/Scene/FrameState.js b/packages/engine/Source/Scene/FrameState.js
index 0ad600f948ba..81b10aa18e5d 100644
--- a/packages/engine/Source/Scene/FrameState.js
+++ b/packages/engine/Source/Scene/FrameState.js
@@ -302,6 +302,8 @@ function FrameState(context, creditDisplay, jobScheduler) {
mieScaleHeight: undefined,
mieAnisotropy: undefined,
dynamicLighting: undefined,
+
+ method: 1,
};
/**
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 41c30da5a1a7..02fd31bffde4 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -59,6 +59,32 @@ vec2 nearestPointOnEllipse(vec2 pos, vec2 radii) {
return v * sign(pos);
}
+vec2 nearestPointOnEllipse1Iter(vec2 pos, vec2 radii) {
+ vec2 p = abs(pos);
+ vec2 inverseRadii = 1.0 / radii;
+ vec2 evoluteScale = (radii.x * radii.x - radii.y * radii.y) * vec2(1.0, -1.0) * inverseRadii;
+
+ // We describe the ellipse parametrically: v = radii * vec2(cos(t), sin(t))
+ // but store the cos and sin of t in a vec2 for efficiency.
+ // Initial guess: t = cos(pi/4)
+ vec2 tTrigs = vec2(0.70710678118);
+ vec2 v = radii * tTrigs;
+
+ const int iterations = 1;
+ for (int i = 0; i < iterations; ++i) {
+ // Find the evolute of the ellipse (center of curvature) at v.
+ vec2 evolute = evoluteScale * tTrigs * tTrigs * tTrigs;
+ // Find the (approximate) intersection of p - evolute with the ellipsoid.
+ vec2 q = normalize(p - evolute) * length(v - evolute);
+ // Update the estimate of t.
+ tTrigs = (q + evolute) * inverseRadii;
+ tTrigs = normalize(clamp(tTrigs, 0.0, 1.0));
+ v = radii * tTrigs;
+ }
+
+ return v * sign(pos);
+}
+
vec3 computeEllipsoidPositionIterative(vec3 positionMC) {
// Get the world-space position and project onto a meridian plane of
// the ellipsoid
@@ -71,6 +97,18 @@ vec3 computeEllipsoidPositionIterative(vec3 positionMC) {
return vec3(nearestPoint.x * normalize(positionWC.xy), nearestPoint.y);
}
+vec3 computeEllipsoidPositionIterative1Iter(vec3 positionMC) {
+ // Get the world-space position and project onto a meridian plane of
+ // the ellipsoid
+ vec3 positionWC = (czm_model * vec4(positionMC, 1.0)).xyz;
+
+ vec2 positionEllipse = vec2(length(positionWC.xy), positionWC.z);
+ vec2 nearestPoint = nearestPointOnEllipse1Iter(positionEllipse, czm_ellipsoidRadii.xz);
+
+ // Reconstruct a 3D point in world space
+ return vec3(nearestPoint.x * normalize(positionWC.xy), nearestPoint.y);
+}
+
#define USE_ITERATIVE_METHOD
vec3 computeFogColor(vec3 positionMC) {
@@ -78,11 +116,24 @@ vec3 computeFogColor(vec3 positionMC) {
vec3 mieColor;
float opacity;
-#ifdef USE_ITERATIVE_METHOD
- vec3 ellipsoidPositionWC = computeEllipsoidPositionIterative(positionMC);
-#else
- vec3 ellipsoidPositionWC = computeEllipsoidPositionCurvature(positionMC);
-#endif
+ // Measuring performance is difficult in the shader, so run the code many times
+ // so the difference is noticeable in the FPS counter
+ const float N = 200.0;
+ const float WEIGHT = 1.0 / N;
+ vec3 ellipsoidPositionWC = vec3(0.0);
+ for (float i = 0.0; i < N; i++) {
+ if (czm_atmosphereMethod == 0.0) {
+ // Baseline for comparison - just use the vertex position in WC
+ ellipsoidPositionWC += WEIGHT * (czm_model * vec4(positionMC, 1.0)).xyz;
+ } else if (czm_atmosphereMethod == 1.0) {
+ // Use the curvature method
+ ellipsoidPositionWC += WEIGHT * computeEllipsoidPositionCurvature(positionMC);
+ } else {
+ // use the 2D iterative method
+ ellipsoidPositionWC += WEIGHT * computeEllipsoidPositionIterative1Iter(positionMC);
+ }
+ }
+
vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(ellipsoidPositionWC);
// The fog color is derived from the ground atmosphere color
@@ -138,4 +189,12 @@ void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
color = vec4(withFog, color.a);
//color = mix(color, vec4(fogColor, 1.0), 0.5);
+
+ // Compare the two methods.
+ vec3 curvature = computeEllipsoidPositionCurvature(attributes.positionMC);
+ vec3 iterative = computeEllipsoidPositionIterative(attributes.positionMC);
+ vec3 iterative1 = computeEllipsoidPositionIterative1Iter(attributes.positionMC);
+
+ //color = vec4(abs(curvature - iterative) / 1e3, 1.0);
+ //color = vec4(abs(iterative - iterative1) / 1e2, 1.0);
}
From e3266dff2bc61cd26a37b967ecac55ae25a27246 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 8 Jan 2024 16:19:35 -0500
Subject: [PATCH 17/55] Remove temporary debugging code
---
.../Source/Renderer/AutomaticUniforms.js | 8 --
.../engine/Source/Renderer/UniformState.js | 9 --
packages/engine/Source/Scene/Atmosphere.js | 8 --
packages/engine/Source/Scene/FrameState.js | 2 -
.../Source/Shaders/Model/FogStageFS.glsl | 127 +++---------------
5 files changed, 15 insertions(+), 139 deletions(-)
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index 604f4c29915c..4d6beab2cf9b 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -1697,14 +1697,6 @@ const AutomaticUniforms = {
},
}),
- czm_atmosphereMethod: new AutomaticUniform({
- size: 1,
- datatype: WebGLConstants.FLOAT,
- getValue: function (uniformState) {
- return uniformState.atmosphereMethod;
- },
- }),
-
/**
* An automatic GLSL uniform representing the splitter position to use when rendering with a splitter.
* This will be in pixel coordinates relative to the canvas.
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index 5eb6cca9f2f9..75bfbde2d316 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -171,8 +171,6 @@ function UniformState() {
this._atmosphereMieAnisotropy = undefined;
this._atmosphereDynamicLighting = undefined;
- this._atmosphereMethod = undefined;
-
this._invertClassificationColor = undefined;
this._splitPosition = 0.0;
@@ -1008,12 +1006,6 @@ Object.defineProperties(UniformState.prototype, {
},
},
- atmosphereMethod: {
- get: function () {
- return this._atmosphereMethod;
- },
- },
-
/**
* A scalar that represents the geometric tolerance per meter
* @memberof UniformState.prototype
@@ -1530,7 +1522,6 @@ UniformState.prototype.update = function (frameState) {
this._atmosphereMieScaleHeight = atmosphere.mieScaleHeight;
this._atmosphereMieAnisotropy = atmosphere.mieAnisotropy;
this._atmosphereDynamicLighting = atmosphere.dynamicLighting;
- this._atmosphereMethod = atmosphere.method;
this._invertClassificationColor = frameState.invertClassificationColor;
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index 1452494f3eda..d5b2c4321547 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -88,12 +88,6 @@ function Atmosphere() {
* @default DynamicAtmosphereLightingType.OFF
*/
this.dynamicLighting = DynamicAtmosphereLightingType.OFF;
-
- // TEMPORARY! Just for side-by-side-comparisons for the PR
- // 0 = positionWC = czm_model * positionMC
- // 1 = positionWC = computeEllipsoidPositionCurvature(positionMC)
- // 2 = positionWC = computeEllipsoidPositionIterative(positionMC)
- this.method = 1;
}
Atmosphere.prototype.update = function (frameState) {
@@ -115,8 +109,6 @@ Atmosphere.prototype.update = function (frameState) {
atmosphere.mieAnisotropy = this.mieAnisotropy;
atmosphere.dynamicLighting = this.dynamicLighting;
-
- atmosphere.method = this.method;
};
export default Atmosphere;
diff --git a/packages/engine/Source/Scene/FrameState.js b/packages/engine/Source/Scene/FrameState.js
index 81b10aa18e5d..0ad600f948ba 100644
--- a/packages/engine/Source/Scene/FrameState.js
+++ b/packages/engine/Source/Scene/FrameState.js
@@ -302,8 +302,6 @@ function FrameState(context, creditDisplay, jobScheduler) {
mieScaleHeight: undefined,
mieAnisotropy: undefined,
dynamicLighting: undefined,
-
- method: 1,
};
/**
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 02fd31bffde4..6c6b2eb23337 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -1,39 +1,10 @@
-vec3 computeEllipsoidPositionCurvature(vec3 positionMC) {
- // Compute the distance from the camera to the local center of curvature.
- vec4 vertexPositionENU = czm_modelToEnu * vec4(positionMC, 1.0);
- vec2 vertexAzimuth = normalize(vertexPositionENU.xy);
- // Curvature = 1 / radius of curvature.
- float azimuthalCurvature = dot(vertexAzimuth * vertexAzimuth, czm_eyeEllipsoidCurvature);
- float eyeToCenter = 1.0 / azimuthalCurvature + czm_eyeHeight;
-
- // Compute the approximate ellipsoid normal at the vertex position.
- // Uses a circular approximation for the Earth curvature along the geodesic.
- vec3 vertexPositionEC = (czm_modelView * vec4(positionMC, 1.0)).xyz;
- vec3 centerToVertex = eyeToCenter * czm_eyeEllipsoidNormalEC + vertexPositionEC;
- vec3 vertexNormal = normalize(centerToVertex);
-
- // Estimate the (sine of the) angle between the camera direction and the vertex normal
- float verticalDistance = dot(vertexPositionEC, czm_eyeEllipsoidNormalEC);
- float horizontalDistance = length(vertexPositionEC - verticalDistance * czm_eyeEllipsoidNormalEC);
- float sinTheta = horizontalDistance / (eyeToCenter + verticalDistance);
- bool isSmallAngle = clamp(sinTheta, 0.0, 0.05) == sinTheta;
-
- // Approximate the change in height above the ellipsoid, from camera to vertex position.
- float exactVersine = 1.0 - dot(czm_eyeEllipsoidNormalEC, vertexNormal);
- float smallAngleVersine = 0.5 * sinTheta * sinTheta;
- float versine = isSmallAngle ? smallAngleVersine : exactVersine;
- float dHeight = dot(vertexPositionEC, vertexNormal) - eyeToCenter * versine;
- float vertexHeight = czm_eyeHeight + dHeight;
-
- vec3 ellipsoidPositionEC = vertexPositionEC - vertexHeight * vertexNormal;
- return (czm_inverseView * vec4(ellipsoidPositionEC, 1.0)).xyz;
-
-}
-
// robust iterative solution without trig functions
// https://github.com/0xfaded/ellipse_demo/issues/1
// https://stackoverflow.com/questions/22959698/distance-from-given-point-to-given-ellipse
-vec2 nearestPointOnEllipse(vec2 pos, vec2 radii) {
+//
+// This version uses only a single iteration for best performance. For fog
+// rendering, the difference is negligible.
+vec2 nearestPointOnEllipseFast(vec2 pos, vec2 radii) {
vec2 p = abs(pos);
vec2 inverseRadii = 1.0 / radii;
vec2 evoluteScale = (radii.x * radii.x - radii.y * radii.y) * vec2(1.0, -1.0) * inverseRadii;
@@ -44,96 +15,36 @@ vec2 nearestPointOnEllipse(vec2 pos, vec2 radii) {
vec2 tTrigs = vec2(0.70710678118);
vec2 v = radii * tTrigs;
- const int iterations = 3;
- for (int i = 0; i < iterations; ++i) {
- // Find the evolute of the ellipse (center of curvature) at v.
- vec2 evolute = evoluteScale * tTrigs * tTrigs * tTrigs;
- // Find the (approximate) intersection of p - evolute with the ellipsoid.
- vec2 q = normalize(p - evolute) * length(v - evolute);
- // Update the estimate of t.
- tTrigs = (q + evolute) * inverseRadii;
- tTrigs = normalize(clamp(tTrigs, 0.0, 1.0));
- v = radii * tTrigs;
- }
+ // Find the evolute of the ellipse (center of curvature) at v.
+ vec2 evolute = evoluteScale * tTrigs * tTrigs * tTrigs;
+ // Find the (approximate) intersection of p - evolute with the ellipsoid.
+ vec2 q = normalize(p - evolute) * length(v - evolute);
+ // Update the estimate of t.
+ tTrigs = (q + evolute) * inverseRadii;
+ tTrigs = normalize(clamp(tTrigs, 0.0, 1.0));
+ v = radii * tTrigs;
return v * sign(pos);
}
-vec2 nearestPointOnEllipse1Iter(vec2 pos, vec2 radii) {
- vec2 p = abs(pos);
- vec2 inverseRadii = 1.0 / radii;
- vec2 evoluteScale = (radii.x * radii.x - radii.y * radii.y) * vec2(1.0, -1.0) * inverseRadii;
-
- // We describe the ellipse parametrically: v = radii * vec2(cos(t), sin(t))
- // but store the cos and sin of t in a vec2 for efficiency.
- // Initial guess: t = cos(pi/4)
- vec2 tTrigs = vec2(0.70710678118);
- vec2 v = radii * tTrigs;
-
- const int iterations = 1;
- for (int i = 0; i < iterations; ++i) {
- // Find the evolute of the ellipse (center of curvature) at v.
- vec2 evolute = evoluteScale * tTrigs * tTrigs * tTrigs;
- // Find the (approximate) intersection of p - evolute with the ellipsoid.
- vec2 q = normalize(p - evolute) * length(v - evolute);
- // Update the estimate of t.
- tTrigs = (q + evolute) * inverseRadii;
- tTrigs = normalize(clamp(tTrigs, 0.0, 1.0));
- v = radii * tTrigs;
- }
-
- return v * sign(pos);
-}
-
-vec3 computeEllipsoidPositionIterative(vec3 positionMC) {
+vec3 computeEllipsoidPositionWC(vec3 positionMC) {
// Get the world-space position and project onto a meridian plane of
// the ellipsoid
vec3 positionWC = (czm_model * vec4(positionMC, 1.0)).xyz;
vec2 positionEllipse = vec2(length(positionWC.xy), positionWC.z);
- vec2 nearestPoint = nearestPointOnEllipse(positionEllipse, czm_ellipsoidRadii.xz);
+ vec2 nearestPoint = nearestPointOnEllipseFast(positionEllipse, czm_ellipsoidRadii.xz);
// Reconstruct a 3D point in world space
return vec3(nearestPoint.x * normalize(positionWC.xy), nearestPoint.y);
}
-vec3 computeEllipsoidPositionIterative1Iter(vec3 positionMC) {
- // Get the world-space position and project onto a meridian plane of
- // the ellipsoid
- vec3 positionWC = (czm_model * vec4(positionMC, 1.0)).xyz;
-
- vec2 positionEllipse = vec2(length(positionWC.xy), positionWC.z);
- vec2 nearestPoint = nearestPointOnEllipse1Iter(positionEllipse, czm_ellipsoidRadii.xz);
-
- // Reconstruct a 3D point in world space
- return vec3(nearestPoint.x * normalize(positionWC.xy), nearestPoint.y);
-}
-
-#define USE_ITERATIVE_METHOD
-
vec3 computeFogColor(vec3 positionMC) {
vec3 rayleighColor = vec3(0.0, 0.0, 1.0);
vec3 mieColor;
float opacity;
- // Measuring performance is difficult in the shader, so run the code many times
- // so the difference is noticeable in the FPS counter
- const float N = 200.0;
- const float WEIGHT = 1.0 / N;
- vec3 ellipsoidPositionWC = vec3(0.0);
- for (float i = 0.0; i < N; i++) {
- if (czm_atmosphereMethod == 0.0) {
- // Baseline for comparison - just use the vertex position in WC
- ellipsoidPositionWC += WEIGHT * (czm_model * vec4(positionMC, 1.0)).xyz;
- } else if (czm_atmosphereMethod == 1.0) {
- // Use the curvature method
- ellipsoidPositionWC += WEIGHT * computeEllipsoidPositionCurvature(positionMC);
- } else {
- // use the 2D iterative method
- ellipsoidPositionWC += WEIGHT * computeEllipsoidPositionIterative1Iter(positionMC);
- }
- }
-
+ vec3 ellipsoidPositionWC = computeEllipsoidPositionWC(positionMC);
vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(ellipsoidPositionWC);
// The fog color is derived from the ground atmosphere color
@@ -189,12 +100,4 @@ void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
color = vec4(withFog, color.a);
//color = mix(color, vec4(fogColor, 1.0), 0.5);
-
- // Compare the two methods.
- vec3 curvature = computeEllipsoidPositionCurvature(attributes.positionMC);
- vec3 iterative = computeEllipsoidPositionIterative(attributes.positionMC);
- vec3 iterative1 = computeEllipsoidPositionIterative1Iter(attributes.positionMC);
-
- //color = vec4(abs(curvature - iterative) / 1e3, 1.0);
- //color = vec4(abs(iterative - iterative1) / 1e2, 1.0);
}
From cc1f34634f76a53e9538008d01b5ff4dcf1c70f9 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Mon, 8 Jan 2024 16:41:05 -0500
Subject: [PATCH 18/55] Have scene.atmosphere work when globe is off
---
.../Scene/DynamicAtmosphereLightingType.js | 22 +++++++++++++++++++
packages/engine/Source/Scene/Scene.js | 10 ++++++---
packages/engine/Source/Scene/SkyAtmosphere.js | 10 +++++++++
3 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
index 5078ac2b861d..892e072a5f08 100644
--- a/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
+++ b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
@@ -31,4 +31,26 @@ const DynamicAtmosphereLightingType = {
SUNLIGHT: 2,
};
+/**
+ * Get the lighting enum from the older globe flags
+ *
+ * @param {Globe} globe The globe
+ * @return {DynamicAtmosphereLightingType} The corresponding enum value
+ *
+ * @private
+ */
+DynamicAtmosphereLightingType.fromGlobeFlags = function (globe) {
+ const lightingOn = globe.enableLighting && globe.dynamicAtmosphereLighting;
+ if (!lightingOn) {
+ return DynamicAtmosphereLightingType.OFF;
+ }
+
+ // Force sunlight
+ if (globe.dynamicAtmosphereLightingFromSun) {
+ return DynamicAtmosphereLightingType.SUNLIGHT;
+ }
+
+ return DynamicAtmosphereLightingType.SCENE_LIGHT;
+};
+
export default Object.freeze(DynamicAtmosphereLightingType);
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index 2d254f3d8c8f..db269eadb67a 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -47,6 +47,7 @@ import DebugCameraPrimitive from "./DebugCameraPrimitive.js";
import DepthPlane from "./DepthPlane.js";
import DerivedCommand from "./DerivedCommand.js";
import DeviceOrientationCameraController from "./DeviceOrientationCameraController.js";
+import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType.js";
import Fog from "./Fog.js";
import FrameState from "./FrameState.js";
import GlobeTranslucencyState from "./GlobeTranslucencyState.js";
@@ -3170,6 +3171,7 @@ Scene.prototype.updateEnvironment = function () {
const environmentState = this._environmentState;
const renderPass = frameState.passes.render;
const offscreenPass = frameState.passes.offscreen;
+ const atmosphere = this.atmosphere;
const skyAtmosphere = this.skyAtmosphere;
const globe = this.globe;
const globeTranslucencyState = this._globeTranslucencyState;
@@ -3188,17 +3190,19 @@ Scene.prototype.updateEnvironment = function () {
} else {
if (defined(skyAtmosphere)) {
if (defined(globe)) {
- skyAtmosphere.setDynamicAtmosphereColor(
- globe.enableLighting && globe.dynamicAtmosphereLighting,
- globe.dynamicAtmosphereLightingFromSun
+ skyAtmosphere.setDynamicLighting(
+ DynamicAtmosphereLightingType.fromGlobeFlags(globe)
);
environmentState.isReadyForAtmosphere =
environmentState.isReadyForAtmosphere ||
!globe.show ||
globe._surface._tilesToRender.length > 0;
} else {
+ const dynamicLighting = atmosphere.dynamicLighting;
+ skyAtmosphere.setDynamicAtmosphereColor(dynamicLighting);
environmentState.isReadyForAtmosphere = true;
}
+
environmentState.skyAtmosphereCommand = skyAtmosphere.update(
frameState,
globe
diff --git a/packages/engine/Source/Scene/SkyAtmosphere.js b/packages/engine/Source/Scene/SkyAtmosphere.js
index 11cccc5f23f5..9c4329052ae5 100644
--- a/packages/engine/Source/Scene/SkyAtmosphere.js
+++ b/packages/engine/Source/Scene/SkyAtmosphere.js
@@ -227,6 +227,16 @@ SkyAtmosphere.prototype.setDynamicAtmosphereColor = function (
this._radiiAndDynamicAtmosphereColor.z = lightEnum;
};
+/**
+ * Set the dynamic lighting enum value for the shader
+ * @param {DynamicAtmosphereLightingType} lightingEnum
+ *
+ * @private
+ */
+SkyAtmosphere.prototype.setDynamicLighting = function (lightingEnum) {
+ this._radiiAndDynamicAtmosphereColor.z = lightingEnum;
+};
+
const scratchModelMatrix = new Matrix4();
/**
From bce6afcc60065fd215ec36c5f8f5fe2f9fd55f72 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 10:39:21 -0500
Subject: [PATCH 19/55] darken dynamic lighting
---
.../engine/Source/Renderer/AutomaticUniforms.js | 13 +++++++++++++
.../engine/Source/Shaders/Model/FogStageFS.glsl | 14 ++++++--------
2 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index 4d6beab2cf9b..8e34b2d75cf5 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -1592,6 +1592,19 @@ const AutomaticUniforms = {
},
}),
+ /**
+ * An automatic GLSL uniform scalar used to set a minimum brightness when dynamic lighting is applied to fog.
+ *
+ * @see czm_fog
+ */
+ czm_fogMinimumBrightness: new AutomaticUniform({
+ size: 1,
+ datatype: WebGLConstants.FLOAT,
+ getValue: function (uniformState) {
+ return uniformState.fogMinimumBrightness;
+ },
+ }),
+
/**
* An automatic uniform representing the color shift for the atmosphere in HSB color space
*
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 6c6b2eb23337..6c74bf721a44 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -62,14 +62,12 @@ vec3 computeFogColor(vec3 positionMC) {
vec4 groundAtmosphereColor = czm_computeAtmosphereColor(ellipsoidPositionWC, lightDirection, rayleighColor, mieColor, opacity);
vec3 fogColor = groundAtmosphereColor.rgb;
- // Darken the fog
-
- // If there is lighting, apply that to the fog.
-//#if defined(DYNAMIC_ATMOSPHERE_LIGHTING) && (defined(ENABLE_VERTEX_LIGHTING) || defined(ENABLE_DAYNIGHT_SHADING))
- //const float u_minimumBrightness = 0.03; // TODO: pull this from the light shader
- //float darken = clamp(dot(normalize(czm_viewerPositionWC), atmosphereLightDirection), u_minimumBrightness, 1.0);
- //fogColor *= darken;
-//#endif
+ // If there is dynamic lighting, apply that to the fog.
+ const float OFF = 0.0;
+ if (czm_atmosphereDynamicLighting != OFF) {
+ float darken = clamp(dot(normalize(czm_viewerPositionWC), lightDirection), czm_fogMinimumBrightness, 1.0);
+ fogColor *= darken;
+ }
// Tonemap if HDR rendering is disabled
#ifndef HDR
From 3112490fdfadce67c8038b4e98573ec4882801b5 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 11:23:57 -0500
Subject: [PATCH 20/55] Remove dead function
---
packages/engine/Source/Scene/Scene.js | 2 +-
packages/engine/Source/Scene/SkyAtmosphere.js | 11 -----------
packages/engine/Specs/Scene/SkyAtmosphereSpec.js | 13 +++++++------
3 files changed, 8 insertions(+), 18 deletions(-)
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index db269eadb67a..8e5a9be1f1a6 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -3199,7 +3199,7 @@ Scene.prototype.updateEnvironment = function () {
globe._surface._tilesToRender.length > 0;
} else {
const dynamicLighting = atmosphere.dynamicLighting;
- skyAtmosphere.setDynamicAtmosphereColor(dynamicLighting);
+ skyAtmosphere.setDynamicLighting(dynamicLighting);
environmentState.isReadyForAtmosphere = true;
}
diff --git a/packages/engine/Source/Scene/SkyAtmosphere.js b/packages/engine/Source/Scene/SkyAtmosphere.js
index 9c4329052ae5..c97a7a88e098 100644
--- a/packages/engine/Source/Scene/SkyAtmosphere.js
+++ b/packages/engine/Source/Scene/SkyAtmosphere.js
@@ -216,17 +216,6 @@ Object.defineProperties(SkyAtmosphere.prototype, {
},
});
-/**
- * @private
- */
-SkyAtmosphere.prototype.setDynamicAtmosphereColor = function (
- enableLighting,
- useSunDirection
-) {
- const lightEnum = enableLighting ? (useSunDirection ? 2.0 : 1.0) : 0.0;
- this._radiiAndDynamicAtmosphereColor.z = lightEnum;
-};
-
/**
* Set the dynamic lighting enum value for the shader
* @param {DynamicAtmosphereLightingType} lightingEnum
diff --git a/packages/engine/Specs/Scene/SkyAtmosphereSpec.js b/packages/engine/Specs/Scene/SkyAtmosphereSpec.js
index edc027b84dea..f3d62d2f4546 100644
--- a/packages/engine/Specs/Scene/SkyAtmosphereSpec.js
+++ b/packages/engine/Specs/Scene/SkyAtmosphereSpec.js
@@ -1,5 +1,6 @@
import {
Cartesian3,
+ DynamicAtmosphereLightingType,
Ellipsoid,
SceneMode,
SkyAtmosphere,
@@ -52,9 +53,9 @@ describe(
s.destroy();
});
- it("draws sky with setDynamicAtmosphereColor set to true", function () {
+ it("draws sky with dynamic lighting (scene light source)", function () {
const s = new SkyAtmosphere();
- s.setDynamicAtmosphereColor(true, false);
+ s.setDynamicLighting(DynamicAtmosphereLightingType.SCENE_LIGHT);
expect(scene).toRender([0, 0, 0, 255]);
scene.render();
@@ -67,9 +68,9 @@ describe(
s.destroy();
});
- it("draws sky with setDynamicAtmosphereColor set to true using the sun direction", function () {
+ it("draws sky with dynamic lighting (sunlight)", function () {
const s = new SkyAtmosphere();
- s.setDynamicAtmosphereColor(true, true);
+ s.setDynamicLighting(DynamicAtmosphereLightingType.SUNLIGHT);
expect(scene).toRender([0, 0, 0, 255]);
scene.render();
@@ -82,9 +83,9 @@ describe(
s.destroy();
});
- it("draws sky with setDynamicAtmosphereColor set to false", function () {
+ it("draws sky with dynamic lighting off", function () {
const s = new SkyAtmosphere();
- s.setDynamicAtmosphereColor(false, false);
+ s.setDynamicLighting(DynamicAtmosphereLightingType.OFF);
expect(scene).toRender([0, 0, 0, 255]);
scene.render();
From b57ea043df37c3f68684b23105f14ba184aa82c2 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 11:30:51 -0500
Subject: [PATCH 21/55] Remove debugging code
---
.../Shaders/Builtin/Functions/computeScattering.glsl | 4 ----
packages/engine/Source/Shaders/Model/FogStageFS.glsl | 9 ---------
2 files changed, 13 deletions(-)
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
index 63cef0c248eb..284ead0846bb 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
@@ -154,8 +154,4 @@ void czm_computeScattering(
// Compute the transmittance i.e. how much light is passing through the atmosphere.
opacity = length(exp(-((czm_atmosphereMieCoefficient * opticalDepth.y) + (czm_atmosphereRayleighCoefficient * opticalDepth.x))));
-
- //rayleighColor = vec3(atmosphereInnerRadius / 1.0e7, lastVals.x / 1.0e7, 0.0); //lastVals;
- //vec3(float(PRIMARY_STEPS) / 16.0, float(LIGHT_STEPS) / 4.0, 0.0);//mieAccumulation; //rayleighAccumulation;
- //rayleighColor = w_stop_gt_lprl /*w_inside_atmosphere*/ * vec3(1.0, 1.0, 0.0); //w_inside_atmosphere, 0.0);
}
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 6c74bf721a44..8350a0438e49 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -56,9 +56,6 @@ vec3 computeFogColor(vec3 positionMC) {
opacity
);
- //rayleighColor = vec3(1.0, 0.0, 0.0);
- //mieColor = vec3(0.0, 1.0, 0.0);
-
vec4 groundAtmosphereColor = czm_computeAtmosphereColor(ellipsoidPositionWC, lightDirection, rayleighColor, mieColor, opacity);
vec3 fogColor = groundAtmosphereColor.rgb;
@@ -75,12 +72,7 @@ vec3 computeFogColor(vec3 positionMC) {
fogColor.rgb = czm_inverseGamma(fogColor.rgb);
#endif
- // TODO: fogColor.a is only used for ground atmosphere... is that needed?
-
- //return positionWC / 1e7;
- //return rayleighColor;
return fogColor.rgb;
- //return mieColor;
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
@@ -97,5 +89,4 @@ void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
vec3 withFog = czm_fog(distanceToCamera, color.rgb, fogColor, fogModifier);
color = vec4(withFog, color.a);
- //color = mix(color, vec4(fogColor, 1.0), 0.5);
}
From bd7f7b95595cdb4280c3384bb70f84ebb84120aa Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 11:35:11 -0500
Subject: [PATCH 22/55] Fix typo
---
packages/engine/Source/Scene/Model/FogPipelineStage.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
index b23cb4654ae9..09b72fedf068 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -9,7 +9,7 @@ import ShaderDestination from "../../Renderer/ShaderDestination.js";
* @private
*/
const FogPipelineStage = {
- name: "FogColorPipelineStage", // Helps with debugging
+ name: "FogPipelineStage", // Helps with debugging
};
FogPipelineStage.process = function (renderResources, model, frameState) {
From c68e971dbb90bd350662b5e0e162dead4604bf30 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 11:52:01 -0500
Subject: [PATCH 23/55] Use common function for SkyAtmosphere
---
.../engine/Source/Renderer/AutomaticUniforms.js | 1 +
.../getDynamicAtmosphereLightDirection.glsl | 7 +++----
.../engine/Source/Shaders/Model/FogStageFS.glsl | 2 +-
.../Source/Shaders/SkyAtmosphereCommon.glsl | 16 +++-------------
.../engine/Source/Shaders/SkyAtmosphereFS.glsl | 5 +++--
.../engine/Source/Shaders/SkyAtmosphereVS.glsl | 5 +++--
6 files changed, 14 insertions(+), 22 deletions(-)
diff --git a/packages/engine/Source/Renderer/AutomaticUniforms.js b/packages/engine/Source/Renderer/AutomaticUniforms.js
index 8e34b2d75cf5..0b16c23ae9a3 100644
--- a/packages/engine/Source/Renderer/AutomaticUniforms.js
+++ b/packages/engine/Source/Renderer/AutomaticUniforms.js
@@ -1696,6 +1696,7 @@ const AutomaticUniforms = {
return uniformState.atmosphereMieAnisotropy;
},
}),
+
/**
* An automatic uniform representing which light source to use for dynamic lighting
*
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl b/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
index 2dd991d28026..9b934362b090 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
@@ -1,16 +1,15 @@
/**
- * Select which direction vector to use for dynamic atmosphere lighting based on the czm_atmosphereDynamicLighting enum.
+ * Select which direction vector to use for dynamic atmosphere lighting based on an enum value
*
* @name czm_getDynamicAtmosphereLightDirection
* @glslfunction
* @see DynamicAtmosphereLightingType.js
*
* @param {vec3} positionWC the position of the vertex/fragment in world coordinates. This is normalized and returned when dynamic lighting is turned off.
+ * @param {float} lightEnum The enum value for selecting between light sources.
* @return {vec3} The normalized light direction vector. Depending on the enum value, it is either positionWC, czm_lightDirectionWC or czm_sunDirectionWC
*/
-vec3 czm_getDynamicAtmosphereLightDirection(vec3 positionWC) {
- float lightEnum = czm_atmosphereDynamicLighting;
-
+vec3 czm_getDynamicAtmosphereLightDirection(vec3 positionWC, float lightEnum) {
const float OFF = 0.0;
const float SCENE_LIGHT = 1.0;
const float SUNLIGHT = 2.0;
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 8350a0438e49..a6c6fa74d652 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -45,7 +45,7 @@ vec3 computeFogColor(vec3 positionMC) {
float opacity;
vec3 ellipsoidPositionWC = computeEllipsoidPositionWC(positionMC);
- vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(ellipsoidPositionWC);
+ vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(ellipsoidPositionWC, czm_atmosphereDynamicLighting);
// The fog color is derived from the ground atmosphere color
czm_computeGroundAtmosphereScattering(
diff --git a/packages/engine/Source/Shaders/SkyAtmosphereCommon.glsl b/packages/engine/Source/Shaders/SkyAtmosphereCommon.glsl
index 0c27d89f9311..9a958474b9d4 100644
--- a/packages/engine/Source/Shaders/SkyAtmosphereCommon.glsl
+++ b/packages/engine/Source/Shaders/SkyAtmosphereCommon.glsl
@@ -8,16 +8,6 @@ float interpolateByDistance(vec4 nearFarScalar, float distance)
return mix(startValue, endValue, t);
}
-vec3 getLightDirection(vec3 positionWC)
-{
- float lightEnum = u_radiiAndDynamicAtmosphereColor.z;
- vec3 lightDirection =
- positionWC * float(lightEnum == 0.0) +
- czm_lightDirectionWC * float(lightEnum == 1.0) +
- czm_sunDirectionWC * float(lightEnum == 2.0);
- return normalize(lightDirection);
-}
-
void computeAtmosphereScattering(vec3 positionWC, vec3 lightDirection, out vec3 rayleighColor, out vec3 mieColor, out float opacity, out float underTranslucentGlobe)
{
float ellipsoidRadiiDifference = czm_ellipsoidRadii.x - czm_ellipsoidRadii.z;
@@ -28,7 +18,7 @@ void computeAtmosphereScattering(vec3 positionWC, vec3 lightDirection, out vec3
float distanceAdjustModifier = ellipsoidRadiiDifference / 2.0;
float distanceAdjust = distanceAdjustModifier * clamp((czm_eyeHeight - distanceAdjustMin) / (distanceAdjustMax - distanceAdjustMin), 0.0, 1.0);
- // Since atmosphere scattering assumes the atmosphere is a spherical shell, we compute an inner radius of the atmosphere best fit
+ // Since atmosphere scattering assumes the atmosphere is a spherical shell, we compute an inner radius of the atmosphere best fit
// for the position on the ellipsoid.
float radiusAdjust = (ellipsoidRadiiDifference / 4.0) + distanceAdjust;
float atmosphereInnerRadius = (length(czm_viewerPositionWC) - czm_eyeHeight) - radiusAdjust;
@@ -46,7 +36,7 @@ void computeAtmosphereScattering(vec3 positionWC, vec3 lightDirection, out vec3
// Check for intersection with the inner radius of the atmopshere.
czm_raySegment primaryRayEarthIntersect = czm_raySphereIntersectionInterval(primaryRay, vec3(0.0), atmosphereInnerRadius + radiusAdjust);
if (primaryRayEarthIntersect.start > 0.0 && primaryRayEarthIntersect.stop > 0.0) {
-
+
// Compute position on globe.
vec3 direction = normalize(positionWC);
czm_ray ellipsoidRay = czm_ray(positionWC, -direction);
@@ -62,7 +52,7 @@ void computeAtmosphereScattering(vec3 positionWC, vec3 lightDirection, out vec3
vec3 nearColor = vec3(0.0);
rayleighColor = mix(nearColor, horizonColor, exp(-angle) * opacity);
-
+
// Set the traslucent flag to avoid alpha adjustment in computeFinalColor funciton.
underTranslucentGlobe = 1.0;
return;
diff --git a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
index d0c67c94d440..db07b7fbd1ec 100644
--- a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
+++ b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
@@ -11,8 +11,9 @@ in float v_translucent;
void main (void)
{
- vec3 lightDirection = getLightDirection(v_outerPositionWC);
-
+ float lightEnum = u_radiiAndDynamicAtmosphereColor.z;
+ vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(v_outerPositionWC, lightEnum);
+
vec3 mieColor;
vec3 rayleighColor;
float opacity;
diff --git a/packages/engine/Source/Shaders/SkyAtmosphereVS.glsl b/packages/engine/Source/Shaders/SkyAtmosphereVS.glsl
index 88d0bf59bcf8..8ed97f1a0979 100644
--- a/packages/engine/Source/Shaders/SkyAtmosphereVS.glsl
+++ b/packages/engine/Source/Shaders/SkyAtmosphereVS.glsl
@@ -12,7 +12,8 @@ out float v_translucent;
void main(void)
{
vec4 positionWC = czm_model * position;
- vec3 lightDirection = getLightDirection(positionWC.xyz);
+ float lightEnum = u_radiiAndDynamicAtmosphereColor.z;
+ vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(positionWC.xyz, lightEnum);
#ifndef PER_FRAGMENT_ATMOSPHERE
computeAtmosphereScattering(
@@ -24,7 +25,7 @@ void main(void)
v_translucent
);
#endif
-
+
v_outerPositionWC = positionWC.xyz;
gl_Position = czm_modelViewProjection * position;
}
From a458be139556441bf0a22b7290dd498981836941 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 11:52:43 -0500
Subject: [PATCH 24/55] Remove debugging code
---
.../Source/Shaders/Builtin/Functions/computeScattering.glsl | 5 -----
1 file changed, 5 deletions(-)
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
index 284ead0846bb..f3c5bb9c5d57 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
@@ -80,8 +80,6 @@ void czm_computeScattering(
vec2 opticalDepth = vec2(0.0);
vec2 heightScale = vec2(czm_atmosphereRayleighScaleHeight, czm_atmosphereMieScaleHeight);
- //vec3 lastVals = vec3(0.0);
-
// Sample positions on the primary ray.
for (int i = 0; i < PRIMARY_STEPS_MAX; ++i) {
@@ -130,9 +128,6 @@ void czm_computeScattering(
// Increment distance on light ray.
lightPositionLength += lightStepLength;
-
- //lastVals = vec3(length(lightPosition));
- //lastVals = vec3(float(lightHeight < 0.0), lightHeight / 1000.0, lightOpticalDepth);
}
// Compute attenuation via the primary ray and the light ray.
From 63efb02eb75bcf9fadc7b5ad57c4e033e3c8d974 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 11:54:52 -0500
Subject: [PATCH 25/55] Remove unneeded file
---
.../Functions/computeEllipsoidPosition.glsl | 30 -------------------
1 file changed, 30 deletions(-)
delete mode 100644 packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
deleted file mode 100644
index ddeeee0df3a8..000000000000
--- a/packages/engine/Source/Shaders/Builtin/Functions/computeEllipsoidPosition.glsl
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Compute the WC position on the elipsoid of the current fragment. The result
- * is low-precision due to use of 32-bit floats.
- *
- * @return {vec3} The position in world coordinates.
- */
-
- /*
-vec3 czm_computeEllipsoidPosition()
-{
- float mpp = czm_metersPerPixel(vec4(0.0, 0.0, -czm_currentFrustum.x, 1.0), 1.0);
- vec2 xy = gl_FragCoord.xy / czm_viewport.zw * 2.0 - vec2(1.0);
- xy *= czm_viewport.zw * mpp * 0.5;
-
- vec3 direction = normalize(vec3(xy, -czm_currentFrustum.x));
- czm_ray ray = czm_ray(vec3(0.0), direction);
-
- vec3 ellipsoid_center = czm_view[3].xyz;
-
- czm_raySegment intersection = czm_rayEllipsoidIntersectionInterval(ray, ellipsoid_center, czm_ellipsoidInverseRadii);
-
- vec3 ellipsoidPosition = czm_pointAlongRay(ray, intersection.start);
- return (czm_inverseView * vec4(ellipsoidPosition, 1.0)).xyz;
-}
-*/
-
-vec3 czm_computeEllipsoidPosition()
-{
- return vec3(0.0);
-}
From e0314cd45a00d40bef3ef71f077692de2ff0d09f Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 13:41:23 -0500
Subject: [PATCH 26/55] Add a uniform for whether the tile is in fog
---
.../Source/Scene/GlobeSurfaceTileProvider.js | 4 +++-
.../Source/Scene/Model/FogPipelineStage.js | 22 ++++++++++++++++++-
.../Scene/Model/ModelRuntimePrimitive.js | 6 -----
.../Source/Scene/Model/ModelSceneGraph.js | 6 +++++
packages/engine/Source/Shaders/GlobeFS.glsl | 2 --
.../Source/Shaders/Model/FogStageFS.glsl | 6 +++++
6 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js b/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js
index e32b797efc48..a6851b02e469 100644
--- a/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js
+++ b/packages/engine/Source/Scene/GlobeSurfaceTileProvider.js
@@ -2502,7 +2502,9 @@ function addDrawCommandsForTile(tileProvider, tile, frameState) {
uniformMapProperties.localizedTranslucencyRectangle
);
- // For performance, use fog in the shader only when the tile is in fog.
+ // For performance, render fog only when fog is enabled and the effect of
+ // fog would be non-negligible. This prevents the shader from running when
+ // the camera is in space, for example.
const applyFog =
enableFog &&
CesiumMath.fog(tile._distance, frameState.fog.density) >
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
index 09b72fedf068..2907582f07ad 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -1,5 +1,7 @@
-import FogStageFS from "../../Shaders/Model/FogStageFS.js";
+import Cartesian3 from "../../Core/Cartesian3.js";
+import CesiumMath from "../../Core/Math.js";
import ShaderDestination from "../../Renderer/ShaderDestination.js";
+import FogStageFS from "../../Shaders/Model/FogStageFS.js";
/**
* The fog color pipeline stage is responsible for applying fog to tiles in the distance in horizon views.
@@ -17,6 +19,24 @@ FogPipelineStage.process = function (renderResources, model, frameState) {
shaderBuilder.addDefine("HAS_FOG", undefined, ShaderDestination.FRAGMENT);
shaderBuilder.addFragmentLines([FogStageFS]);
+
+ // Add a uniform so fog is only calculated when the effect would
+ // be non-negligible For example when the camera is in space, fog density decreases
+ // to 0 so fog shouldn't be rendered. Since this state may change rapidly if
+ // the camera is moving, this is implemented as a uniform, not a define.
+ shaderBuilder.addUniform("bool", "u_isInFog", ShaderDestination.FRAGMENT);
+ renderResources.uniformMap.u_isInFog = function () {
+ // We only need a rough measure of distance to the model, so measure
+ // from the camera to the bounding sphere center.
+ const distance = Cartesian3.distance(
+ frameState.camera.position,
+ model.boundingSphere.center
+ );
+
+ return (
+ CesiumMath.fog(distance, frameState.fog.density) > CesiumMath.EPSILON3
+ );
+ };
};
export default FogPipelineStage;
diff --git a/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js b/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
index e168b6b280e8..ed82ca255cf4 100644
--- a/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
+++ b/packages/engine/Source/Scene/Model/ModelRuntimePrimitive.js
@@ -11,7 +11,6 @@ import CustomShaderMode from "./CustomShaderMode.js";
import CustomShaderPipelineStage from "./CustomShaderPipelineStage.js";
import DequantizationPipelineStage from "./DequantizationPipelineStage.js";
import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js";
-import FogPipelineStage from "./FogPipelineStage.js";
import GeometryPipelineStage from "./GeometryPipelineStage.js";
import LightingPipelineStage from "./LightingPipelineStage.js";
import MaterialPipelineStage from "./MaterialPipelineStage.js";
@@ -200,7 +199,6 @@ ModelRuntimePrimitive.prototype.configurePipeline = function (frameState) {
const mode = frameState.mode;
const use2D =
mode !== SceneMode.SCENE3D && !frameState.scene3DOnly && model._projectTo2D;
- const fogRenderable = frameState.fog.enabled && frameState.fog.renderable;
const exaggerateTerrain = frameState.verticalExaggeration !== 1.0;
const hasMorphTargets =
@@ -305,10 +303,6 @@ ModelRuntimePrimitive.prototype.configurePipeline = function (frameState) {
pipelineStages.push(AlphaPipelineStage);
- if (fogRenderable) {
- pipelineStages.push(FogPipelineStage);
- }
-
pipelineStages.push(PrimitiveStatisticsPipelineStage);
return;
diff --git a/packages/engine/Source/Scene/Model/ModelSceneGraph.js b/packages/engine/Source/Scene/Model/ModelSceneGraph.js
index 1ff305d3adcf..72e3cbf11d7b 100644
--- a/packages/engine/Source/Scene/Model/ModelSceneGraph.js
+++ b/packages/engine/Source/Scene/Model/ModelSceneGraph.js
@@ -9,6 +9,7 @@ import SceneMode from "../SceneMode.js";
import SplitDirection from "../SplitDirection.js";
import buildDrawCommand from "./buildDrawCommand.js";
import TilesetPipelineStage from "./TilesetPipelineStage.js";
+import FogPipelineStage from "./FogPipelineStage.js";
import ImageBasedLightingPipelineStage from "./ImageBasedLightingPipelineStage.js";
import ModelArticulation from "./ModelArticulation.js";
import ModelColorPipelineStage from "./ModelColorPipelineStage.js";
@@ -606,6 +607,7 @@ ModelSceneGraph.prototype.configurePipeline = function (frameState) {
modelPipelineStages.length = 0;
const model = this._model;
+ const fogRenderable = frameState.fog.enabled && frameState.fog.renderable;
if (defined(model.color)) {
modelPipelineStages.push(ModelColorPipelineStage);
@@ -638,6 +640,10 @@ ModelSceneGraph.prototype.configurePipeline = function (frameState) {
if (ModelType.is3DTiles(model.type)) {
modelPipelineStages.push(TilesetPipelineStage);
}
+
+ if (fogRenderable) {
+ modelPipelineStages.push(FogPipelineStage);
+ }
};
ModelSceneGraph.prototype.update = function (frameState, updateForAnimations) {
diff --git a/packages/engine/Source/Shaders/GlobeFS.glsl b/packages/engine/Source/Shaders/GlobeFS.glsl
index 79d5960159ee..3d181e60ee0c 100644
--- a/packages/engine/Source/Shaders/GlobeFS.glsl
+++ b/packages/engine/Source/Shaders/GlobeFS.glsl
@@ -523,8 +523,6 @@ void main()
finalColor.rgb = mix(finalColor.rgb, finalAtmosphereColor.rgb, fade);
#endif
-
- //finalColor.rgb = computeEllipsoidPosition() / 1e7;
}
#endif
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index a6c6fa74d652..5d319ded73e7 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -76,6 +76,12 @@ vec3 computeFogColor(vec3 positionMC) {
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
+ if (!u_isInFog) {
+ // Debugging
+ //color.rgb = vec3(1.0, 1.0, 0.0);
+ return;
+ }
+
vec3 fogColor = computeFogColor(attributes.positionMC);
// Note: camera is far away (distance > nightFadeOutDistance), scattering is computed in the fragment shader.
From 7d4e2fa7a49957ef4272468feb0810e905ddaded Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 13:43:03 -0500
Subject: [PATCH 27/55] remove debugging code
---
packages/engine/Source/Shaders/Model/FogStageFS.glsl | 2 --
1 file changed, 2 deletions(-)
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 5d319ded73e7..5a7d0e471054 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -77,8 +77,6 @@ vec3 computeFogColor(vec3 positionMC) {
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
if (!u_isInFog) {
- // Debugging
- //color.rgb = vec3(1.0, 1.0, 0.0);
return;
}
From d78360aab6cf9072fe92114d25aeaff9af067199 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 16:10:10 -0500
Subject: [PATCH 28/55] Scattering is to be computed in the vertex shader
---
.../engine/Source/Renderer/UniformState.js | 12 +++
.../Source/Scene/Model/FogPipelineStage.js | 14 +++-
packages/engine/Source/Shaders/GlobeFS.glsl | 2 +
.../Source/Shaders/Model/FogStageFS.glsl | 82 +++++++++++--------
.../Source/Shaders/Model/FogStageVS.glsl | 12 +++
.../Source/Shaders/Model/GeometryStageFS.glsl | 2 +-
.../Source/Shaders/Model/GeometryStageVS.glsl | 8 +-
.../engine/Source/Shaders/Model/ModelVS.glsl | 12 ++-
8 files changed, 99 insertions(+), 45 deletions(-)
create mode 100644 packages/engine/Source/Shaders/Model/FogStageVS.glsl
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index 75bfbde2d316..d4745818c480 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -161,6 +161,7 @@ function UniformState() {
this._specularEnvironmentMapsMaximumLOD = undefined;
this._fogDensity = undefined;
+ this._fogMinimumBrightness = undefined;
this._atmosphereHsbShift = undefined;
this._atmosphereLightIntensity = undefined;
@@ -924,6 +925,17 @@ Object.defineProperties(UniformState.prototype, {
},
},
+ /**
+ * A scalar used as a minimum value when brightening fog
+ * @memberof UniformState.prototype
+ * @type {number}
+ */
+ fogMinimumBrightness: {
+ get: function () {
+ return this._fogMinimumBrightness;
+ },
+ },
+
/**
* A color shift to apply to the atmosphere color in HSB.
* @memberof UniformState.prototype
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
index 2907582f07ad..971f6cb03295 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -2,6 +2,7 @@ import Cartesian3 from "../../Core/Cartesian3.js";
import CesiumMath from "../../Core/Math.js";
import ShaderDestination from "../../Renderer/ShaderDestination.js";
import FogStageFS from "../../Shaders/Model/FogStageFS.js";
+import FogStageVS from "../../Shaders/Model/FogStageVS.js";
/**
* The fog color pipeline stage is responsible for applying fog to tiles in the distance in horizon views.
@@ -17,7 +18,18 @@ const FogPipelineStage = {
FogPipelineStage.process = function (renderResources, model, frameState) {
const shaderBuilder = renderResources.shaderBuilder;
- shaderBuilder.addDefine("HAS_FOG", undefined, ShaderDestination.FRAGMENT);
+ shaderBuilder.addDefine(
+ "COMPUTE_POSITION_WC_ATMOSPHERE",
+ undefined,
+ ShaderDestination.BOTH
+ );
+
+ shaderBuilder.addVarying("vec3", "v_atmosphereRayleighColor");
+ shaderBuilder.addVarying("vec3", "v_atmosphereMieColor");
+ shaderBuilder.addVarying("float", "v_atmosphereOpacity");
+
+ shaderBuilder.addDefine("HAS_FOG", undefined, ShaderDestination.BOTH);
+ shaderBuilder.addVertexLines([FogStageVS]);
shaderBuilder.addFragmentLines([FogStageFS]);
// Add a uniform so fog is only calculated when the effect would
diff --git a/packages/engine/Source/Shaders/GlobeFS.glsl b/packages/engine/Source/Shaders/GlobeFS.glsl
index 3d181e60ee0c..e344dfec2bfc 100644
--- a/packages/engine/Source/Shaders/GlobeFS.glsl
+++ b/packages/engine/Source/Shaders/GlobeFS.glsl
@@ -496,6 +496,8 @@ void main()
finalColor = vec4(czm_fog(v_distance, finalColor.rgb, fogColor.rgb, modifier), finalColor.a);
#else
+ // Apply ground atmosphere. This happens when the camera is far away from the earth.
+
// The transmittance is based on optical depth i.e. the length of segment of the ray inside the atmosphere.
// This value is larger near the "circumference", as it is further away from the camera. We use it to
// brighten up that area of the ground atmosphere.
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 5a7d0e471054..870e040d7132 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -39,24 +39,8 @@ vec3 computeEllipsoidPositionWC(vec3 positionMC) {
return vec3(nearestPoint.x * normalize(positionWC.xy), nearestPoint.y);
}
-vec3 computeFogColor(vec3 positionMC) {
- vec3 rayleighColor = vec3(0.0, 0.0, 1.0);
- vec3 mieColor;
- float opacity;
-
- vec3 ellipsoidPositionWC = computeEllipsoidPositionWC(positionMC);
- vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(ellipsoidPositionWC, czm_atmosphereDynamicLighting);
+void applyFog(inout vec4 color, vec4 groundAtmosphereColor, vec3 lightDirection, float distanceToCamera) {
- // The fog color is derived from the ground atmosphere color
- czm_computeGroundAtmosphereScattering(
- ellipsoidPositionWC,
- lightDirection,
- rayleighColor,
- mieColor,
- opacity
- );
-
- vec4 groundAtmosphereColor = czm_computeAtmosphereColor(ellipsoidPositionWC, lightDirection, rayleighColor, mieColor, opacity);
vec3 fogColor = groundAtmosphereColor.rgb;
// If there is dynamic lighting, apply that to the fog.
@@ -67,30 +51,58 @@ vec3 computeFogColor(vec3 positionMC) {
}
// Tonemap if HDR rendering is disabled
-#ifndef HDR
- fogColor.rgb = czm_acesTonemapping(fogColor.rgb);
- fogColor.rgb = czm_inverseGamma(fogColor.rgb);
-#endif
+ #ifndef HDR
+ fogColor.rgb = czm_acesTonemapping(fogColor.rgb);
+ fogColor.rgb = czm_inverseGamma(fogColor.rgb);
+ #endif
- return fogColor.rgb;
+ // Matches the constant in GlobeFS.glsl. This makes the fog falloff
+ // more gradual.
+ const float fogModifier = 0.15;
+ vec3 withFog = czm_fog(distanceToCamera, color.rgb, fogColor, fogModifier);
+ color = vec4(withFog, color.a);
}
void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
- if (!u_isInFog) {
- return;
- }
+ vec3 rayleighColor;
+ vec3 mieColor;
+ float opacity;
- vec3 fogColor = computeFogColor(attributes.positionMC);
+ vec3 positionWC;
+ vec3 lightDirection;
+
+ // When the camera is in space, compute the position per-fragment for
+ // more accurate ground atmosphere. All other cases will use
+ //
+ // The if condition will be added in https://github.com/CesiumGS/cesium/issues/11717
+ if (false) {
+ positionWC = computeEllipsoidPositionWC(attributes.positionMC);
+ lightDirection = czm_getDynamicAtmosphereLightDirection(positionWC, czm_atmosphereDynamicLighting);
+
+ // The fog color is derived from the ground atmosphere color
+ czm_computeGroundAtmosphereScattering(
+ positionWC,
+ lightDirection,
+ rayleighColor,
+ mieColor,
+ opacity
+ );
+ } else {
+ positionWC = attributes.positionWC;
+ lightDirection = czm_getDynamicAtmosphereLightDirection(positionWC, czm_atmosphereDynamicLighting);
+ rayleighColor = v_atmosphereRayleighColor;
+ mieColor = v_atmosphereMieColor;
+ opacity = v_atmosphereOpacity;
+ }
- // Note: camera is far away (distance > nightFadeOutDistance), scattering is computed in the fragment shader.
- // otherwise in the vertex shader. but for prototyping, I'll do everything in the FS for simplicity
+ //color correct rayleigh and mie colors
- // Matches the constant in GlobeFS.glsl. This makes the fog falloff
- // more gradual.
- const float fogModifier = 0.15;
- float distanceToCamera = attributes.positionEC.z;
- // where to get distance?
- vec3 withFog = czm_fog(distanceToCamera, color.rgb, fogColor, fogModifier);
+ vec4 groundAtmosphereColor = czm_computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
- color = vec4(withFog, color.a);
+ if (u_isInFog) {
+ float distanceToCamera = length(attributes.positionEC);
+ applyFog(color, groundAtmosphereColor, lightDirection, distanceToCamera);
+ } else {
+ // Ground atmosphere
+ }
}
diff --git a/packages/engine/Source/Shaders/Model/FogStageVS.glsl b/packages/engine/Source/Shaders/Model/FogStageVS.glsl
new file mode 100644
index 000000000000..6a81b426105f
--- /dev/null
+++ b/packages/engine/Source/Shaders/Model/FogStageVS.glsl
@@ -0,0 +1,12 @@
+void fogStage(ProcessedAttributes attributes) {
+ vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(v_positionWC, czm_atmosphereDynamicLighting);
+
+ czm_computeGroundAtmosphereScattering(
+ // This assumes the geometry stage came before this.
+ v_positionWC,
+ lightDirection,
+ v_atmosphereRayleighColor,
+ v_atmosphereMieColor,
+ v_atmosphereOpacity
+ );
+}
diff --git a/packages/engine/Source/Shaders/Model/GeometryStageFS.glsl b/packages/engine/Source/Shaders/Model/GeometryStageFS.glsl
index 4e52d7b2087d..6f5217a11ba3 100644
--- a/packages/engine/Source/Shaders/Model/GeometryStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/GeometryStageFS.glsl
@@ -3,7 +3,7 @@ void geometryStage(out ProcessedAttributes attributes)
attributes.positionMC = v_positionMC;
attributes.positionEC = v_positionEC;
- #ifdef COMPUTE_POSITION_WC_CUSTOM_SHADER
+ #if defined(COMPUTE_POSITION_WC_CUSTOM_SHADER) || defined(COMPUTE_POSITION_WC_STYLE) || defined(COMPUTE_POSITION_WC_ATMOSPHERE)
attributes.positionWC = v_positionWC;
#endif
diff --git a/packages/engine/Source/Shaders/Model/GeometryStageVS.glsl b/packages/engine/Source/Shaders/Model/GeometryStageVS.glsl
index 2cda604bb8d9..58ebdff53291 100644
--- a/packages/engine/Source/Shaders/Model/GeometryStageVS.glsl
+++ b/packages/engine/Source/Shaders/Model/GeometryStageVS.glsl
@@ -1,4 +1,4 @@
-vec4 geometryStage(inout ProcessedAttributes attributes, mat4 modelView, mat3 normal)
+vec4 geometryStage(inout ProcessedAttributes attributes, mat4 modelView, mat3 normal)
{
vec4 computedPosition;
@@ -16,7 +16,7 @@ vec4 geometryStage(inout ProcessedAttributes attributes, mat4 modelView, mat3 no
#endif
// Sometimes the custom shader and/or style needs this
- #if defined(COMPUTE_POSITION_WC_CUSTOM_SHADER) || defined(COMPUTE_POSITION_WC_STYLE)
+ #if defined(COMPUTE_POSITION_WC_CUSTOM_SHADER) || defined(COMPUTE_POSITION_WC_STYLE) || defined(COMPUTE_POSITION_WC_ATMOSPHERE)
// Note that this is a 32-bit position which may result in jitter on small
// scales.
v_positionWC = (czm_model * vec4(positionMC, 1.0)).xyz;
@@ -27,7 +27,7 @@ vec4 geometryStage(inout ProcessedAttributes attributes, mat4 modelView, mat3 no
#endif
#ifdef HAS_TANGENTS
- v_tangentEC = normalize(normal * attributes.tangentMC);
+ v_tangentEC = normalize(normal * attributes.tangentMC);
#endif
#ifdef HAS_BITANGENTS
@@ -37,6 +37,6 @@ vec4 geometryStage(inout ProcessedAttributes attributes, mat4 modelView, mat3 no
// All other varyings need to be dynamically generated in
// GeometryPipelineStage
setDynamicVaryings(attributes);
-
+
return computedPosition;
}
diff --git a/packages/engine/Source/Shaders/Model/ModelVS.glsl b/packages/engine/Source/Shaders/Model/ModelVS.glsl
index e9a4eb5e63ec..b16a634161d0 100644
--- a/packages/engine/Source/Shaders/Model/ModelVS.glsl
+++ b/packages/engine/Source/Shaders/Model/ModelVS.glsl
@@ -7,7 +7,7 @@ czm_modelVertexOutput defaultVertexOutput(vec3 positionMC) {
return vsOutput;
}
-void main()
+void main()
{
// Initialize the attributes struct with all
// attributes except quantized ones.
@@ -66,14 +66,14 @@ void main()
// Update the position for this instance in place
#ifdef HAS_INSTANCING
- // The legacy instance stage is used when rendering i3dm models that
+ // The legacy instance stage is used when rendering i3dm models that
// encode instances transforms in world space, as opposed to glTF models
// that use EXT_mesh_gpu_instancing, where instance transforms are encoded
// in object space.
#ifdef USE_LEGACY_INSTANCING
mat4 instanceModelView;
mat3 instanceModelViewInverseTranspose;
-
+
legacyInstancingStage(attributes, instanceModelView, instanceModelViewInverseTranspose);
modelView = instanceModelView;
@@ -104,7 +104,11 @@ void main()
// Compute the final position in each coordinate system needed.
// This returns the value that will be assigned to gl_Position.
- vec4 positionClip = geometryStage(attributes, modelView, normal);
+ vec4 positionClip = geometryStage(attributes, modelView, normal);
+
+ #ifdef HAS_FOG
+ fogStage(attributes);
+ #endif
#ifdef HAS_SILHOUETTE
silhouetteStage(attributes, positionClip);
From 59ab44d57dadb5c246e9a73a801e0a2e5e735952 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 9 Jan 2024 16:56:29 -0500
Subject: [PATCH 29/55] Apply HSB shift
---
.../Builtin/Functions/applyHSBShift.glsl | 20 +++++++++++++++++++
packages/engine/Source/Shaders/GlobeFS.glsl | 6 ++++--
.../Source/Shaders/Model/FogStageFS.glsl | 2 ++
.../Source/Shaders/SkyAtmosphereFS.glsl | 9 +--------
4 files changed, 27 insertions(+), 10 deletions(-)
create mode 100644 packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl b/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
new file mode 100644
index 000000000000..81575fecab1f
--- /dev/null
+++ b/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
@@ -0,0 +1,20 @@
+/**
+ * Apply a color shift in HSB color space
+ *
+ *
+ * @param {vec3} rgb The color in RGB space.
+ * @param {vec3} hsbShift The amount to shift each component. The xyz components correspond to hue, saturation, and brightness. Shifting the hue by +/- 1.0 corresponds to shifting the hue by a full cycle. Saturation and brightness are clamped between 0 and 1 after the adjustment
+ */
+vec3 czm_applyHSBShift(vec3 rgb, vec3 hsbShift) {
+ // Convert rgb color to hsb
+ vec3 hsb = czm_RGBToHSB(rgb);
+
+ // Perform hsb shift
+ // Hue cycles around so no clamp is needed.
+ hsb.x += hsbShift.x; // hue
+ hsb.y = clamp(hsb.y + hsbShift.y, 0.0, 1.0); // saturation
+ hsb.z = clamp(hsb.z + hsbShift.z, 0.0, 1.0); // brightness
+
+ // Convert shifted hsb back to rgb
+ return czm_HSBToRGB(hsb);
+}
diff --git a/packages/engine/Source/Shaders/GlobeFS.glsl b/packages/engine/Source/Shaders/GlobeFS.glsl
index e344dfec2bfc..a2bf6b48811a 100644
--- a/packages/engine/Source/Shaders/GlobeFS.glsl
+++ b/packages/engine/Source/Shaders/GlobeFS.glsl
@@ -472,8 +472,10 @@ void main()
opacity = v_atmosphereOpacity;
#endif
- rayleighColor = colorCorrect(rayleighColor);
- mieColor = colorCorrect(mieColor);
+ #ifdef COLOR_CORRECT
+ rayleighColor = czm_applyHSBShift(rayleighColor, u_hsbShift);
+ mieColor = czm_applyHSBShift(mieColor, u_hsbShift);
+ #endif
vec4 groundAtmosphereColor = computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
index 870e040d7132..5ac40c52f2af 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/FogStageFS.glsl
@@ -96,6 +96,8 @@ void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
}
//color correct rayleigh and mie colors
+ rayleighColor = czm_applyHSBShift(rayleighColor, czm_atmosphereHsbShift);
+ mieColor = czm_applyHSBShift(mieColor, czm_atmosphereHsbShift);
vec4 groundAtmosphereColor = czm_computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
diff --git a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
index db07b7fbd1ec..639730bba374 100644
--- a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
+++ b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
@@ -43,14 +43,7 @@ void main (void)
#endif
#ifdef COLOR_CORRECT
- // Convert rgb color to hsb
- vec3 hsb = czm_RGBToHSB(color.rgb);
- // Perform hsb shift
- hsb.x += u_hsbShift.x; // hue
- hsb.y = clamp(hsb.y + u_hsbShift.y, 0.0, 1.0); // saturation
- hsb.z = hsb.z > czm_epsilon7 ? hsb.z + u_hsbShift.z : 0.0; // brightness
- // Convert shifted hsb back to rgb
- color.rgb = czm_HSBToRGB(hsb);
+ color.rgb = czm_applyHSBShift(color.rgb, u_hsbShift);
#endif
// For the parts of the sky atmosphere that are not behind a translucent globe,
From 35294416e7ddec8663928051feb69670e4f4d375 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 11:55:02 -0500
Subject: [PATCH 30/55] Fix model matrix spec
---
.../Scene/Model/ModelMatrixUpdateStageSpec.js | 57 +++++++++----------
1 file changed, 26 insertions(+), 31 deletions(-)
diff --git a/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js b/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js
index eddd956faa54..19b3b9396841 100644
--- a/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js
@@ -131,6 +131,22 @@ describe(
leafNode._transformDirty = true;
}
+ function applyTransform(node, transform) {
+ const expectedOriginalTransform = Matrix4.clone(node.originalTransform);
+ expect(node._transformDirty).toEqual(false);
+
+ node.transform = Matrix4.multiplyTransformation(
+ node.transform,
+ transform,
+ new Matrix4()
+ );
+ expect(node._transformDirty).toEqual(true);
+
+ expect(
+ Matrix4.equals(node.originalTransform, expectedOriginalTransform)
+ ).toBe(true);
+ }
+
it("updates leaf nodes using node transform setter", function () {
return loadAndZoomToModelAsync(
{
@@ -138,24 +154,16 @@ describe(
},
scene
).then(function (model) {
+ scene.renderForSpecs();
+
const sceneGraph = model.sceneGraph;
const node = getStaticLeafNode(model);
const primitive = node.runtimePrimitives[0];
- const drawCommand = primitive.drawCommand;
- const expectedOriginalTransform = Matrix4.clone(node.transform);
- expect(node._transformDirty).toEqual(false);
+ let drawCommand = getDrawCommand(node);
- const translation = new Cartesian3(0, 5, 0);
- node.transform = Matrix4.multiplyByTranslation(
- node.transform,
- translation,
- new Matrix4()
- );
- expect(node._transformDirty).toEqual(true);
- expect(
- Matrix4.equals(node.originalTransform, expectedOriginalTransform)
- ).toBe(true);
+ const transform = Matrix4.fromTranslation(new Cartesian3(0, 5, 0));
+ applyTransform(node, transform);
const expectedComputedTransform = Matrix4.multiplyTransformation(
sceneGraph.computedModelMatrix,
@@ -163,9 +171,9 @@ describe(
new Matrix4()
);
- const expectedModelMatrix = Matrix4.multiplyByTranslation(
+ const expectedModelMatrix = Matrix4.multiplyTransformation(
drawCommand.modelMatrix,
- translation,
+ transform,
new Matrix4()
);
@@ -176,6 +184,7 @@ describe(
);
scene.renderForSpecs();
+ drawCommand = getDrawCommand(node);
expect(
Matrix4.equalsEpsilon(
@@ -193,22 +202,6 @@ describe(
});
});
- function applyTransform(node, transform) {
- const expectedOriginalTransform = Matrix4.clone(node.originalTransform);
- expect(node._transformDirty).toEqual(false);
-
- node.transform = Matrix4.multiplyTransformation(
- node.transform,
- transform,
- new Matrix4()
- );
- expect(node._transformDirty).toEqual(true);
-
- expect(
- Matrix4.equals(node.originalTransform, expectedOriginalTransform)
- ).toBe(true);
- }
-
it("updates nodes with children using node transform setter", function () {
return loadAndZoomToModelAsync(
{
@@ -407,6 +400,8 @@ describe(
model.modelMatrix = Matrix4.fromUniformScale(-1);
scene.renderForSpecs();
+ expect(rootPrimitive.drawCommand).toBe(rootDrawCommand);
+
expect(rootDrawCommand.cullFace).toBe(CullFace.FRONT);
expect(childDrawCommand.cullFace).toBe(CullFace.FRONT);
});
From f8d35605e37d6afcdde4eec21706d156f21ec588 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 11:59:22 -0500
Subject: [PATCH 31/55] Async-ify model matrix update stage
---
.../Scene/Model/ModelMatrixUpdateStageSpec.js | 428 +++++++++---------
1 file changed, 211 insertions(+), 217 deletions(-)
diff --git a/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js b/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js
index 19b3b9396841..17ffae777fe4 100644
--- a/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelMatrixUpdateStageSpec.js
@@ -147,264 +147,258 @@ describe(
).toBe(true);
}
- it("updates leaf nodes using node transform setter", function () {
- return loadAndZoomToModelAsync(
+ it("updates leaf nodes using node transform setter", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: simpleSkin,
},
scene
- ).then(function (model) {
- scene.renderForSpecs();
+ );
+ scene.renderForSpecs();
+
+ const sceneGraph = model.sceneGraph;
+ const node = getStaticLeafNode(model);
+ const primitive = node.runtimePrimitives[0];
+
+ let drawCommand = getDrawCommand(node);
+
+ const transform = Matrix4.fromTranslation(new Cartesian3(0, 5, 0));
+ applyTransform(node, transform);
- const sceneGraph = model.sceneGraph;
- const node = getStaticLeafNode(model);
- const primitive = node.runtimePrimitives[0];
+ const expectedComputedTransform = Matrix4.multiplyTransformation(
+ sceneGraph.computedModelMatrix,
+ node.transform,
+ new Matrix4()
+ );
- let drawCommand = getDrawCommand(node);
+ const expectedModelMatrix = Matrix4.multiplyTransformation(
+ drawCommand.modelMatrix,
+ transform,
+ new Matrix4()
+ );
- const transform = Matrix4.fromTranslation(new Cartesian3(0, 5, 0));
- applyTransform(node, transform);
+ const expectedBoundingSphere = BoundingSphere.transform(
+ primitive.boundingSphere,
+ expectedComputedTransform,
+ new BoundingSphere()
+ );
- const expectedComputedTransform = Matrix4.multiplyTransformation(
- sceneGraph.computedModelMatrix,
- node.transform,
- new Matrix4()
- );
+ scene.renderForSpecs();
+ drawCommand = getDrawCommand(node);
- const expectedModelMatrix = Matrix4.multiplyTransformation(
+ expect(
+ Matrix4.equalsEpsilon(
drawCommand.modelMatrix,
- transform,
- new Matrix4()
- );
-
- const expectedBoundingSphere = BoundingSphere.transform(
- primitive.boundingSphere,
- expectedComputedTransform,
- new BoundingSphere()
- );
-
- scene.renderForSpecs();
- drawCommand = getDrawCommand(node);
-
- expect(
- Matrix4.equalsEpsilon(
- drawCommand.modelMatrix,
- expectedModelMatrix,
- CesiumMath.EPSILON15
- )
- ).toBe(true);
- expect(
- BoundingSphere.equals(
- drawCommand.boundingVolume,
- expectedBoundingSphere
- )
- ).toBe(true);
- });
+ expectedModelMatrix,
+ CesiumMath.EPSILON15
+ )
+ ).toBe(true);
+ expect(
+ BoundingSphere.equals(
+ drawCommand.boundingVolume,
+ expectedBoundingSphere
+ )
+ ).toBe(true);
});
- it("updates nodes with children using node transform setter", function () {
- return loadAndZoomToModelAsync(
+ it("updates nodes with children using node transform setter", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: simpleSkin,
},
scene
- ).then(function (model) {
- modifyModel(model);
- scene.renderForSpecs();
-
- const rootNode = getParentRootNode(model);
- const staticLeafNode = getStaticLeafNode(model);
- const transformedLeafNode = getChildLeafNode(model);
-
- let rootDrawCommand = getDrawCommand(rootNode);
- let staticDrawCommand = getDrawCommand(staticLeafNode);
- let transformedDrawCommand = getDrawCommand(transformedLeafNode);
-
- const childTransformation = Matrix4.fromTranslation(
- new Cartesian3(0, 5, 0)
- );
- applyTransform(transformedLeafNode, childTransformation);
-
- const rootTransformation = Matrix4.fromTranslation(
- new Cartesian3(12, 5, 0)
- );
- applyTransform(rootNode, rootTransformation);
-
- const expectedRootModelMatrix = Matrix4.multiplyTransformation(
- rootTransformation,
- rootDrawCommand.modelMatrix,
- new Matrix4()
- );
- const expectedStaticLeafModelMatrix = Matrix4.clone(
- staticDrawCommand.modelMatrix,
- new Matrix4()
- );
-
- const finalTransform = new Matrix4();
- Matrix4.multiply(
- rootTransformation,
- childTransformation,
- finalTransform
- );
- const expectedTransformedLeafModelMatrix = Matrix4.multiplyTransformation(
- finalTransform,
- transformedDrawCommand.modelMatrix,
- new Matrix4()
- );
-
- scene.renderForSpecs();
- rootDrawCommand = getDrawCommand(rootNode);
- staticDrawCommand = getDrawCommand(staticLeafNode);
- transformedDrawCommand = getDrawCommand(transformedLeafNode);
-
- expect(rootDrawCommand.modelMatrix).toEqual(expectedRootModelMatrix);
- expect(staticDrawCommand.modelMatrix).toEqual(
- expectedStaticLeafModelMatrix
- );
- expect(transformedDrawCommand.modelMatrix).toEqual(
- expectedTransformedLeafModelMatrix
- );
- });
+ );
+
+ modifyModel(model);
+ scene.renderForSpecs();
+
+ const rootNode = getParentRootNode(model);
+ const staticLeafNode = getStaticLeafNode(model);
+ const transformedLeafNode = getChildLeafNode(model);
+
+ let rootDrawCommand = getDrawCommand(rootNode);
+ let staticDrawCommand = getDrawCommand(staticLeafNode);
+ let transformedDrawCommand = getDrawCommand(transformedLeafNode);
+
+ const childTransformation = Matrix4.fromTranslation(
+ new Cartesian3(0, 5, 0)
+ );
+ applyTransform(transformedLeafNode, childTransformation);
+
+ const rootTransformation = Matrix4.fromTranslation(
+ new Cartesian3(12, 5, 0)
+ );
+ applyTransform(rootNode, rootTransformation);
+
+ const expectedRootModelMatrix = Matrix4.multiplyTransformation(
+ rootTransformation,
+ rootDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+ const expectedStaticLeafModelMatrix = Matrix4.clone(
+ staticDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+
+ const finalTransform = new Matrix4();
+ Matrix4.multiply(rootTransformation, childTransformation, finalTransform);
+ const expectedTransformedLeafModelMatrix = Matrix4.multiplyTransformation(
+ finalTransform,
+ transformedDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+
+ scene.renderForSpecs();
+ rootDrawCommand = getDrawCommand(rootNode);
+ staticDrawCommand = getDrawCommand(staticLeafNode);
+ transformedDrawCommand = getDrawCommand(transformedLeafNode);
+
+ expect(rootDrawCommand.modelMatrix).toEqual(expectedRootModelMatrix);
+ expect(staticDrawCommand.modelMatrix).toEqual(
+ expectedStaticLeafModelMatrix
+ );
+ expect(transformedDrawCommand.modelMatrix).toEqual(
+ expectedTransformedLeafModelMatrix
+ );
});
- it("updates with new model matrix", function () {
- return loadAndZoomToModelAsync(
+ it("updates with new model matrix", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: simpleSkin,
},
scene
- ).then(function (model) {
- modifyModel(model);
- scene.renderForSpecs();
-
- const rootNode = getParentRootNode(model);
- const staticLeafNode = getStaticLeafNode(model);
- const transformedLeafNode = getChildLeafNode(model);
-
- let rootDrawCommand = getDrawCommand(rootNode);
- let staticDrawCommand = getDrawCommand(staticLeafNode);
- let transformedDrawCommand = getDrawCommand(transformedLeafNode);
-
- const expectedRootModelMatrix = Matrix4.multiplyTransformation(
- modelMatrix,
- rootDrawCommand.modelMatrix,
- new Matrix4()
- );
- const expectedStaticLeafModelMatrix = Matrix4.multiplyTransformation(
- modelMatrix,
- staticDrawCommand.modelMatrix,
- new Matrix4()
- );
- const expectedTransformedLeafModelMatrix = Matrix4.multiplyTransformation(
- modelMatrix,
- transformedDrawCommand.modelMatrix,
- new Matrix4()
- );
-
- model.modelMatrix = modelMatrix;
- scene.renderForSpecs();
-
- rootDrawCommand = getDrawCommand(rootNode);
- staticDrawCommand = getDrawCommand(staticLeafNode);
- transformedDrawCommand = getDrawCommand(transformedLeafNode);
-
- expect(rootDrawCommand.modelMatrix).toEqual(expectedRootModelMatrix);
- expect(staticDrawCommand.modelMatrix).toEqual(
- expectedStaticLeafModelMatrix
- );
- expect(transformedDrawCommand.modelMatrix).toEqual(
- expectedTransformedLeafModelMatrix
- );
- });
+ );
+
+ modifyModel(model);
+ scene.renderForSpecs();
+
+ const rootNode = getParentRootNode(model);
+ const staticLeafNode = getStaticLeafNode(model);
+ const transformedLeafNode = getChildLeafNode(model);
+
+ let rootDrawCommand = getDrawCommand(rootNode);
+ let staticDrawCommand = getDrawCommand(staticLeafNode);
+ let transformedDrawCommand = getDrawCommand(transformedLeafNode);
+
+ const expectedRootModelMatrix = Matrix4.multiplyTransformation(
+ modelMatrix,
+ rootDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+ const expectedStaticLeafModelMatrix = Matrix4.multiplyTransformation(
+ modelMatrix,
+ staticDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+ const expectedTransformedLeafModelMatrix = Matrix4.multiplyTransformation(
+ modelMatrix,
+ transformedDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+
+ model.modelMatrix = modelMatrix;
+ scene.renderForSpecs();
+
+ rootDrawCommand = getDrawCommand(rootNode);
+ staticDrawCommand = getDrawCommand(staticLeafNode);
+ transformedDrawCommand = getDrawCommand(transformedLeafNode);
+
+ expect(rootDrawCommand.modelMatrix).toEqual(expectedRootModelMatrix);
+ expect(staticDrawCommand.modelMatrix).toEqual(
+ expectedStaticLeafModelMatrix
+ );
+ expect(transformedDrawCommand.modelMatrix).toEqual(
+ expectedTransformedLeafModelMatrix
+ );
});
- it("updates with new model matrix and model scale", function () {
- return loadAndZoomToModelAsync(
+ it("updates with new model matrix and model scale", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: simpleSkin,
},
scene
- ).then(function (model) {
- modifyModel(model);
- scene.renderForSpecs();
-
- const modelScale = 5.0;
- const scaledModelMatrix = Matrix4.multiplyByUniformScale(
- modelMatrix,
- modelScale,
- new Matrix4()
- );
-
- const rootNode = getParentRootNode(model);
- const staticLeafNode = getStaticLeafNode(model);
- const transformedLeafNode = getChildLeafNode(model);
-
- let rootDrawCommand = getDrawCommand(rootNode);
- let staticDrawCommand = getDrawCommand(staticLeafNode);
- let transformedDrawCommand = getDrawCommand(transformedLeafNode);
-
- const expectedRootModelMatrix = Matrix4.multiplyTransformation(
- scaledModelMatrix,
- rootDrawCommand.modelMatrix,
- new Matrix4()
- );
- const expectedStaticLeafModelMatrix = Matrix4.multiplyTransformation(
- scaledModelMatrix,
- staticDrawCommand.modelMatrix,
- new Matrix4()
- );
- const expectedTransformedLeafModelMatrix = Matrix4.multiplyTransformation(
- scaledModelMatrix,
- transformedDrawCommand.modelMatrix,
- new Matrix4()
- );
-
- model.modelMatrix = modelMatrix;
- model.scale = modelScale;
- scene.renderForSpecs();
- rootDrawCommand = getDrawCommand(rootNode);
- staticDrawCommand = getDrawCommand(staticLeafNode);
- transformedDrawCommand = getDrawCommand(transformedLeafNode);
-
- expect(rootDrawCommand.modelMatrix).toEqual(expectedRootModelMatrix);
- expect(staticDrawCommand.modelMatrix).toEqual(
- expectedStaticLeafModelMatrix
- );
- expect(transformedDrawCommand.modelMatrix).toEqual(
- expectedTransformedLeafModelMatrix
- );
- });
+ );
+
+ modifyModel(model);
+ scene.renderForSpecs();
+
+ const modelScale = 5.0;
+ const scaledModelMatrix = Matrix4.multiplyByUniformScale(
+ modelMatrix,
+ modelScale,
+ new Matrix4()
+ );
+
+ const rootNode = getParentRootNode(model);
+ const staticLeafNode = getStaticLeafNode(model);
+ const transformedLeafNode = getChildLeafNode(model);
+
+ let rootDrawCommand = getDrawCommand(rootNode);
+ let staticDrawCommand = getDrawCommand(staticLeafNode);
+ let transformedDrawCommand = getDrawCommand(transformedLeafNode);
+
+ const expectedRootModelMatrix = Matrix4.multiplyTransformation(
+ scaledModelMatrix,
+ rootDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+ const expectedStaticLeafModelMatrix = Matrix4.multiplyTransformation(
+ scaledModelMatrix,
+ staticDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+ const expectedTransformedLeafModelMatrix = Matrix4.multiplyTransformation(
+ scaledModelMatrix,
+ transformedDrawCommand.modelMatrix,
+ new Matrix4()
+ );
+
+ model.modelMatrix = modelMatrix;
+ model.scale = modelScale;
+ scene.renderForSpecs();
+ rootDrawCommand = getDrawCommand(rootNode);
+ staticDrawCommand = getDrawCommand(staticLeafNode);
+ transformedDrawCommand = getDrawCommand(transformedLeafNode);
+
+ expect(rootDrawCommand.modelMatrix).toEqual(expectedRootModelMatrix);
+ expect(staticDrawCommand.modelMatrix).toEqual(
+ expectedStaticLeafModelMatrix
+ );
+ expect(transformedDrawCommand.modelMatrix).toEqual(
+ expectedTransformedLeafModelMatrix
+ );
});
- it("updates render state cull face when scale is negative", function () {
- return loadAndZoomToModelAsync(
+ it("updates render state cull face when scale is negative", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: simpleSkin,
},
scene
- ).then(function (model) {
- modifyModel(model);
+ );
+ modifyModel(model);
- const rootNode = getParentRootNode(model);
- const childNode = getChildLeafNode(model);
+ const rootNode = getParentRootNode(model);
+ const childNode = getChildLeafNode(model);
- const rootPrimitive = rootNode.runtimePrimitives[0];
- const childPrimitive = childNode.runtimePrimitives[0];
+ const rootPrimitive = rootNode.runtimePrimitives[0];
+ const childPrimitive = childNode.runtimePrimitives[0];
- const rootDrawCommand = rootPrimitive.drawCommand;
- const childDrawCommand = childPrimitive.drawCommand;
+ const rootDrawCommand = rootPrimitive.drawCommand;
+ const childDrawCommand = childPrimitive.drawCommand;
- expect(rootDrawCommand.cullFace).toBe(CullFace.BACK);
- expect(childDrawCommand.cullFace).toBe(CullFace.BACK);
+ expect(rootDrawCommand.cullFace).toBe(CullFace.BACK);
+ expect(childDrawCommand.cullFace).toBe(CullFace.BACK);
- model.modelMatrix = Matrix4.fromUniformScale(-1);
- scene.renderForSpecs();
+ model.modelMatrix = Matrix4.fromUniformScale(-1);
+ scene.renderForSpecs();
- expect(rootPrimitive.drawCommand).toBe(rootDrawCommand);
+ expect(rootPrimitive.drawCommand).toBe(rootDrawCommand);
- expect(rootDrawCommand.cullFace).toBe(CullFace.FRONT);
- expect(childDrawCommand.cullFace).toBe(CullFace.FRONT);
- });
+ expect(rootDrawCommand.cullFace).toBe(CullFace.FRONT);
+ expect(childDrawCommand.cullFace).toBe(CullFace.FRONT);
});
},
"WebGL"
From af067852408eb617d633729998a0e92b662cd1de Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 12:41:08 -0500
Subject: [PATCH 32/55] Move pipeline stage specs to ModelSceneGraph
---
.../Scene/Model/ModelRuntimePrimitiveSpec.js | 77 -------------------
.../Specs/Scene/Model/ModelSceneGraphSpec.js | 51 ++++++++++++
2 files changed, 51 insertions(+), 77 deletions(-)
diff --git a/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js b/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js
index 8dd9ddb669b3..6e73cc3fbc8b 100644
--- a/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelRuntimePrimitiveSpec.js
@@ -7,7 +7,6 @@ import {
CustomShaderMode,
CustomShaderPipelineStage,
FeatureIdPipelineStage,
- FogPipelineStage,
CPUStylingPipelineStage,
DequantizationPipelineStage,
GeometryPipelineStage,
@@ -993,80 +992,4 @@ describe("Scene/Model/ModelRuntimePrimitive", function () {
primitive.configurePipeline(frameState);
verifyExpectedStages(primitive.pipelineStages, expectedStages);
});
-
- it("does not add fog stage when fog is not enabled", function () {
- const primitive = new ModelRuntimePrimitive({
- primitive: mockPrimitive,
- node: mockNode,
- model: mockModel,
- });
- const frameState = createFrameState(mockWebgl2Context);
- frameState.fog.enabled = false;
- frameState.fog.renderable = false;
-
- const expectedStages = [
- GeometryPipelineStage,
- MaterialPipelineStage,
- FeatureIdPipelineStage,
- MetadataPipelineStage,
- LightingPipelineStage,
- PickingPipelineStage,
- AlphaPipelineStage,
- PrimitiveStatisticsPipelineStage,
- ];
-
- primitive.configurePipeline(frameState);
- verifyExpectedStages(primitive.pipelineStages, expectedStages);
- });
-
- it("does not add fog stage when fog is not renderable", function () {
- const primitive = new ModelRuntimePrimitive({
- primitive: mockPrimitive,
- node: mockNode,
- model: mockModel,
- });
- const frameState = createFrameState(mockWebgl2Context);
- frameState.fog.enabled = true;
- frameState.fog.renderable = false;
-
- const expectedStages = [
- GeometryPipelineStage,
- MaterialPipelineStage,
- FeatureIdPipelineStage,
- MetadataPipelineStage,
- LightingPipelineStage,
- PickingPipelineStage,
- AlphaPipelineStage,
- PrimitiveStatisticsPipelineStage,
- ];
-
- primitive.configurePipeline(frameState);
- verifyExpectedStages(primitive.pipelineStages, expectedStages);
- });
-
- it("configures pipeline stages when fog is enabled and renderable", function () {
- const primitive = new ModelRuntimePrimitive({
- primitive: mockPrimitive,
- node: mockNode,
- model: mockModel,
- });
- const frameState = createFrameState(mockWebgl2Context);
- frameState.fog.enabled = true;
- frameState.fog.renderable = true;
-
- const expectedStages = [
- GeometryPipelineStage,
- MaterialPipelineStage,
- FeatureIdPipelineStage,
- MetadataPipelineStage,
- LightingPipelineStage,
- PickingPipelineStage,
- AlphaPipelineStage,
- FogPipelineStage,
- PrimitiveStatisticsPipelineStage,
- ];
-
- primitive.configurePipeline(frameState);
- verifyExpectedStages(primitive.pipelineStages, expectedStages);
- });
});
diff --git a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
index b5196ff355d3..c28d5b0aece0 100644
--- a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
@@ -4,6 +4,8 @@ import {
Color,
CustomShader,
CustomShaderPipelineStage,
+ Fog,
+ FogPipelineStage,
Math as CesiumMath,
Matrix4,
ModelColorPipelineStage,
@@ -41,6 +43,7 @@ describe(
afterEach(function () {
scene.primitives.removeAll();
+ scene.fog = new Fog();
ResourceCache.clearForSpecs();
});
@@ -353,6 +356,54 @@ describe(
});
});
+ it("does not add fog stage when fog is not enabled", function () {
+ spyOn(FogPipelineStage, "process");
+ scene.fog.enabled = false;
+ scene.fog.renderable = false;
+ return loadAndZoomToModelAsync(
+ {
+ gltf: buildingsMetadata,
+ },
+ scene
+ ).then(function (model) {
+ model.customShader = new CustomShader();
+ model.update(scene.frameState);
+ expect(FogPipelineStage.process).not.toHaveBeenCalled();
+ });
+ });
+
+ it("does not add fog stage when fog is not renderable", function () {
+ spyOn(FogPipelineStage, "process");
+ scene.fog.enabled = true;
+ scene.fog.renderable = false;
+ return loadAndZoomToModelAsync(
+ {
+ gltf: buildingsMetadata,
+ },
+ scene
+ ).then(function (model) {
+ model.customShader = new CustomShader();
+ model.update(scene.frameState);
+ expect(FogPipelineStage.process).not.toHaveBeenCalled();
+ });
+ });
+
+ it("adds FogPipelineStage when fog is enabled and renderable", function () {
+ spyOn(FogPipelineStage, "process");
+ scene.fog.enabled = true;
+ scene.fog.renderable = true;
+ return loadAndZoomToModelAsync(
+ {
+ gltf: buildingsMetadata,
+ },
+ scene
+ ).then(function (model) {
+ model.customShader = new CustomShader();
+ model.update(scene.frameState);
+ expect(FogPipelineStage.process).toHaveBeenCalled();
+ });
+ });
+
it("pushDrawCommands ignores hidden nodes", function () {
return loadAndZoomToModelAsync(
{
From 4e6b942c5bff73c704a0a8e6bc17af91f396d4da Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 12:44:48 -0500
Subject: [PATCH 33/55] fix auto imports
---
packages/engine/Source/Scene/Atmosphere.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index d5b2c4321547..ca2ce6d727b2 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -1,5 +1,5 @@
-import Cartesian3 from "../Core/Cartesian3";
-import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType";
+import Cartesian3 from "../Core/Cartesian3.js";
+import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType.js";
function Atmosphere() {
/**
From cdc24fc57dbf0488adeb985d281f178886a2460c Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 13:25:25 -0500
Subject: [PATCH 34/55] Add test for czm_fogMinimumBrightness
---
.../engine/Source/Renderer/UniformState.js | 1 +
.../Specs/Renderer/AutomaticUniformSpec.js | 32 ++++++++++++++++++-
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index d4745818c480..7c0c56924c62 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -1514,6 +1514,7 @@ UniformState.prototype.update = function (frameState) {
}
this._fogDensity = frameState.fog.density;
+ this._fogMinimumBrightness = frameState.fog.minimumBrightness;
const atmosphere = frameState.atmosphere;
diff --git a/packages/engine/Specs/Renderer/AutomaticUniformSpec.js b/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
index 349687d5a1ba..c93eac8870d3 100644
--- a/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
+++ b/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
@@ -1793,7 +1793,7 @@ describe(
undefined,
undefined,
undefined,
- // Explicit position and direction as the default position of (0, 0, 0)
+ // Provide position and direction because the default position of (0, 0, 0)
// will lead to a divide by zero when updating fog below.
new Cartesian3(1.0, 0.0, 0.0),
new Cartesian3(0.0, 1.0, 0.0)
@@ -1816,6 +1816,36 @@ describe(
}).contextToRender();
});
+ it("has czm_fogMinimumBrightness", function () {
+ const frameState = createFrameState(
+ context,
+ createMockCamera(
+ undefined,
+ undefined,
+ undefined,
+ // Provide position and direction because the default position of (0, 0, 0)
+ // will lead to a divide by zero when updating fog below
+ new Cartesian3(1.0, 0.0, 0.0),
+ new Cartesian3(0.0, 1.0, 0.0)
+ )
+ );
+ const fog = new Fog();
+ fog.minimumBrightness = 0.25;
+ fog.update(frameState);
+
+ const us = context.uniformState;
+ us.update(frameState);
+
+ const fs =
+ "void main() {" +
+ " out_FragColor = vec4(czm_fogMinimumBrightness == 0.25);" +
+ "}";
+ expect({
+ context,
+ fragmentShader: fs,
+ }).contextToRender();
+ });
+
it("has czm_atmosphereHsbShift", function () {
const frameState = createFrameState(context, createMockCamera());
const atmosphere = new Atmosphere();
From 19cd032854c14cfd34ee05ca00483fcafe046a09 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 14:12:18 -0500
Subject: [PATCH 35/55] Add specs for FogPipelineStage
---
.../Source/Scene/Model/FogPipelineStage.js | 4 +-
.../Specs/Scene/Model/FogPipelineStageSpec.js | 113 ++++++++++++++++++
.../Specs/Scene/Model/ModelSceneGraphSpec.js | 13 +-
3 files changed, 121 insertions(+), 9 deletions(-)
create mode 100644 packages/engine/Specs/Scene/Model/FogPipelineStageSpec.js
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/FogPipelineStage.js
index 971f6cb03295..5a323e59b114 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/FogPipelineStage.js
@@ -18,6 +18,7 @@ const FogPipelineStage = {
FogPipelineStage.process = function (renderResources, model, frameState) {
const shaderBuilder = renderResources.shaderBuilder;
+ shaderBuilder.addDefine("HAS_FOG", undefined, ShaderDestination.BOTH);
shaderBuilder.addDefine(
"COMPUTE_POSITION_WC_ATMOSPHERE",
undefined,
@@ -28,11 +29,10 @@ FogPipelineStage.process = function (renderResources, model, frameState) {
shaderBuilder.addVarying("vec3", "v_atmosphereMieColor");
shaderBuilder.addVarying("float", "v_atmosphereOpacity");
- shaderBuilder.addDefine("HAS_FOG", undefined, ShaderDestination.BOTH);
shaderBuilder.addVertexLines([FogStageVS]);
shaderBuilder.addFragmentLines([FogStageFS]);
- // Add a uniform so fog is only calculated when the effect would
+ // Add a uniform so fog is only calculated when the efcfect would
// be non-negligible For example when the camera is in space, fog density decreases
// to 0 so fog shouldn't be rendered. Since this state may change rapidly if
// the camera is moving, this is implemented as a uniform, not a define.
diff --git a/packages/engine/Specs/Scene/Model/FogPipelineStageSpec.js b/packages/engine/Specs/Scene/Model/FogPipelineStageSpec.js
new file mode 100644
index 000000000000..281ed762ab02
--- /dev/null
+++ b/packages/engine/Specs/Scene/Model/FogPipelineStageSpec.js
@@ -0,0 +1,113 @@
+import {
+ _shadersFogStageFS,
+ _shadersFogStageVS,
+ Cartesian3,
+ FogPipelineStage,
+ ShaderBuilder,
+} from "../../../index.js";
+import ShaderBuilderTester from "../../../../../Specs/ShaderBuilderTester.js";
+
+describe("Scene/Model/FogPipelineStage", function () {
+ const mockModel = {
+ boundingSphere: {
+ center: Cartesian3.fromDegrees(0, 0, 0),
+ },
+ };
+
+ function mockFrameState() {
+ return {
+ camera: {
+ // position the camera a little bit east of the model
+ // and slightly above
+ position: Cartesian3.fromDegrees(0.01, 0, 1),
+ },
+ fog: {
+ density: 2e-4,
+ },
+ };
+ }
+
+ function mockRenderResources() {
+ return {
+ shaderBuilder: new ShaderBuilder(),
+ uniformMap: {},
+ };
+ }
+
+ it("Configures shader", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ FogPipelineStage.process(renderResources, mockModel, frameState);
+
+ const shaderBuilder = renderResources.shaderBuilder;
+
+ ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, [
+ "HAS_FOG",
+ "COMPUTE_POSITION_WC_ATMOSPHERE",
+ ]);
+ ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, [
+ "HAS_FOG",
+ "COMPUTE_POSITION_WC_ATMOSPHERE",
+ ]);
+
+ ShaderBuilderTester.expectHasVaryings(shaderBuilder, [
+ "vec3 v_atmosphereRayleighColor;",
+ "vec3 v_atmosphereMieColor;",
+ "float v_atmosphereOpacity;",
+ ]);
+
+ ShaderBuilderTester.expectVertexLinesEqual(shaderBuilder, [
+ _shadersFogStageVS,
+ ]);
+ ShaderBuilderTester.expectFragmentLinesEqual(shaderBuilder, [
+ _shadersFogStageFS,
+ ]);
+
+ ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, []);
+ ShaderBuilderTester.expectHasFragmentUniforms(shaderBuilder, [
+ "uniform bool u_isInFog;",
+ ]);
+ });
+
+ it("u_isInFog() is false if the camera is at the model center", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ frameState.camera.position = Cartesian3.clone(
+ mockModel.boundingSphere.center,
+ frameState.camera.position
+ );
+
+ FogPipelineStage.process(renderResources, mockModel, frameState);
+
+ const uniformMap = renderResources.uniformMap;
+ expect(uniformMap.u_isInFog()).toBe(false);
+ });
+
+ it("u_isInFog() is false if the camera is in space", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ // For this case, the fact that Fog decreases the density to 0 when
+ // the camera is far above the model is what causes u_isInFog to
+ // be false.
+ frameState.camera.position = Cartesian3.fromDegrees(0.001, 0, 100000);
+ frameState.fog.density = 0;
+
+ FogPipelineStage.process(renderResources, mockModel, frameState);
+
+ const uniformMap = renderResources.uniformMap;
+ expect(uniformMap.u_isInFog()).toBe(false);
+ });
+
+ it("u_isInFog() is true when the tile is in fog", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ FogPipelineStage.process(renderResources, mockModel, frameState);
+
+ const uniformMap = renderResources.uniformMap;
+ expect(uniformMap.u_isInFog()).toBe(true);
+ });
+});
diff --git a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
index c28d5b0aece0..2b8eea6ae815 100644
--- a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
@@ -388,20 +388,19 @@ describe(
});
});
- it("adds FogPipelineStage when fog is enabled and renderable", function () {
+ it("adds fog stage when fog is enabled and renderable", async function () {
spyOn(FogPipelineStage, "process");
scene.fog.enabled = true;
scene.fog.renderable = true;
- return loadAndZoomToModelAsync(
+ const model = await loadAndZoomToModelAsync(
{
gltf: buildingsMetadata,
},
scene
- ).then(function (model) {
- model.customShader = new CustomShader();
- model.update(scene.frameState);
- expect(FogPipelineStage.process).toHaveBeenCalled();
- });
+ );
+ model.customShader = new CustomShader();
+ model.update(scene.frameState);
+ expect(FogPipelineStage.process).toHaveBeenCalled();
});
it("pushDrawCommands ignores hidden nodes", function () {
From 7e4e6e1fc7a5ba96de68df8bd1c21ce4e12a8d72 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 14:23:45 -0500
Subject: [PATCH 36/55] async-ify model scene graph tests
---
.../Specs/Scene/Model/ModelSceneGraphSpec.js | 500 +++++++++---------
1 file changed, 242 insertions(+), 258 deletions(-)
diff --git a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
index 2b8eea6ae815..05f86fcf865d 100644
--- a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
@@ -47,79 +47,77 @@ describe(
ResourceCache.clearForSpecs();
});
- it("creates runtime nodes and runtime primitives from a model", function () {
- return loadAndZoomToModelAsync({ gltf: vertexColorGltfUrl }, scene).then(
- function (model) {
- const sceneGraph = model._sceneGraph;
- const components = sceneGraph._components;
+ it("creates runtime nodes and runtime primitives from a model", async function () {
+ const model = await loadAndZoomToModelAsync(
+ { gltf: vertexColorGltfUrl },
+ scene
+ );
+ const sceneGraph = model._sceneGraph;
+ const components = sceneGraph._components;
- expect(sceneGraph).toBeDefined();
+ expect(sceneGraph).toBeDefined();
- const runtimeNodes = sceneGraph._runtimeNodes;
- expect(runtimeNodes.length).toEqual(components.nodes.length);
+ const runtimeNodes = sceneGraph._runtimeNodes;
+ expect(runtimeNodes.length).toEqual(components.nodes.length);
- expect(runtimeNodes[0].runtimePrimitives.length).toEqual(1);
- expect(runtimeNodes[1].runtimePrimitives.length).toEqual(1);
- }
- );
+ expect(runtimeNodes[0].runtimePrimitives.length).toEqual(1);
+ expect(runtimeNodes[1].runtimePrimitives.length).toEqual(1);
});
- it("builds draw commands for all opaque styled features", function () {
+ it("builds draw commands for all opaque styled features", async function () {
const style = new Cesium3DTileStyle({
color: {
conditions: [["${height} > 1", "color('red')"]],
},
});
- return loadAndZoomToModelAsync(
+ const model = await loadAndZoomToModelAsync(
{
gltf: buildingsMetadata,
},
scene
- ).then(function (model) {
- model.style = style;
+ );
+ model.style = style;
- const frameState = scene.frameState;
- const commandList = frameState.commandList;
- commandList.length = 0;
+ const frameState = scene.frameState;
+ const commandList = frameState.commandList;
+ commandList.length = 0;
- // Reset the draw commands so we can inspect the draw command generation.
- model._drawCommandsBuilt = false;
- scene.renderForSpecs();
+ // Reset the draw commands so we can inspect the draw command generation.
+ model._drawCommandsBuilt = false;
+ scene.renderForSpecs();
- expect(commandList.length).toEqual(1);
- expect(commandList[0].pass).toEqual(Pass.OPAQUE);
- });
+ expect(commandList.length).toEqual(1);
+ expect(commandList[0].pass).toEqual(Pass.OPAQUE);
});
- it("builds draw commands for all translucent styled features", function () {
+ it("builds draw commands for all translucent styled features", async function () {
const style = new Cesium3DTileStyle({
color: {
conditions: [["${height} > 1", "color('red', 0.1)"]],
},
});
- return loadAndZoomToModelAsync(
+ const model = await loadAndZoomToModelAsync(
{
gltf: buildingsMetadata,
},
scene
- ).then(function (model) {
- model.style = style;
+ );
+ model.style = style;
- const frameState = scene.frameState;
- const commandList = frameState.commandList;
- commandList.length = 0;
+ const frameState = scene.frameState;
+ const commandList = frameState.commandList;
+ commandList.length = 0;
- // Reset the draw commands so we can inspect the draw command generation.
- model._drawCommandsBuilt = false;
- scene.renderForSpecs();
+ // Reset the draw commands so we can inspect the draw command generation.
+ model._drawCommandsBuilt = false;
+ scene.renderForSpecs();
- expect(commandList.length).toEqual(1);
- expect(commandList[0].pass).toEqual(Pass.TRANSLUCENT);
- });
+ expect(commandList.length).toEqual(1);
+ expect(commandList[0].pass).toEqual(Pass.TRANSLUCENT);
});
- it("builds draw commands for both opaque and translucent styled features", function () {
+ it("builds draw commands for both opaque and translucent styled features", async function () {
const style = new Cesium3DTileStyle({
color: {
conditions: [
@@ -129,263 +127,250 @@ describe(
},
});
- return loadAndZoomToModelAsync(
+ const model = await loadAndZoomToModelAsync(
{
gltf: buildingsMetadata,
},
scene
- ).then(function (model) {
- model.style = style;
+ );
+ model.style = style;
- const frameState = scene.frameState;
- const commandList = frameState.commandList;
- commandList.length = 0;
+ const frameState = scene.frameState;
+ const commandList = frameState.commandList;
+ commandList.length = 0;
- // Reset the draw commands so we can inspect the draw command generation.
- model._drawCommandsBuilt = false;
- scene.renderForSpecs();
+ // Reset the draw commands so we can inspect the draw command generation.
+ model._drawCommandsBuilt = false;
+ scene.renderForSpecs();
- expect(commandList.length).toEqual(2);
- expect(commandList[0].pass).toEqual(Pass.TRANSLUCENT);
- expect(commandList[1].pass).toEqual(Pass.OPAQUE);
- });
+ expect(commandList.length).toEqual(2);
+ expect(commandList[0].pass).toEqual(Pass.TRANSLUCENT);
+ expect(commandList[1].pass).toEqual(Pass.OPAQUE);
});
- it("builds draw commands for each primitive", function () {
+ it("builds draw commands for each primitive", async function () {
spyOn(ModelSceneGraph.prototype, "buildDrawCommands").and.callThrough();
spyOn(ModelSceneGraph.prototype, "pushDrawCommands").and.callThrough();
- return loadAndZoomToModelAsync({ gltf: parentGltfUrl }, scene).then(
- function (model) {
- const sceneGraph = model._sceneGraph;
- const runtimeNodes = sceneGraph._runtimeNodes;
-
- let primitivesCount = 0;
- for (let i = 0; i < runtimeNodes.length; i++) {
- primitivesCount += runtimeNodes[i].runtimePrimitives.length;
- }
-
- const frameState = scene.frameState;
- frameState.commandList.length = 0;
- scene.renderForSpecs();
- expect(
- ModelSceneGraph.prototype.buildDrawCommands
- ).toHaveBeenCalled();
- expect(ModelSceneGraph.prototype.pushDrawCommands).toHaveBeenCalled();
- expect(frameState.commandList.length).toEqual(primitivesCount);
-
- // Reset the draw command list to see if they're re-built.
- model._drawCommandsBuilt = false;
- frameState.commandList.length = 0;
- scene.renderForSpecs();
- expect(
- ModelSceneGraph.prototype.buildDrawCommands
- ).toHaveBeenCalled();
- expect(ModelSceneGraph.prototype.pushDrawCommands).toHaveBeenCalled();
- expect(frameState.commandList.length).toEqual(primitivesCount);
- }
+ const model = await loadAndZoomToModelAsync(
+ { gltf: parentGltfUrl },
+ scene
);
+
+ const sceneGraph = model._sceneGraph;
+ const runtimeNodes = sceneGraph._runtimeNodes;
+
+ let primitivesCount = 0;
+ for (let i = 0; i < runtimeNodes.length; i++) {
+ primitivesCount += runtimeNodes[i].runtimePrimitives.length;
+ }
+
+ const frameState = scene.frameState;
+ frameState.commandList.length = 0;
+ scene.renderForSpecs();
+ expect(ModelSceneGraph.prototype.buildDrawCommands).toHaveBeenCalled();
+ expect(ModelSceneGraph.prototype.pushDrawCommands).toHaveBeenCalled();
+ expect(frameState.commandList.length).toEqual(primitivesCount);
+
+ // Reset the draw command list to see if they're re-built.
+ model._drawCommandsBuilt = false;
+ frameState.commandList.length = 0;
+ scene.renderForSpecs();
+ expect(ModelSceneGraph.prototype.buildDrawCommands).toHaveBeenCalled();
+ expect(ModelSceneGraph.prototype.pushDrawCommands).toHaveBeenCalled();
+ expect(frameState.commandList.length).toEqual(primitivesCount);
});
- it("stores runtime nodes correctly", function () {
- return loadAndZoomToModelAsync({ gltf: parentGltfUrl }, scene).then(
- function (model) {
- const sceneGraph = model._sceneGraph;
- const components = sceneGraph._components;
- const runtimeNodes = sceneGraph._runtimeNodes;
+ it("stores runtime nodes correctly", async function () {
+ const model = await loadAndZoomToModelAsync(
+ { gltf: parentGltfUrl },
+ scene
+ );
- expect(runtimeNodes[0].node).toEqual(components.nodes[0]);
- expect(runtimeNodes[1].node).toEqual(components.nodes[1]);
+ const sceneGraph = model._sceneGraph;
+ const components = sceneGraph._components;
+ const runtimeNodes = sceneGraph._runtimeNodes;
- const rootNodes = sceneGraph._rootNodes;
- expect(rootNodes[0]).toEqual(0);
- }
- );
+ expect(runtimeNodes[0].node).toEqual(components.nodes[0]);
+ expect(runtimeNodes[1].node).toEqual(components.nodes[1]);
+
+ const rootNodes = sceneGraph._rootNodes;
+ expect(rootNodes[0]).toEqual(0);
});
- it("propagates node transforms correctly", function () {
- return loadAndZoomToModelAsync(
+ it("propagates node transforms correctly", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: parentGltfUrl,
upAxis: Axis.Z,
forwardAxis: Axis.X,
},
scene
- ).then(function (model) {
- const sceneGraph = model._sceneGraph;
- const components = sceneGraph._components;
- const runtimeNodes = sceneGraph._runtimeNodes;
-
- expect(components.upAxis).toEqual(Axis.Z);
- expect(components.forwardAxis).toEqual(Axis.X);
-
- const parentTransform = ModelUtility.getNodeTransform(
- components.nodes[0]
- );
- const childTransform = ModelUtility.getNodeTransform(
- components.nodes[1]
- );
- expect(runtimeNodes[0].transform).toEqual(parentTransform);
- expect(runtimeNodes[0].transformToRoot).toEqual(Matrix4.IDENTITY);
- expect(runtimeNodes[1].transform).toEqual(childTransform);
- expect(runtimeNodes[1].transformToRoot).toEqual(parentTransform);
- });
+ );
+ const sceneGraph = model._sceneGraph;
+ const components = sceneGraph._components;
+ const runtimeNodes = sceneGraph._runtimeNodes;
+
+ expect(components.upAxis).toEqual(Axis.Z);
+ expect(components.forwardAxis).toEqual(Axis.X);
+
+ const parentTransform = ModelUtility.getNodeTransform(
+ components.nodes[0]
+ );
+ const childTransform = ModelUtility.getNodeTransform(components.nodes[1]);
+ expect(runtimeNodes[0].transform).toEqual(parentTransform);
+ expect(runtimeNodes[0].transformToRoot).toEqual(Matrix4.IDENTITY);
+ expect(runtimeNodes[1].transform).toEqual(childTransform);
+ expect(runtimeNodes[1].transformToRoot).toEqual(parentTransform);
});
- it("creates runtime skin from model", function () {
- return loadAndZoomToModelAsync({ gltf: simpleSkinGltfUrl }, scene).then(
- function (model) {
- const sceneGraph = model._sceneGraph;
- const components = sceneGraph._components;
- const runtimeNodes = sceneGraph._runtimeNodes;
-
- expect(runtimeNodes[0].node).toEqual(components.nodes[0]);
- expect(runtimeNodes[1].node).toEqual(components.nodes[1]);
- expect(runtimeNodes[2].node).toEqual(components.nodes[2]);
-
- const rootNodes = sceneGraph._rootNodes;
- expect(rootNodes[0]).toEqual(0);
- expect(rootNodes[1]).toEqual(1);
-
- const runtimeSkins = sceneGraph._runtimeSkins;
- expect(runtimeSkins[0].skin).toEqual(components.skins[0]);
- expect(runtimeSkins[0].joints).toEqual([
- runtimeNodes[1],
- runtimeNodes[2],
- ]);
- expect(runtimeSkins[0].jointMatrices.length).toEqual(2);
-
- const skinnedNodes = sceneGraph._skinnedNodes;
- expect(skinnedNodes[0]).toEqual(0);
-
- expect(runtimeNodes[0].computedJointMatrices.length).toEqual(2);
- }
+ it("creates runtime skin from model", async function () {
+ const model = await loadAndZoomToModelAsync(
+ { gltf: simpleSkinGltfUrl },
+ scene
);
+
+ const sceneGraph = model._sceneGraph;
+ const components = sceneGraph._components;
+ const runtimeNodes = sceneGraph._runtimeNodes;
+
+ expect(runtimeNodes[0].node).toEqual(components.nodes[0]);
+ expect(runtimeNodes[1].node).toEqual(components.nodes[1]);
+ expect(runtimeNodes[2].node).toEqual(components.nodes[2]);
+
+ const rootNodes = sceneGraph._rootNodes;
+ expect(rootNodes[0]).toEqual(0);
+ expect(rootNodes[1]).toEqual(1);
+
+ const runtimeSkins = sceneGraph._runtimeSkins;
+ expect(runtimeSkins[0].skin).toEqual(components.skins[0]);
+ expect(runtimeSkins[0].joints).toEqual([
+ runtimeNodes[1],
+ runtimeNodes[2],
+ ]);
+ expect(runtimeSkins[0].jointMatrices.length).toEqual(2);
+
+ const skinnedNodes = sceneGraph._skinnedNodes;
+ expect(skinnedNodes[0]).toEqual(0);
+
+ expect(runtimeNodes[0].computedJointMatrices.length).toEqual(2);
});
- it("creates articulation from model", function () {
- return loadAndZoomToModelAsync({ gltf: boxArticulationsUrl }, scene).then(
- function (model) {
- const sceneGraph = model._sceneGraph;
- const components = sceneGraph._components;
- const runtimeNodes = sceneGraph._runtimeNodes;
-
- expect(runtimeNodes[0].node).toEqual(components.nodes[0]);
-
- const rootNodes = sceneGraph._rootNodes;
- expect(rootNodes[0]).toEqual(0);
-
- const runtimeArticulations = sceneGraph._runtimeArticulations;
- const runtimeArticulation =
- runtimeArticulations["SampleArticulation"];
- expect(runtimeArticulation).toBeDefined();
- expect(runtimeArticulation.name).toBe("SampleArticulation");
- expect(runtimeArticulation.runtimeNodes.length).toBe(1);
- expect(runtimeArticulation.runtimeStages.length).toBe(10);
- }
+ it("creates articulation from model", async function () {
+ const model = await loadAndZoomToModelAsync(
+ { gltf: boxArticulationsUrl },
+ scene
);
+
+ const sceneGraph = model._sceneGraph;
+ const components = sceneGraph._components;
+ const runtimeNodes = sceneGraph._runtimeNodes;
+
+ expect(runtimeNodes[0].node).toEqual(components.nodes[0]);
+
+ const rootNodes = sceneGraph._rootNodes;
+ expect(rootNodes[0]).toEqual(0);
+
+ const runtimeArticulations = sceneGraph._runtimeArticulations;
+ const runtimeArticulation = runtimeArticulations["SampleArticulation"];
+ expect(runtimeArticulation).toBeDefined();
+ expect(runtimeArticulation.name).toBe("SampleArticulation");
+ expect(runtimeArticulation.runtimeNodes.length).toBe(1);
+ expect(runtimeArticulation.runtimeStages.length).toBe(10);
});
- it("applies articulations", function () {
- return loadAndZoomToModelAsync(
+ it("applies articulations", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: boxArticulationsUrl,
},
scene
- ).then(function (model) {
- const sceneGraph = model._sceneGraph;
- const runtimeNodes = sceneGraph._runtimeNodes;
- const rootNode = runtimeNodes[0];
-
- expect(rootNode.transform).toEqual(rootNode.originalTransform);
-
- sceneGraph.setArticulationStage("SampleArticulation MoveX", 1.0);
- sceneGraph.setArticulationStage("SampleArticulation MoveY", 2.0);
- sceneGraph.setArticulationStage("SampleArticulation MoveZ", 3.0);
- sceneGraph.setArticulationStage("SampleArticulation Yaw", 4.0);
- sceneGraph.setArticulationStage("SampleArticulation Pitch", 5.0);
- sceneGraph.setArticulationStage("SampleArticulation Roll", 6.0);
- sceneGraph.setArticulationStage("SampleArticulation Size", 0.9);
- sceneGraph.setArticulationStage("SampleArticulation SizeX", 0.8);
- sceneGraph.setArticulationStage("SampleArticulation SizeY", 0.7);
- sceneGraph.setArticulationStage("SampleArticulation SizeZ", 0.6);
-
- // Articulations shouldn't affect the node until applyArticulations is called.
- expect(rootNode.transform).toEqual(rootNode.originalTransform);
-
- sceneGraph.applyArticulations();
-
- // prettier-ignore
- const expected = [
- 0.714769048324, -0.0434061192623, -0.074974104652, 0,
- -0.061883302957, 0.0590679731276, -0.624164586760, 0,
- 0.037525155822, 0.5366347296529, 0.047064101083, 0,
- 1, 3, -2, 1,
- ];
-
- expect(rootNode.transform).toEqualEpsilon(
- expected,
- CesiumMath.EPSILON10
- );
- });
+ );
+ const sceneGraph = model._sceneGraph;
+ const runtimeNodes = sceneGraph._runtimeNodes;
+ const rootNode = runtimeNodes[0];
+
+ expect(rootNode.transform).toEqual(rootNode.originalTransform);
+
+ sceneGraph.setArticulationStage("SampleArticulation MoveX", 1.0);
+ sceneGraph.setArticulationStage("SampleArticulation MoveY", 2.0);
+ sceneGraph.setArticulationStage("SampleArticulation MoveZ", 3.0);
+ sceneGraph.setArticulationStage("SampleArticulation Yaw", 4.0);
+ sceneGraph.setArticulationStage("SampleArticulation Pitch", 5.0);
+ sceneGraph.setArticulationStage("SampleArticulation Roll", 6.0);
+ sceneGraph.setArticulationStage("SampleArticulation Size", 0.9);
+ sceneGraph.setArticulationStage("SampleArticulation SizeX", 0.8);
+ sceneGraph.setArticulationStage("SampleArticulation SizeY", 0.7);
+ sceneGraph.setArticulationStage("SampleArticulation SizeZ", 0.6);
+
+ // Articulations shouldn't affect the node until applyArticulations is called.
+ expect(rootNode.transform).toEqual(rootNode.originalTransform);
+
+ sceneGraph.applyArticulations();
+
+ // prettier-ignore
+ const expected = [
+ 0.714769048324, -0.0434061192623, -0.074974104652, 0,
+ -0.061883302957, 0.0590679731276, -0.624164586760, 0,
+ 0.037525155822, 0.5366347296529, 0.047064101083, 0,
+ 1, 3, -2, 1,
+ ];
+
+ expect(rootNode.transform).toEqualEpsilon(expected, CesiumMath.EPSILON10);
});
- it("adds ModelColorPipelineStage when color is set on the model", function () {
+ it("adds ModelColorPipelineStage when color is set on the model", async function () {
spyOn(ModelColorPipelineStage, "process");
- return loadAndZoomToModelAsync(
+ await loadAndZoomToModelAsync(
{
color: Color.RED,
gltf: parentGltfUrl,
},
scene
- ).then(function () {
- expect(ModelColorPipelineStage.process).toHaveBeenCalled();
- });
+ );
+ expect(ModelColorPipelineStage.process).toHaveBeenCalled();
});
- it("adds CustomShaderPipelineStage when customShader is set on the model", function () {
+ it("adds CustomShaderPipelineStage when customShader is set on the model", async function () {
spyOn(CustomShaderPipelineStage, "process");
- return loadAndZoomToModelAsync(
+ const model = await loadAndZoomToModelAsync(
{
gltf: buildingsMetadata,
},
scene
- ).then(function (model) {
- model.customShader = new CustomShader();
- model.update(scene.frameState);
- expect(CustomShaderPipelineStage.process).toHaveBeenCalled();
- });
+ );
+ model.customShader = new CustomShader();
+ model.update(scene.frameState);
+ expect(CustomShaderPipelineStage.process).toHaveBeenCalled();
});
- it("does not add fog stage when fog is not enabled", function () {
+ it("does not add fog stage when fog is not enabled", async function () {
spyOn(FogPipelineStage, "process");
scene.fog.enabled = false;
scene.fog.renderable = false;
- return loadAndZoomToModelAsync(
+ const model = await loadAndZoomToModelAsync(
{
gltf: buildingsMetadata,
},
scene
- ).then(function (model) {
- model.customShader = new CustomShader();
- model.update(scene.frameState);
- expect(FogPipelineStage.process).not.toHaveBeenCalled();
- });
+ );
+ model.customShader = new CustomShader();
+ model.update(scene.frameState);
+ expect(FogPipelineStage.process).not.toHaveBeenCalled();
});
- it("does not add fog stage when fog is not renderable", function () {
+ it("does not add fog stage when fog is not renderable", async function () {
spyOn(FogPipelineStage, "process");
scene.fog.enabled = true;
scene.fog.renderable = false;
- return loadAndZoomToModelAsync(
+ const model = await loadAndZoomToModelAsync(
{
gltf: buildingsMetadata,
},
scene
- ).then(function (model) {
- model.customShader = new CustomShader();
- model.update(scene.frameState);
- expect(FogPipelineStage.process).not.toHaveBeenCalled();
- });
+ );
+ model.customShader = new CustomShader();
+ model.update(scene.frameState);
+ expect(FogPipelineStage.process).not.toHaveBeenCalled();
});
it("adds fog stage when fog is enabled and renderable", async function () {
@@ -403,41 +388,40 @@ describe(
expect(FogPipelineStage.process).toHaveBeenCalled();
});
- it("pushDrawCommands ignores hidden nodes", function () {
- return loadAndZoomToModelAsync(
+ it("pushDrawCommands ignores hidden nodes", async function () {
+ const model = await loadAndZoomToModelAsync(
{
gltf: duckUrl,
},
scene
- ).then(function (model) {
- const frameState = scene.frameState;
- const commandList = frameState.commandList;
-
- const sceneGraph = model._sceneGraph;
- const rootNode = sceneGraph._runtimeNodes[0];
- const meshNode = sceneGraph._runtimeNodes[2];
-
- expect(rootNode.show).toBe(true);
- expect(meshNode.show).toBe(true);
-
- sceneGraph.pushDrawCommands(frameState);
- const originalLength = commandList.length;
- expect(originalLength).not.toEqual(0);
-
- commandList.length = 0;
- meshNode.show = false;
- sceneGraph.pushDrawCommands(frameState);
- expect(commandList.length).toEqual(0);
-
- meshNode.show = true;
- rootNode.show = false;
- sceneGraph.pushDrawCommands(frameState);
- expect(commandList.length).toEqual(0);
-
- rootNode.show = true;
- sceneGraph.pushDrawCommands(frameState);
- expect(commandList.length).toEqual(originalLength);
- });
+ );
+ const frameState = scene.frameState;
+ const commandList = frameState.commandList;
+
+ const sceneGraph = model._sceneGraph;
+ const rootNode = sceneGraph._runtimeNodes[0];
+ const meshNode = sceneGraph._runtimeNodes[2];
+
+ expect(rootNode.show).toBe(true);
+ expect(meshNode.show).toBe(true);
+
+ sceneGraph.pushDrawCommands(frameState);
+ const originalLength = commandList.length;
+ expect(originalLength).not.toEqual(0);
+
+ commandList.length = 0;
+ meshNode.show = false;
+ sceneGraph.pushDrawCommands(frameState);
+ expect(commandList.length).toEqual(0);
+
+ meshNode.show = true;
+ rootNode.show = false;
+ sceneGraph.pushDrawCommands(frameState);
+ expect(commandList.length).toEqual(0);
+
+ rootNode.show = true;
+ sceneGraph.pushDrawCommands(frameState);
+ expect(commandList.length).toEqual(originalLength);
});
it("throws for undefined options.model", function () {
From 393e7fc59cfa44133334aeb619aad0bb67e538dd Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 14:28:00 -0500
Subject: [PATCH 37/55] Update documentation
---
packages/engine/Source/Scene/SkyAtmosphere.js | 2 +-
.../Source/Shaders/Builtin/Functions/applyHSBShift.glsl | 5 +++--
.../Source/Shaders/Builtin/Functions/approximateTanh.glsl | 2 +-
.../Source/Shaders/Builtin/Functions/computeScattering.glsl | 2 --
packages/engine/Source/Shaders/Model/ModelVS.glsl | 1 +
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/packages/engine/Source/Scene/SkyAtmosphere.js b/packages/engine/Source/Scene/SkyAtmosphere.js
index c97a7a88e098..628737ce5610 100644
--- a/packages/engine/Source/Scene/SkyAtmosphere.js
+++ b/packages/engine/Source/Scene/SkyAtmosphere.js
@@ -218,7 +218,7 @@ Object.defineProperties(SkyAtmosphere.prototype, {
/**
* Set the dynamic lighting enum value for the shader
- * @param {DynamicAtmosphereLightingType} lightingEnum
+ * @param {DynamicAtmosphereLightingType} lightingEnum The enum that determines the dynamic atmosphere light source
*
* @private
*/
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl b/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
index 81575fecab1f..30b9424eb4ea 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
@@ -1,9 +1,10 @@
/**
- * Apply a color shift in HSB color space
- *
+ * Apply a HSB color shift to an RGB color.
*
* @param {vec3} rgb The color in RGB space.
* @param {vec3} hsbShift The amount to shift each component. The xyz components correspond to hue, saturation, and brightness. Shifting the hue by +/- 1.0 corresponds to shifting the hue by a full cycle. Saturation and brightness are clamped between 0 and 1 after the adjustment
+ *
+ * @return {vec3} The RGB color after shifting in HSB space and clamping saturation and brightness to a valid range.
*/
vec3 czm_applyHSBShift(vec3 rgb, vec3 hsbShift) {
// Convert rgb color to hsb
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl b/packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl
index f956b14274c3..4c8132762cf9 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/approximateTanh.glsl
@@ -6,5 +6,5 @@
*/
float czm_approximateTanh(float x) {
float x2 = x * x;
- return max(-1.0, min(+1.0, x * (27.0 + x2) / (27.0 + 9.0 * x2)));
+ return max(-1.0, min(1.0, x * (27.0 + x2) / (27.0 + 9.0 * x2)));
}
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
index f3c5bb9c5d57..76edcf9ed525 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/computeScattering.glsl
@@ -133,8 +133,6 @@ void czm_computeScattering(
// Compute attenuation via the primary ray and the light ray.
vec3 attenuation = exp(-((czm_atmosphereMieCoefficient * (opticalDepth.y + lightOpticalDepth.y)) + (czm_atmosphereRayleighCoefficient * (opticalDepth.x + lightOpticalDepth.x))));
- //lastAttenuation = vec3(rayStepLength, lightStepLength);
-
// Accumulate the scattering.
rayleighAccumulation += sampleDensity.x * attenuation;
mieAccumulation += sampleDensity.y * attenuation;
diff --git a/packages/engine/Source/Shaders/Model/ModelVS.glsl b/packages/engine/Source/Shaders/Model/ModelVS.glsl
index b16a634161d0..fde15b377409 100644
--- a/packages/engine/Source/Shaders/Model/ModelVS.glsl
+++ b/packages/engine/Source/Shaders/Model/ModelVS.glsl
@@ -106,6 +106,7 @@ void main()
// This returns the value that will be assigned to gl_Position.
vec4 positionClip = geometryStage(attributes, modelView, normal);
+ // This must go after the geometry stage as it needs v_positionWC
#ifdef HAS_FOG
fogStage(attributes);
#endif
From 9b4c8468d160942e2d3934dee4250a94aa16dab7 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 14:38:11 -0500
Subject: [PATCH 38/55] Add specs for DynamicAtmosphereLightingType
---
.../DynamicAtmosphereLightingTypeSpec.js | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js
diff --git a/packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js b/packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js
new file mode 100644
index 000000000000..e2fcc864ddd6
--- /dev/null
+++ b/packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js
@@ -0,0 +1,47 @@
+import { DynamicAtmosphereLightingType } from "../../index.js";
+
+describe("Scene/DynamicAtmosphereLightingType", function () {
+ function mockGlobe() {
+ return {
+ enableLighting: false,
+ dynamicAtmosphereLighting: false,
+ dynamicAtmosphereLightingFromSun: false,
+ };
+ }
+
+ it("returns OFF when lighting is disabled", function () {
+ const globe = mockGlobe();
+
+ expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
+ DynamicAtmosphereLightingType.OFF
+ );
+
+ globe.enableLighting = true;
+
+ expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
+ DynamicAtmosphereLightingType.OFF
+ );
+
+ globe.enableLighting = false;
+ globe.dynamicAtmosphereLighting = true;
+ expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
+ DynamicAtmosphereLightingType.OFF
+ );
+ });
+
+ it("selects a light type depending on globe.dynamicAtmosphereLightingFromSun", function () {
+ const globe = mockGlobe();
+ globe.enableLighting = true;
+ globe.dynamicAtmosphereLighting = true;
+
+ globe.dynamicAtmosphereLightingFromSun = true;
+ expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
+ DynamicAtmosphereLightingType.SUNLIGHT
+ );
+
+ globe.dynamicAtmosphereLightingFromSun = false;
+ expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
+ DynamicAtmosphereLightingType.SCENE_LIGHT
+ );
+ });
+});
From e0f9b33a3ab2a85f61b1ccc7e9f0834bdc02b50e Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Wed, 10 Jan 2024 14:50:48 -0500
Subject: [PATCH 39/55] Rename FogPipelineStage -> AtmospherePipelineStage
---
...ineStage.js => AtmospherePipelineStage.js} | 27 +++++++++++--------
.../Source/Scene/Model/ModelSceneGraph.js | 4 +--
...FogStageFS.glsl => AtmosphereStageFS.glsl} | 2 +-
...FogStageVS.glsl => AtmosphereStageVS.glsl} | 2 +-
.../engine/Source/Shaders/Model/ModelFS.glsl | 4 +--
.../engine/Source/Shaders/Model/ModelVS.glsl | 4 +--
...Spec.js => AtmospherePipelineStageSpec.js} | 26 +++++++++---------
.../Specs/Scene/Model/ModelSceneGraphSpec.js | 14 +++++-----
8 files changed, 44 insertions(+), 39 deletions(-)
rename packages/engine/Source/Scene/Model/{FogPipelineStage.js => AtmospherePipelineStage.js} (65%)
rename packages/engine/Source/Shaders/Model/{FogStageFS.glsl => AtmosphereStageFS.glsl} (98%)
rename packages/engine/Source/Shaders/Model/{FogStageVS.glsl => AtmosphereStageVS.glsl} (86%)
rename packages/engine/Specs/Scene/Model/{FogPipelineStageSpec.js => AtmospherePipelineStageSpec.js} (81%)
diff --git a/packages/engine/Source/Scene/Model/FogPipelineStage.js b/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js
similarity index 65%
rename from packages/engine/Source/Scene/Model/FogPipelineStage.js
rename to packages/engine/Source/Scene/Model/AtmospherePipelineStage.js
index 5a323e59b114..672e5fccc316 100644
--- a/packages/engine/Source/Scene/Model/FogPipelineStage.js
+++ b/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js
@@ -1,24 +1,29 @@
import Cartesian3 from "../../Core/Cartesian3.js";
import CesiumMath from "../../Core/Math.js";
import ShaderDestination from "../../Renderer/ShaderDestination.js";
-import FogStageFS from "../../Shaders/Model/FogStageFS.js";
-import FogStageVS from "../../Shaders/Model/FogStageVS.js";
+import AtmosphereStageFS from "../../Shaders/Model/AtmosphereStageFS.js";
+import AtmosphereStageVS from "../../Shaders/Model/AtmosphereStageVS.js";
/**
- * The fog color pipeline stage is responsible for applying fog to tiles in the distance in horizon views.
+ * The atmosphere pipeline stage applies all earth atmosphere effects that apply
+ * to models, including fog.
*
- * @namespace FogColorPipelineStage
+ * @namespace AtmospherePipelineStage
*
* @private
*/
-const FogPipelineStage = {
- name: "FogPipelineStage", // Helps with debugging
+const AtmospherePipelineStage = {
+ name: "AtmospherePipelineStage", // Helps with debugging
};
-FogPipelineStage.process = function (renderResources, model, frameState) {
+AtmospherePipelineStage.process = function (
+ renderResources,
+ model,
+ frameState
+) {
const shaderBuilder = renderResources.shaderBuilder;
- shaderBuilder.addDefine("HAS_FOG", undefined, ShaderDestination.BOTH);
+ shaderBuilder.addDefine("HAS_ATMOSPHERE", undefined, ShaderDestination.BOTH);
shaderBuilder.addDefine(
"COMPUTE_POSITION_WC_ATMOSPHERE",
undefined,
@@ -29,8 +34,8 @@ FogPipelineStage.process = function (renderResources, model, frameState) {
shaderBuilder.addVarying("vec3", "v_atmosphereMieColor");
shaderBuilder.addVarying("float", "v_atmosphereOpacity");
- shaderBuilder.addVertexLines([FogStageVS]);
- shaderBuilder.addFragmentLines([FogStageFS]);
+ shaderBuilder.addVertexLines([AtmosphereStageVS]);
+ shaderBuilder.addFragmentLines([AtmosphereStageFS]);
// Add a uniform so fog is only calculated when the efcfect would
// be non-negligible For example when the camera is in space, fog density decreases
@@ -51,4 +56,4 @@ FogPipelineStage.process = function (renderResources, model, frameState) {
};
};
-export default FogPipelineStage;
+export default AtmospherePipelineStage;
diff --git a/packages/engine/Source/Scene/Model/ModelSceneGraph.js b/packages/engine/Source/Scene/Model/ModelSceneGraph.js
index 72e3cbf11d7b..0dacc4c85b5d 100644
--- a/packages/engine/Source/Scene/Model/ModelSceneGraph.js
+++ b/packages/engine/Source/Scene/Model/ModelSceneGraph.js
@@ -9,7 +9,7 @@ import SceneMode from "../SceneMode.js";
import SplitDirection from "../SplitDirection.js";
import buildDrawCommand from "./buildDrawCommand.js";
import TilesetPipelineStage from "./TilesetPipelineStage.js";
-import FogPipelineStage from "./FogPipelineStage.js";
+import AtmospherePipelineStage from "./AtmospherePipelineStage.js";
import ImageBasedLightingPipelineStage from "./ImageBasedLightingPipelineStage.js";
import ModelArticulation from "./ModelArticulation.js";
import ModelColorPipelineStage from "./ModelColorPipelineStage.js";
@@ -642,7 +642,7 @@ ModelSceneGraph.prototype.configurePipeline = function (frameState) {
}
if (fogRenderable) {
- modelPipelineStages.push(FogPipelineStage);
+ modelPipelineStages.push(AtmospherePipelineStage);
}
};
diff --git a/packages/engine/Source/Shaders/Model/FogStageFS.glsl b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
similarity index 98%
rename from packages/engine/Source/Shaders/Model/FogStageFS.glsl
rename to packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
index 5ac40c52f2af..1b91e3f87c5f 100644
--- a/packages/engine/Source/Shaders/Model/FogStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
@@ -63,7 +63,7 @@ void applyFog(inout vec4 color, vec4 groundAtmosphereColor, vec3 lightDirection,
color = vec4(withFog, color.a);
}
-void fogStage(inout vec4 color, in ProcessedAttributes attributes) {
+void atmosphereStage(inout vec4 color, in ProcessedAttributes attributes) {
vec3 rayleighColor;
vec3 mieColor;
float opacity;
diff --git a/packages/engine/Source/Shaders/Model/FogStageVS.glsl b/packages/engine/Source/Shaders/Model/AtmosphereStageVS.glsl
similarity index 86%
rename from packages/engine/Source/Shaders/Model/FogStageVS.glsl
rename to packages/engine/Source/Shaders/Model/AtmosphereStageVS.glsl
index 6a81b426105f..ad5809f50d9f 100644
--- a/packages/engine/Source/Shaders/Model/FogStageVS.glsl
+++ b/packages/engine/Source/Shaders/Model/AtmosphereStageVS.glsl
@@ -1,4 +1,4 @@
-void fogStage(ProcessedAttributes attributes) {
+void atmosphereStage(ProcessedAttributes attributes) {
vec3 lightDirection = czm_getDynamicAtmosphereLightDirection(v_positionWC, czm_atmosphereDynamicLighting);
czm_computeGroundAtmosphereScattering(
diff --git a/packages/engine/Source/Shaders/Model/ModelFS.glsl b/packages/engine/Source/Shaders/Model/ModelFS.glsl
index 534c7f2e78f2..13d2aec6663e 100644
--- a/packages/engine/Source/Shaders/Model/ModelFS.glsl
+++ b/packages/engine/Source/Shaders/Model/ModelFS.glsl
@@ -79,8 +79,8 @@ void main()
silhouetteStage(color);
#endif
- #ifdef HAS_FOG
- fogStage(color, attributes);
+ #ifdef HAS_ATMOSPHERE
+ atmosphereStage(color, attributes);
#endif
out_FragColor = color;
diff --git a/packages/engine/Source/Shaders/Model/ModelVS.glsl b/packages/engine/Source/Shaders/Model/ModelVS.glsl
index fde15b377409..36a65de3e1e3 100644
--- a/packages/engine/Source/Shaders/Model/ModelVS.glsl
+++ b/packages/engine/Source/Shaders/Model/ModelVS.glsl
@@ -107,8 +107,8 @@ void main()
vec4 positionClip = geometryStage(attributes, modelView, normal);
// This must go after the geometry stage as it needs v_positionWC
- #ifdef HAS_FOG
- fogStage(attributes);
+ #ifdef HAS_ATMOSPHERE
+ atmosphereStage(attributes);
#endif
#ifdef HAS_SILHOUETTE
diff --git a/packages/engine/Specs/Scene/Model/FogPipelineStageSpec.js b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
similarity index 81%
rename from packages/engine/Specs/Scene/Model/FogPipelineStageSpec.js
rename to packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
index 281ed762ab02..a806709784bd 100644
--- a/packages/engine/Specs/Scene/Model/FogPipelineStageSpec.js
+++ b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
@@ -1,13 +1,13 @@
import {
- _shadersFogStageFS,
- _shadersFogStageVS,
+ _shadersAtmosphereStageFS,
+ _shadersAtmosphereStageVS,
Cartesian3,
- FogPipelineStage,
+ AtmospherePipelineStage,
ShaderBuilder,
} from "../../../index.js";
import ShaderBuilderTester from "../../../../../Specs/ShaderBuilderTester.js";
-describe("Scene/Model/FogPipelineStage", function () {
+describe("Scene/Model/AtmospherePipelineStage", function () {
const mockModel = {
boundingSphere: {
center: Cartesian3.fromDegrees(0, 0, 0),
@@ -34,20 +34,20 @@ describe("Scene/Model/FogPipelineStage", function () {
};
}
- it("Configures shader", function () {
+ it("configures shader", function () {
const renderResources = mockRenderResources();
const frameState = mockFrameState();
- FogPipelineStage.process(renderResources, mockModel, frameState);
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
const shaderBuilder = renderResources.shaderBuilder;
ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, [
- "HAS_FOG",
+ "HAS_ATMOSPHERE",
"COMPUTE_POSITION_WC_ATMOSPHERE",
]);
ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, [
- "HAS_FOG",
+ "HAS_ATMOSPHERE",
"COMPUTE_POSITION_WC_ATMOSPHERE",
]);
@@ -58,10 +58,10 @@ describe("Scene/Model/FogPipelineStage", function () {
]);
ShaderBuilderTester.expectVertexLinesEqual(shaderBuilder, [
- _shadersFogStageVS,
+ _shadersAtmosphereStageVS,
]);
ShaderBuilderTester.expectFragmentLinesEqual(shaderBuilder, [
- _shadersFogStageFS,
+ _shadersAtmosphereStageFS,
]);
ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, []);
@@ -79,7 +79,7 @@ describe("Scene/Model/FogPipelineStage", function () {
frameState.camera.position
);
- FogPipelineStage.process(renderResources, mockModel, frameState);
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
const uniformMap = renderResources.uniformMap;
expect(uniformMap.u_isInFog()).toBe(false);
@@ -95,7 +95,7 @@ describe("Scene/Model/FogPipelineStage", function () {
frameState.camera.position = Cartesian3.fromDegrees(0.001, 0, 100000);
frameState.fog.density = 0;
- FogPipelineStage.process(renderResources, mockModel, frameState);
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
const uniformMap = renderResources.uniformMap;
expect(uniformMap.u_isInFog()).toBe(false);
@@ -105,7 +105,7 @@ describe("Scene/Model/FogPipelineStage", function () {
const renderResources = mockRenderResources();
const frameState = mockFrameState();
- FogPipelineStage.process(renderResources, mockModel, frameState);
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
const uniformMap = renderResources.uniformMap;
expect(uniformMap.u_isInFog()).toBe(true);
diff --git a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
index 05f86fcf865d..8de1acdc3ee6 100644
--- a/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSceneGraphSpec.js
@@ -5,7 +5,7 @@ import {
CustomShader,
CustomShaderPipelineStage,
Fog,
- FogPipelineStage,
+ AtmospherePipelineStage,
Math as CesiumMath,
Matrix4,
ModelColorPipelineStage,
@@ -344,7 +344,7 @@ describe(
});
it("does not add fog stage when fog is not enabled", async function () {
- spyOn(FogPipelineStage, "process");
+ spyOn(AtmospherePipelineStage, "process");
scene.fog.enabled = false;
scene.fog.renderable = false;
const model = await loadAndZoomToModelAsync(
@@ -355,11 +355,11 @@ describe(
);
model.customShader = new CustomShader();
model.update(scene.frameState);
- expect(FogPipelineStage.process).not.toHaveBeenCalled();
+ expect(AtmospherePipelineStage.process).not.toHaveBeenCalled();
});
it("does not add fog stage when fog is not renderable", async function () {
- spyOn(FogPipelineStage, "process");
+ spyOn(AtmospherePipelineStage, "process");
scene.fog.enabled = true;
scene.fog.renderable = false;
const model = await loadAndZoomToModelAsync(
@@ -370,11 +370,11 @@ describe(
);
model.customShader = new CustomShader();
model.update(scene.frameState);
- expect(FogPipelineStage.process).not.toHaveBeenCalled();
+ expect(AtmospherePipelineStage.process).not.toHaveBeenCalled();
});
it("adds fog stage when fog is enabled and renderable", async function () {
- spyOn(FogPipelineStage, "process");
+ spyOn(AtmospherePipelineStage, "process");
scene.fog.enabled = true;
scene.fog.renderable = true;
const model = await loadAndZoomToModelAsync(
@@ -385,7 +385,7 @@ describe(
);
model.customShader = new CustomShader();
model.update(scene.frameState);
- expect(FogPipelineStage.process).toHaveBeenCalled();
+ expect(AtmospherePipelineStage.process).toHaveBeenCalled();
});
it("pushDrawCommands ignores hidden nodes", async function () {
From 5236406612fe7dbdd96ee4ccccdab635699b7dfa Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 11:17:43 -0500
Subject: [PATCH 40/55] add render tests for model fog
---
.../Scene/Model/AtmospherePipelineStage.js | 2 +-
.../engine/Specs/Scene/Model/ModelSpec.js | 438 ++++++++++++++++++
2 files changed, 439 insertions(+), 1 deletion(-)
diff --git a/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js b/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js
index 672e5fccc316..46b9e97debda 100644
--- a/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js
+++ b/packages/engine/Source/Scene/Model/AtmospherePipelineStage.js
@@ -46,7 +46,7 @@ AtmospherePipelineStage.process = function (
// We only need a rough measure of distance to the model, so measure
// from the camera to the bounding sphere center.
const distance = Cartesian3.distance(
- frameState.camera.position,
+ frameState.camera.positionWC,
model.boundingSphere.center
);
diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js
index 25c1f5c802a2..68b68d77d918 100644
--- a/packages/engine/Specs/Scene/Model/ModelSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSpec.js
@@ -12,11 +12,14 @@ import {
Credit,
defaultValue,
defined,
+ DirectionalLight,
DistanceDisplayCondition,
+ DynamicAtmosphereLightingType,
DracoLoader,
Ellipsoid,
Event,
FeatureDetection,
+ Fog,
HeadingPitchRange,
HeadingPitchRoll,
HeightReference,
@@ -39,6 +42,7 @@ import {
ShadowMode,
SplitDirection,
StyleCommandsNeeded,
+ SunLight,
Transforms,
WireframeIndexGenerator,
} from "../../../index.js";
@@ -4411,6 +4415,440 @@ describe(
});
});
+ describe("fog", function () {
+ const sunnyDate = JulianDate.fromIso8601("2024-01-11T15:00:00Z");
+ const darkDate = JulianDate.fromIso8601("2024-01-11T00:00:00Z");
+
+ afterEach(function () {
+ scene.fog = new Fog();
+ scene.light = new SunLight();
+ scene.camera.switchToPerspectiveFrustum();
+ });
+
+ function viewFog(scene, model) {
+ // In order for fog to create a visible change, the camera needs to be
+ // further away from the model. This would make the box sub-pixel
+ // so to make it fill the canvas, use an ortho camera the same
+ // width of the box to make the scene look 2D.
+ const center = model.boundingSphere.center;
+ scene.camera.lookAt(center, new Cartesian3(1000, 0, 0));
+ scene.camera.switchToOrthographicFrustum();
+ scene.camera.frustum.width = 1;
+ }
+
+ it("renders a model in fog", async function () {
+ // Move the fog very close to the camera;
+ scene.fog.density = 1.0;
+
+ // Increase the brightness to make the fog color
+ // stand out more for this test
+ scene.atmosphere.brightnessShift = 1.0;
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(0, 0, 10.0)
+ ),
+ },
+ scene
+ );
+
+ viewFog(scene, model);
+
+ const renderOptions = {
+ scene,
+ time: sunnyDate,
+ };
+
+ // First, turn off the fog to capture the original color
+ let originalColor;
+ scene.fog.enabled = false;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ originalColor = rgba;
+ expect(originalColor).not.toEqual([0, 0, 0, 255]);
+ });
+
+ // Now turn on fog. The result should be bluish
+ // than before due to scattering.
+ scene.fog.enabled = true;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+
+ // The result should have a bluish tint
+ const [r, g, b, a] = rgba;
+ expect(b).toBeGreaterThan(r);
+ expect(b).toBeGreaterThan(g);
+ expect(a).toBe(255);
+ });
+ });
+
+ it("renders a model in fog (sunlight)", async function () {
+ // Move the fog very close to the camera;
+ scene.fog.density = 1.0;
+
+ // Increase the brightness to make the fog color
+ // stand out more for this test
+ scene.atmosphere.brightnessShift = 1.0;
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(0, 0, 10.0)
+ ),
+ },
+ scene
+ );
+
+ // In order for fog to render, the camera needs to be
+ // further away from the model. This would make the box sub-pixel
+ // so to make it fill the canvas, use an ortho camera the same
+ // width of the box to make the scene look 2D.
+ const center = model.boundingSphere.center;
+ scene.camera.lookAt(center, new Cartesian3(1000, 0, 0));
+ scene.camera.switchToOrthographicFrustum();
+ scene.camera.frustum.width = 1;
+
+ // Grab the color when dynamic lighting is off for comparison
+ scene.atmosphere.dynamicLighting = DynamicAtmosphereLightingType.OFF;
+ const renderOptions = {
+ scene,
+ time: sunnyDate,
+ };
+ let originalColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ originalColor = rgba;
+ expect(originalColor).not.toEqual([0, 0, 0, 255]);
+ });
+
+ // switch the lighting model to sunlight
+ scene.atmosphere.dynamicLighting =
+ DynamicAtmosphereLightingType.SUNLIGHT;
+
+ // Render in the sun, it should be a different color than the
+ // original
+ let sunnyColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ sunnyColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+ });
+
+ // Render in the dark, it should be a different color and
+ // darker than in sun
+ renderOptions.time = darkDate;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+ expect(rgba).not.toEqual(sunnyColor);
+
+ const [sunR, sunG, sunB, sunA] = sunnyColor;
+ const [r, g, b, a] = rgba;
+ expect(r).toBeLessThan(sunR);
+ expect(g).toBeLessThan(sunG);
+ expect(b).toBeLessThan(sunB);
+ expect(a).toBe(sunA);
+ });
+ });
+
+ it("renders a model in fog (scene light)", async function () {
+ // Move the fog very close to the camera;
+ scene.fog.density = 1.0;
+
+ // Increase the brightness to make the fog color
+ // stand out more for this test
+ scene.atmosphere.brightnessShift = 1.0;
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(0, 0, 10.0)
+ ),
+ },
+ scene
+ );
+
+ viewFog(scene, model);
+
+ // Grab the color when dynamic lighting is off for comparison
+ scene.atmosphere.dynamicLighting = DynamicAtmosphereLightingType.OFF;
+ const renderOptions = {
+ scene,
+ time: sunnyDate,
+ };
+ let originalColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ originalColor = rgba;
+ expect(originalColor).not.toEqual([0, 0, 0, 255]);
+ });
+
+ // Also grab the color in sunlight for comparison
+ scene.atmosphere.dynamicLighting =
+ DynamicAtmosphereLightingType.SUNLIGHT;
+ let sunnyColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ sunnyColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+ });
+
+ // Set a light on the scene, but since dynamicLighting is SUNLIGHT,
+ // it should have no effect yet
+ scene.light = new DirectionalLight({
+ direction: new Cartesian3(0, 1, 0),
+ });
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).toEqual(sunnyColor);
+ });
+
+ // Set dynamic lighting to use the scene light, now it should
+ // render a different color from the other light sources
+ scene.atmosphere.dynamicLighting =
+ DynamicAtmosphereLightingType.SCENE_LIGHT;
+
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+ expect(rgba).not.toEqual(sunnyColor);
+ });
+ });
+
+ it("adjusts atmosphere light intensity", async function () {
+ // Move the fog very close to the camera;
+ scene.fog.density = 1.0;
+
+ // Increase the brightness to make the fog color
+ // stand out more. We'll use the light intensity to
+ // modulate this.
+ scene.atmosphere.brightnessShift = 1.0;
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(0, 0, 10.0)
+ ),
+ },
+ scene
+ );
+
+ viewFog(scene, model);
+
+ const renderOptions = {
+ scene,
+ time: sunnyDate,
+ };
+
+ // Grab the original color.
+ let originalColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ originalColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+
+ // The result should have a bluish tint from the atmosphere
+ const [r, g, b, a] = rgba;
+ expect(b).toBeGreaterThan(r);
+ expect(b).toBeGreaterThan(g);
+ expect(a).toBe(255);
+ });
+
+ // Turn down the light intensity
+ scene.atmosphere.lightIntensity = 5.0;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+
+ // Check that each component (except alpha) is darker than before
+ const [oldR, oldG, oldB, oldA] = originalColor;
+ const [r, g, b, a] = rgba;
+ expect(r).toBeLessThan(oldR);
+ expect(g).toBeLessThan(oldG);
+ expect(b).toBeLessThan(oldB);
+ expect(a).toBe(oldA);
+ });
+ });
+
+ it("applies a hue shift", async function () {
+ // Move the fog very close to the camera;
+ scene.fog.density = 1.0;
+
+ // Increase the brightness to make the fog color
+ // stand out more for this test
+ scene.atmosphere.brightnessShift = 1.0;
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(0, 0, 10.0)
+ ),
+ },
+ scene
+ );
+
+ viewFog(scene, model);
+
+ const renderOptions = {
+ scene,
+ time: sunnyDate,
+ };
+
+ // Grab the original color.
+ let originalColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ originalColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+
+ // The result should have a bluish tint from the atmosphere
+ const [r, g, b, a] = rgba;
+ expect(b).toBeGreaterThan(r);
+ expect(b).toBeGreaterThan(g);
+ expect(a).toBe(255);
+ });
+
+ // Shift the fog color to be reddish
+ scene.atmosphere.hueShift = 0.4;
+ let redColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ redColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(redColor).not.toEqual(originalColor);
+
+ // Check for a reddish tint
+ const [r, g, b, a] = rgba;
+ expect(r).toBeGreaterThan(g);
+ expect(r).toBeGreaterThan(b);
+ expect(a).toBe(255);
+ });
+
+ // ...now greenish
+ scene.atmosphere.hueShift = 0.7;
+ let greenColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ greenColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(greenColor).not.toEqual(originalColor);
+ expect(greenColor).not.toEqual(redColor);
+
+ // Check for a greenish tint
+ const [r, g, b, a] = rgba;
+ expect(g).toBeGreaterThan(r);
+ expect(g).toBeGreaterThan(b);
+ expect(a).toBe(255);
+ });
+
+ // ...and all the way around the color wheel back to bluish
+ scene.atmosphere.hueShift = 1.0;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).toEqual(originalColor);
+ });
+ });
+
+ it("applies a brightness shift", async function () {
+ // Move the fog very close to the camera;
+ scene.fog.density = 1.0;
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(0, 0, 10.0)
+ ),
+ },
+ scene
+ );
+
+ viewFog(scene, model);
+
+ const renderOptions = {
+ scene,
+ time: sunnyDate,
+ };
+
+ // Grab the original color.
+ let originalColor;
+ scene.atmosphere.brightnessShift = 1.0;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ originalColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+
+ // The result should have a bluish tint from the atmosphere
+ const [r, g, b, a] = rgba;
+ expect(b).toBeGreaterThan(r);
+ expect(b).toBeGreaterThan(g);
+ expect(a).toBe(255);
+ });
+
+ // Turn down the brightness
+ scene.atmosphere.brightnessShift = 0.5;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+
+ // Check that each component (except alpha) is darker than before
+ const [oldR, oldG, oldB, oldA] = originalColor;
+ const [r, g, b, a] = rgba;
+ expect(r).toBeLessThan(oldR);
+ expect(g).toBeLessThan(oldG);
+ expect(b).toBeLessThan(oldB);
+ expect(a).toBe(oldA);
+ });
+ });
+
+ it("applies a saturation shift", async function () {
+ // Move the fog very close to the camera;
+ scene.fog.density = 1.0;
+
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(
+ Cartesian3.fromDegrees(0, 0, 10.0)
+ ),
+ },
+ scene
+ );
+
+ viewFog(scene, model);
+
+ const renderOptions = {
+ scene,
+ time: sunnyDate,
+ };
+
+ // Grab the original color.
+ let originalColor;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ originalColor = rgba;
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+
+ // The result should have a bluish tint from the atmosphere
+ const [r, g, b, a] = rgba;
+ expect(b).toBeGreaterThan(r);
+ expect(b).toBeGreaterThan(g);
+ expect(a).toBe(255);
+ });
+
+ // Turn down the saturation all the way
+ scene.atmosphere.saturationShift = -1.0;
+ expect(renderOptions).toRenderAndCall(function (rgba) {
+ expect(rgba).not.toEqual([0, 0, 0, 255]);
+ expect(rgba).not.toEqual(originalColor);
+
+ // Check that each component (except alpha) is the same
+ // as grey values have R = G = B
+ const [r, g, b, a] = rgba;
+ expect(g).toBe(r);
+ expect(b).toBe(g);
+ expect(a).toBe(255);
+ });
+ });
+ });
+
it("pick returns position of intersection between ray and model surface", async function () {
const model = await loadAndZoomToModelAsync(
{
From 920ed281ab159e0b761837a8d99dbaa5a4984a42 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 11:30:25 -0500
Subject: [PATCH 41/55] Fix atmosphere pipeline specs
---
.../Specs/Scene/Model/AtmospherePipelineStageSpec.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
index a806709784bd..7a72c724e295 100644
--- a/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
+++ b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
@@ -19,7 +19,7 @@ describe("Scene/Model/AtmospherePipelineStage", function () {
camera: {
// position the camera a little bit east of the model
// and slightly above
- position: Cartesian3.fromDegrees(0.01, 0, 1),
+ positionWC: Cartesian3.fromDegrees(0.01, 0, 1),
},
fog: {
density: 2e-4,
@@ -74,9 +74,9 @@ describe("Scene/Model/AtmospherePipelineStage", function () {
const renderResources = mockRenderResources();
const frameState = mockFrameState();
- frameState.camera.position = Cartesian3.clone(
+ frameState.camera.positionWC = Cartesian3.clone(
mockModel.boundingSphere.center,
- frameState.camera.position
+ frameState.camera.positionWC
);
AtmospherePipelineStage.process(renderResources, mockModel, frameState);
@@ -92,7 +92,7 @@ describe("Scene/Model/AtmospherePipelineStage", function () {
// For this case, the fact that Fog decreases the density to 0 when
// the camera is far above the model is what causes u_isInFog to
// be false.
- frameState.camera.position = Cartesian3.fromDegrees(0.001, 0, 100000);
+ frameState.camera.positionWC = Cartesian3.fromDegrees(0.001, 0, 100000);
frameState.fog.density = 0;
AtmospherePipelineStage.process(renderResources, mockModel, frameState);
From dbbd50b9bcac6fc4f6809c10aa97f68a1577783d Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 11:33:36 -0500
Subject: [PATCH 42/55] Update changelog
---
CHANGES.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGES.md b/CHANGES.md
index ecc231f785dc..4a3f6b0c11e1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -15,6 +15,7 @@
- Added `Cesium3DTileset.getHeight` to sample height values of the loaded tiles. If using WebGL 1, the `enablePick` option must be set to true to use this function. [#11581](https://github.com/CesiumGS/cesium/pull/11581)
- Added `Cesium3DTileset.disableCollision` to allow the camera from to go inside or below a 3D tileset, for instance, to be used with 3D Tiles interiors. [#11581](https://github.com/CesiumGS/cesium/pull/11581)
- The `Cesium3DTileset.dynamicScreenSpaceError` optimization is now enabled by default, as this improves performance for street-level horizon views. Furthermore, the default settings of this feature were tuned for improved performance. `Cesium3DTileset.dynamicScreenSpaceErrorDensity` was changed from 0.00278 to 0.0002. `Cesium3DTileset.dynamicScreenSpaceErrorFactor` was changed from 4 to 24. [#11718](https://github.com/CesiumGS/cesium/pull/11718)
+- Fog rendering now applies to glTF models and 3D Tiles. This can be configured using `scene.fog` and the new `scene.atmosphere`. [#11744](https://github.com/CesiumGS/cesium/pull/11744)
##### Fixes :wrench:
From 060eb471683c45f5cd318c146bbf77667b6fd68f Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 13:53:15 -0500
Subject: [PATCH 43/55] Update documentation based on PR feedback
---
packages/engine/Source/Scene/Atmosphere.js | 34 +++++++++++++++++++
.../Scene/DynamicAtmosphereLightingType.js | 4 +--
packages/engine/Source/Scene/Scene.js | 9 +++++
3 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index ca2ce6d727b2..6913e2fe7577 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -1,6 +1,40 @@
import Cartesian3 from "../Core/Cartesian3.js";
import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType.js";
+/**
+ * Common atmosphere settings used by sky atmosphere, ground atmosphere, and fog.
+ *
+ *
+ * This class is not to be confused with {@link SkyAtmosphere}, which is responsible for rendering the sky.
+ *
+ *
+ * Currently, these settings only apply to 3D Tiles and models, but will eventually affect the sky atmosphere and globe. See {@link https://github.com/CesiumGS/cesium/issues/11681|issue #11681}.
+ *
+ *
+ * While the atmosphere settings affect the color of fog, see {@link Fog} to control how fog is rendered.
+ *
+ *
+ * @example
+ * // Turn on dynamic atmosphere lighting using the sun direction
+ * scene.atmosphere.dynamicLighting = Cesium.DynamicAtmosphereLightingType.SUNLIGHT;
+ *
+ * @example
+ * // Turn on dynamic lighting using whatever light source is in the scene
+ * scene.light = new Cesium.DirectionalLight({
+ * direction: new Cesium.Cartesian3(1, 0, 0);
+ * });
+ * scene.atmosphere.dynamicLighting = Cesium.DynamicAtmosphereLightingType.SCENE_LIGHT;
+ *
+ * @example
+ * // Adjust the color of the atmosphere effects.
+ * scene.atmosphere.hueShift = 0.4; // Cycle 40% around the color wheel
+ * scene.atmosphere.brightnessShift = 0.25; // Increase the brightness
+ * scene.atmosphere.saturationShift = -0.1; // Desaturate the colors
+ *
+ * @see SkyAtmosphere
+ * @see Globe
+ * @see Fog
+ */
function Atmosphere() {
/**
* The intensity of the light that is used for computing the ground atmosphere color.
diff --git a/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
index 892e072a5f08..a018222add16 100644
--- a/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
+++ b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
@@ -7,8 +7,8 @@
*/
const DynamicAtmosphereLightingType = {
/**
- * Do not use dynamic atmosphere lighting. Anything that uses atmosphere
- * lighting will be lit from directly above the vertex/fragment
+ * Do not use dynamic atmosphere lighting. Atmosphere lighting effects will
+ * be lit from directly above rather than using the scene's light source.
*
* @type {number}
* @constant
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index da4417c9e70b..d481993bf70f 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -514,6 +514,15 @@ function Scene(options) {
*/
this.cameraEventWaitTime = 500.0;
+ /**
+ * Settings for atmosphere lighting effects. This is not to be confused with
+ * {@link Scene#skyAtmosphere} which is responsible for rendering the sky.
+ *
+ * Currently these settings only apply to 3D Tiles and models. In the future this will apply to the globe as well, see {@link https://github.com/CesiumGS/cesium/issues/11681|issue #11681}.
+ *
+ *
+ * @type {Atmosphere}
+ */
this.atmosphere = new Atmosphere();
/**
From 7592fa48fd48e1a8a304f0ed6e56ad89df74a81a Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 14:32:02 -0500
Subject: [PATCH 44/55] Add missing JSDoc tags
---
packages/engine/Source/Scene/Atmosphere.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index 6913e2fe7577..cbdbecfc65ba 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -14,6 +14,9 @@ import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType.js";
* While the atmosphere settings affect the color of fog, see {@link Fog} to control how fog is rendered.
*
*
+ * @alias Atmosphere
+ * @constructor
+ *
* @example
* // Turn on dynamic atmosphere lighting using the sun direction
* scene.atmosphere.dynamicLighting = Cesium.DynamicAtmosphereLightingType.SUNLIGHT;
@@ -21,7 +24,7 @@ import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType.js";
* @example
* // Turn on dynamic lighting using whatever light source is in the scene
* scene.light = new Cesium.DirectionalLight({
- * direction: new Cesium.Cartesian3(1, 0, 0);
+ * direction: new Cesium.Cartesian3(1, 0, 0)
* });
* scene.atmosphere.dynamicLighting = Cesium.DynamicAtmosphereLightingType.SCENE_LIGHT;
*
From 7b930e2df3fb9895e5b0789c374fb20ab9c5d857 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 15:07:19 -0500
Subject: [PATCH 45/55] Mark specs as requiring WebGL
---
packages/engine/Specs/Scene/AtmosphereSpec.js | 124 ++++++-----
.../Model/AtmospherePipelineStageSpec.js | 206 +++++++++---------
2 files changed, 169 insertions(+), 161 deletions(-)
diff --git a/packages/engine/Specs/Scene/AtmosphereSpec.js b/packages/engine/Specs/Scene/AtmosphereSpec.js
index 9758be52837c..d23a1db0f4f5 100644
--- a/packages/engine/Specs/Scene/AtmosphereSpec.js
+++ b/packages/engine/Specs/Scene/AtmosphereSpec.js
@@ -2,68 +2,72 @@ import { Cartesian3, DynamicAtmosphereLightingType } from "../../index.js";
import createScene from "../../../../Specs/createScene";
-describe("scene/Atmosphere", function () {
- let scene;
- beforeEach(function () {
- scene = createScene();
- });
+describe(
+ "scene/Atmosphere",
+ function () {
+ let scene;
+ beforeEach(function () {
+ scene = createScene();
+ });
- afterEach(function () {
- scene.destroyForSpecs();
- });
+ afterEach(function () {
+ scene.destroyForSpecs();
+ });
- it("updates frameState each frame", function () {
- const atmosphere = scene.atmosphere;
- const frameStateAtmosphere = scene.frameState.atmosphere;
+ it("updates frameState each frame", function () {
+ const atmosphere = scene.atmosphere;
+ const frameStateAtmosphere = scene.frameState.atmosphere;
- // Render and check that scene.atmosphere updated
- // frameState.atmosphere. For the first frame this should
- // be the default settings.
- scene.renderForSpecs();
- expect(frameStateAtmosphere.hsbShift).toEqual(new Cartesian3());
- expect(frameStateAtmosphere.lightIntensity).toEqual(10.0);
- expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
- new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6)
- );
- expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(10000.0);
- expect(frameStateAtmosphere.mieCoefficient).toEqual(
- new Cartesian3(21e-6, 21e-6, 21e-6)
- );
- expect(frameStateAtmosphere.mieScaleHeight).toEqual(3200.0);
- expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.9);
- expect(frameStateAtmosphere.dynamicLighting).toEqual(
- DynamicAtmosphereLightingType.OFF
- );
+ // Render and check that scene.atmosphere updated
+ // frameState.atmosphere. For the first frame this should
+ // be the default settings.
+ scene.renderForSpecs();
+ expect(frameStateAtmosphere.hsbShift).toEqual(new Cartesian3());
+ expect(frameStateAtmosphere.lightIntensity).toEqual(10.0);
+ expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
+ new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6)
+ );
+ expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(10000.0);
+ expect(frameStateAtmosphere.mieCoefficient).toEqual(
+ new Cartesian3(21e-6, 21e-6, 21e-6)
+ );
+ expect(frameStateAtmosphere.mieScaleHeight).toEqual(3200.0);
+ expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.9);
+ expect(frameStateAtmosphere.dynamicLighting).toEqual(
+ DynamicAtmosphereLightingType.OFF
+ );
- // Now change the settings, render again and check that
- // the frame state was updated.
- atmosphere.hueShift = 0.5;
- atmosphere.saturationShift = -0.5;
- atmosphere.brightnessShift = 0.25;
- atmosphere.lightIntensity = 5.0;
- atmosphere.rayleighCoefficient = new Cartesian3(1.0, 1.0, 1.0);
- atmosphere.rayleighScaleHeight = 1000;
- atmosphere.mieCoefficient = new Cartesian3(2.0, 2.0, 2.0);
- atmosphere.mieScaleHeight = 100;
- atmosphere.mieAnisotropy = 0.5;
- atmosphere.dynamicLighting = DynamicAtmosphereLightingType.SUNLIGHT;
+ // Now change the settings, render again and check that
+ // the frame state was updated.
+ atmosphere.hueShift = 0.5;
+ atmosphere.saturationShift = -0.5;
+ atmosphere.brightnessShift = 0.25;
+ atmosphere.lightIntensity = 5.0;
+ atmosphere.rayleighCoefficient = new Cartesian3(1.0, 1.0, 1.0);
+ atmosphere.rayleighScaleHeight = 1000;
+ atmosphere.mieCoefficient = new Cartesian3(2.0, 2.0, 2.0);
+ atmosphere.mieScaleHeight = 100;
+ atmosphere.mieAnisotropy = 0.5;
+ atmosphere.dynamicLighting = DynamicAtmosphereLightingType.SUNLIGHT;
- scene.renderForSpecs();
- expect(frameStateAtmosphere.hsbShift).toEqual(
- new Cartesian3(0.5, -0.5, 0.25)
- );
- expect(frameStateAtmosphere.lightIntensity).toEqual(5.0);
- expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
- new Cartesian3(1.0, 1.0, 1.0)
- );
- expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(1000);
- expect(frameStateAtmosphere.mieCoefficient).toEqual(
- new Cartesian3(2.0, 2.0, 2.0)
- );
- expect(frameStateAtmosphere.mieScaleHeight).toEqual(100.0);
- expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.5);
- expect(frameStateAtmosphere.dynamicLighting).toEqual(
- DynamicAtmosphereLightingType.SUNLIGHT
- );
- });
-});
+ scene.renderForSpecs();
+ expect(frameStateAtmosphere.hsbShift).toEqual(
+ new Cartesian3(0.5, -0.5, 0.25)
+ );
+ expect(frameStateAtmosphere.lightIntensity).toEqual(5.0);
+ expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
+ new Cartesian3(1.0, 1.0, 1.0)
+ );
+ expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(1000);
+ expect(frameStateAtmosphere.mieCoefficient).toEqual(
+ new Cartesian3(2.0, 2.0, 2.0)
+ );
+ expect(frameStateAtmosphere.mieScaleHeight).toEqual(100.0);
+ expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.5);
+ expect(frameStateAtmosphere.dynamicLighting).toEqual(
+ DynamicAtmosphereLightingType.SUNLIGHT
+ );
+ });
+ },
+ "WebGL"
+);
diff --git a/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
index 7a72c724e295..2a17311dccc1 100644
--- a/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
+++ b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
@@ -7,107 +7,111 @@ import {
} from "../../../index.js";
import ShaderBuilderTester from "../../../../../Specs/ShaderBuilderTester.js";
-describe("Scene/Model/AtmospherePipelineStage", function () {
- const mockModel = {
- boundingSphere: {
- center: Cartesian3.fromDegrees(0, 0, 0),
- },
- };
-
- function mockFrameState() {
- return {
- camera: {
- // position the camera a little bit east of the model
- // and slightly above
- positionWC: Cartesian3.fromDegrees(0.01, 0, 1),
- },
- fog: {
- density: 2e-4,
+describe(
+ "Scene/Model/AtmospherePipelineStage",
+ function () {
+ const mockModel = {
+ boundingSphere: {
+ center: Cartesian3.fromDegrees(0, 0, 0),
},
};
- }
- function mockRenderResources() {
- return {
- shaderBuilder: new ShaderBuilder(),
- uniformMap: {},
- };
- }
-
- it("configures shader", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
-
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
-
- const shaderBuilder = renderResources.shaderBuilder;
-
- ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, [
- "HAS_ATMOSPHERE",
- "COMPUTE_POSITION_WC_ATMOSPHERE",
- ]);
- ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, [
- "HAS_ATMOSPHERE",
- "COMPUTE_POSITION_WC_ATMOSPHERE",
- ]);
-
- ShaderBuilderTester.expectHasVaryings(shaderBuilder, [
- "vec3 v_atmosphereRayleighColor;",
- "vec3 v_atmosphereMieColor;",
- "float v_atmosphereOpacity;",
- ]);
-
- ShaderBuilderTester.expectVertexLinesEqual(shaderBuilder, [
- _shadersAtmosphereStageVS,
- ]);
- ShaderBuilderTester.expectFragmentLinesEqual(shaderBuilder, [
- _shadersAtmosphereStageFS,
- ]);
-
- ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, []);
- ShaderBuilderTester.expectHasFragmentUniforms(shaderBuilder, [
- "uniform bool u_isInFog;",
- ]);
- });
-
- it("u_isInFog() is false if the camera is at the model center", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
-
- frameState.camera.positionWC = Cartesian3.clone(
- mockModel.boundingSphere.center,
- frameState.camera.positionWC
- );
-
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
-
- const uniformMap = renderResources.uniformMap;
- expect(uniformMap.u_isInFog()).toBe(false);
- });
-
- it("u_isInFog() is false if the camera is in space", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
-
- // For this case, the fact that Fog decreases the density to 0 when
- // the camera is far above the model is what causes u_isInFog to
- // be false.
- frameState.camera.positionWC = Cartesian3.fromDegrees(0.001, 0, 100000);
- frameState.fog.density = 0;
-
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
-
- const uniformMap = renderResources.uniformMap;
- expect(uniformMap.u_isInFog()).toBe(false);
- });
-
- it("u_isInFog() is true when the tile is in fog", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
-
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
-
- const uniformMap = renderResources.uniformMap;
- expect(uniformMap.u_isInFog()).toBe(true);
- });
-});
+ function mockFrameState() {
+ return {
+ camera: {
+ // position the camera a little bit east of the model
+ // and slightly above
+ positionWC: Cartesian3.fromDegrees(0.01, 0, 1),
+ },
+ fog: {
+ density: 2e-4,
+ },
+ };
+ }
+
+ function mockRenderResources() {
+ return {
+ shaderBuilder: new ShaderBuilder(),
+ uniformMap: {},
+ };
+ }
+
+ it("configures shader", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+
+ const shaderBuilder = renderResources.shaderBuilder;
+
+ ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, [
+ "HAS_ATMOSPHERE",
+ "COMPUTE_POSITION_WC_ATMOSPHERE",
+ ]);
+ ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, [
+ "HAS_ATMOSPHERE",
+ "COMPUTE_POSITION_WC_ATMOSPHERE",
+ ]);
+
+ ShaderBuilderTester.expectHasVaryings(shaderBuilder, [
+ "vec3 v_atmosphereRayleighColor;",
+ "vec3 v_atmosphereMieColor;",
+ "float v_atmosphereOpacity;",
+ ]);
+
+ ShaderBuilderTester.expectVertexLinesEqual(shaderBuilder, [
+ _shadersAtmosphereStageVS,
+ ]);
+ ShaderBuilderTester.expectFragmentLinesEqual(shaderBuilder, [
+ _shadersAtmosphereStageFS,
+ ]);
+
+ ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, []);
+ ShaderBuilderTester.expectHasFragmentUniforms(shaderBuilder, [
+ "uniform bool u_isInFog;",
+ ]);
+ });
+
+ it("u_isInFog() is false if the camera is at the model center", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ frameState.camera.positionWC = Cartesian3.clone(
+ mockModel.boundingSphere.center,
+ frameState.camera.positionWC
+ );
+
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+
+ const uniformMap = renderResources.uniformMap;
+ expect(uniformMap.u_isInFog()).toBe(false);
+ });
+
+ it("u_isInFog() is false if the camera is in space", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ // For this case, the fact that Fog decreases the density to 0 when
+ // the camera is far above the model is what causes u_isInFog to
+ // be false.
+ frameState.camera.positionWC = Cartesian3.fromDegrees(0.001, 0, 100000);
+ frameState.fog.density = 0;
+
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+
+ const uniformMap = renderResources.uniformMap;
+ expect(uniformMap.u_isInFog()).toBe(false);
+ });
+
+ it("u_isInFog() is true when the tile is in fog", function () {
+ const renderResources = mockRenderResources();
+ const frameState = mockFrameState();
+
+ AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+
+ const uniformMap = renderResources.uniformMap;
+ expect(uniformMap.u_isInFog()).toBe(true);
+ });
+ },
+ "WebGL"
+);
From 84e77ea38ee8f6b90404b0d0042cc9a923d15c42 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 15:09:08 -0500
Subject: [PATCH 46/55] Reset atmosphere after each render test
---
packages/engine/Specs/Scene/Model/ModelSpec.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js
index 68b68d77d918..3a760805c223 100644
--- a/packages/engine/Specs/Scene/Model/ModelSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSpec.js
@@ -1,4 +1,5 @@
import {
+ Atmosphere,
Axis,
Cartesian2,
Cartesian3,
@@ -4420,6 +4421,7 @@ describe(
const darkDate = JulianDate.fromIso8601("2024-01-11T00:00:00Z");
afterEach(function () {
+ scene.atmosphere = new Atmosphere();
scene.fog = new Fog();
scene.light = new SunLight();
scene.camera.switchToPerspectiveFrustum();
From 6c2af2f4ef8adfa7328295fecf91b76ba643e0e8 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 16:10:42 -0500
Subject: [PATCH 47/55] Add a separate bullet for adding scene.atmosphere
---
CHANGES.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/CHANGES.md b/CHANGES.md
index 4a3f6b0c11e1..e4376dcab8c7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -15,7 +15,8 @@
- Added `Cesium3DTileset.getHeight` to sample height values of the loaded tiles. If using WebGL 1, the `enablePick` option must be set to true to use this function. [#11581](https://github.com/CesiumGS/cesium/pull/11581)
- Added `Cesium3DTileset.disableCollision` to allow the camera from to go inside or below a 3D tileset, for instance, to be used with 3D Tiles interiors. [#11581](https://github.com/CesiumGS/cesium/pull/11581)
- The `Cesium3DTileset.dynamicScreenSpaceError` optimization is now enabled by default, as this improves performance for street-level horizon views. Furthermore, the default settings of this feature were tuned for improved performance. `Cesium3DTileset.dynamicScreenSpaceErrorDensity` was changed from 0.00278 to 0.0002. `Cesium3DTileset.dynamicScreenSpaceErrorFactor` was changed from 4 to 24. [#11718](https://github.com/CesiumGS/cesium/pull/11718)
-- Fog rendering now applies to glTF models and 3D Tiles. This can be configured using `scene.fog` and the new `scene.atmosphere`. [#11744](https://github.com/CesiumGS/cesium/pull/11744)
+- Fog rendering now applies to glTF models and 3D Tiles. This can be configured using `scene.fog` and `scene.atmosphere`. [#11744](https://github.com/CesiumGS/cesium/pull/11744)
+- Added `scene.atmosphere` to store common atmosphere lighting parameters. [#11744](https://github.com/CesiumGS/cesium/pull/11744) and [#11681](https://github.com/CesiumGS/cesium/issues/11681)
##### Fixes :wrench:
From defd31098b104f9ccee8e08021d1d612638ad6bb Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 11 Jan 2024 16:53:55 -0500
Subject: [PATCH 48/55] Don't mock in AtmospherePipelineStageSpec
---
packages/engine/Source/Scene/Fog.js | 1 +
.../Model/AtmospherePipelineStageSpec.js | 95 ++++++++++---------
2 files changed, 50 insertions(+), 46 deletions(-)
diff --git a/packages/engine/Source/Scene/Fog.js b/packages/engine/Source/Scene/Fog.js
index 217ef14afcb2..877d45b44f05 100644
--- a/packages/engine/Source/Scene/Fog.js
+++ b/packages/engine/Source/Scene/Fog.js
@@ -173,6 +173,7 @@ Fog.prototype.update = function (frameState) {
frameState.mode !== SceneMode.SCENE3D
) {
frameState.fog.enabled = false;
+ frameState.fog.density = 0;
return;
}
diff --git a/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
index 2a17311dccc1..753fbcbcb5e8 100644
--- a/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
+++ b/packages/engine/Specs/Scene/Model/AtmospherePipelineStageSpec.js
@@ -3,44 +3,53 @@ import {
_shadersAtmosphereStageVS,
Cartesian3,
AtmospherePipelineStage,
- ShaderBuilder,
+ ModelRenderResources,
+ Transforms,
} from "../../../index.js";
+import createScene from "../../../../../Specs/createScene.js";
import ShaderBuilderTester from "../../../../../Specs/ShaderBuilderTester.js";
+import loadAndZoomToModelAsync from "./loadAndZoomToModelAsync.js";
describe(
"Scene/Model/AtmospherePipelineStage",
function () {
- const mockModel = {
- boundingSphere: {
- center: Cartesian3.fromDegrees(0, 0, 0),
- },
- };
-
- function mockFrameState() {
- return {
- camera: {
- // position the camera a little bit east of the model
- // and slightly above
- positionWC: Cartesian3.fromDegrees(0.01, 0, 1),
+ const boxTexturedGlbUrl =
+ "./Data/Models/glTF-2.0/BoxTextured/glTF-Binary/BoxTextured.glb";
+
+ let scene;
+ let model;
+ beforeAll(async function () {
+ scene = createScene();
+
+ const center = Cartesian3.fromDegrees(0, 0, 0);
+ model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGlbUrl,
+ modelMatrix: Transforms.eastNorthUpToFixedFrame(center),
},
- fog: {
- density: 2e-4,
- },
- };
- }
+ scene
+ );
+ });
- function mockRenderResources() {
- return {
- shaderBuilder: new ShaderBuilder(),
- uniformMap: {},
- };
- }
+ let renderResources;
+ beforeEach(async function () {
+ renderResources = new ModelRenderResources(model);
- it("configures shader", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
+ // position the camera a little bit east of the model
+ // and slightly above it.
+ scene.frameState.camera.position = Cartesian3.fromDegrees(0.01, 0, 1000);
+ scene.frameState.camera.direction = new Cartesian3(0, -1, 0);
+
+ // Reset the fog density
+ scene.fog.density = 2e-4;
+ });
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+ afterAll(async function () {
+ scene.destroyForSpecs();
+ });
+
+ it("configures shader", function () {
+ AtmospherePipelineStage.process(renderResources, model, scene.frameState);
const shaderBuilder = renderResources.shaderBuilder;
@@ -73,41 +82,35 @@ describe(
});
it("u_isInFog() is false if the camera is at the model center", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
-
- frameState.camera.positionWC = Cartesian3.clone(
- mockModel.boundingSphere.center,
- frameState.camera.positionWC
+ const frameState = scene.frameState;
+ frameState.camera.position = Cartesian3.clone(
+ model.boundingSphere.center,
+ frameState.camera.position
);
+ scene.renderForSpecs();
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+ AtmospherePipelineStage.process(renderResources, model, frameState);
const uniformMap = renderResources.uniformMap;
expect(uniformMap.u_isInFog()).toBe(false);
});
it("u_isInFog() is false if the camera is in space", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
+ const frameState = scene.frameState;
- // For this case, the fact that Fog decreases the density to 0 when
- // the camera is far above the model is what causes u_isInFog to
- // be false.
- frameState.camera.positionWC = Cartesian3.fromDegrees(0.001, 0, 100000);
- frameState.fog.density = 0;
+ frameState.camera.position = Cartesian3.fromDegrees(0.01, 0, 900000);
+ scene.renderForSpecs();
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+ AtmospherePipelineStage.process(renderResources, model, frameState);
const uniformMap = renderResources.uniformMap;
expect(uniformMap.u_isInFog()).toBe(false);
});
it("u_isInFog() is true when the tile is in fog", function () {
- const renderResources = mockRenderResources();
- const frameState = mockFrameState();
+ scene.renderForSpecs();
- AtmospherePipelineStage.process(renderResources, mockModel, frameState);
+ AtmospherePipelineStage.process(renderResources, model, scene.frameState);
const uniformMap = renderResources.uniformMap;
expect(uniformMap.u_isInFog()).toBe(true);
From dca2dcfa10865c49f3d0e4d8566040ccc5db53e8 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Tue, 16 Jan 2024 10:06:09 -0500
Subject: [PATCH 49/55] address PR feedback
---
packages/engine/Source/Scene/Atmosphere.js | 11 ++++-------
.../Source/Scene/DynamicAtmosphereLightingType.js | 4 ++--
packages/engine/Source/Scene/Scene.js | 5 +----
.../Functions/getDynamicAtmosphereLightDirection.glsl | 4 ++--
.../Source/Shaders/Model/AtmosphereStageFS.glsl | 4 ++--
packages/engine/Specs/Scene/AtmosphereSpec.js | 2 +-
.../Specs/Scene/DynamicAtmosphereLightingTypeSpec.js | 6 +++---
packages/engine/Specs/Scene/Model/ModelSpec.js | 4 ++--
packages/engine/Specs/Scene/SkyAtmosphereSpec.js | 2 +-
9 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index cbdbecfc65ba..399cff147ecb 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -2,15 +2,12 @@ import Cartesian3 from "../Core/Cartesian3.js";
import DynamicAtmosphereLightingType from "./DynamicAtmosphereLightingType.js";
/**
- * Common atmosphere settings used by sky atmosphere, ground atmosphere, and fog.
+ * Common atmosphere settings used by 3D Tiles and models for rendering sky atmosphere, ground atmosphere, and fog.
*
*
* This class is not to be confused with {@link SkyAtmosphere}, which is responsible for rendering the sky.
*
*
- * Currently, these settings only apply to 3D Tiles and models, but will eventually affect the sky atmosphere and globe. See {@link https://github.com/CesiumGS/cesium/issues/11681|issue #11681}.
- *
- *
* While the atmosphere settings affect the color of fog, see {@link Fog} to control how fog is rendered.
*
*
@@ -118,13 +115,13 @@ function Atmosphere() {
this.brightnessShift = 0.0;
/**
- * When not DynamicAtmosphereLightingType.OFF, the selected light source will
+ * When not DynamicAtmosphereLightingType.NONE, the selected light source will
* be used for dynamically lighting all atmosphere-related rendering effects.
*
* @type {DynamicAtmosphereLightingType}
- * @default DynamicAtmosphereLightingType.OFF
+ * @default DynamicAtmosphereLightingType.NONE
*/
- this.dynamicLighting = DynamicAtmosphereLightingType.OFF;
+ this.dynamicLighting = DynamicAtmosphereLightingType.NONE;
}
Atmosphere.prototype.update = function (frameState) {
diff --git a/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
index a018222add16..d1d9967a730e 100644
--- a/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
+++ b/packages/engine/Source/Scene/DynamicAtmosphereLightingType.js
@@ -13,7 +13,7 @@ const DynamicAtmosphereLightingType = {
* @type {number}
* @constant
*/
- OFF: 0,
+ NONE: 0,
/**
* Use the scene's current light source for dynamic atmosphere lighting.
*
@@ -42,7 +42,7 @@ const DynamicAtmosphereLightingType = {
DynamicAtmosphereLightingType.fromGlobeFlags = function (globe) {
const lightingOn = globe.enableLighting && globe.dynamicAtmosphereLighting;
if (!lightingOn) {
- return DynamicAtmosphereLightingType.OFF;
+ return DynamicAtmosphereLightingType.NONE;
}
// Force sunlight
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index d481993bf70f..03f46e1ccbd2 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -515,11 +515,8 @@ function Scene(options) {
this.cameraEventWaitTime = 500.0;
/**
- * Settings for atmosphere lighting effects. This is not to be confused with
+ * Settings for atmosphere lighting effects affecting 3D Tiles and model rendering. This is not to be confused with
* {@link Scene#skyAtmosphere} which is responsible for rendering the sky.
- *
- * Currently these settings only apply to 3D Tiles and models. In the future this will apply to the globe as well, see {@link https://github.com/CesiumGS/cesium/issues/11681|issue #11681}.
- *
*
* @type {Atmosphere}
*/
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl b/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
index 9b934362b090..70063302734f 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/getDynamicAtmosphereLightDirection.glsl
@@ -10,12 +10,12 @@
* @return {vec3} The normalized light direction vector. Depending on the enum value, it is either positionWC, czm_lightDirectionWC or czm_sunDirectionWC
*/
vec3 czm_getDynamicAtmosphereLightDirection(vec3 positionWC, float lightEnum) {
- const float OFF = 0.0;
+ const float NONE = 0.0;
const float SCENE_LIGHT = 1.0;
const float SUNLIGHT = 2.0;
vec3 lightDirection =
- positionWC * float(lightEnum == OFF) +
+ positionWC * float(lightEnum == NONE) +
czm_lightDirectionWC * float(lightEnum == SCENE_LIGHT) +
czm_sunDirectionWC * float(lightEnum == SUNLIGHT);
return normalize(lightDirection);
diff --git a/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
index 1b91e3f87c5f..7b2dc693415b 100644
--- a/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
@@ -44,8 +44,8 @@ void applyFog(inout vec4 color, vec4 groundAtmosphereColor, vec3 lightDirection,
vec3 fogColor = groundAtmosphereColor.rgb;
// If there is dynamic lighting, apply that to the fog.
- const float OFF = 0.0;
- if (czm_atmosphereDynamicLighting != OFF) {
+ const float NONE = 0.0;
+ if (czm_atmosphereDynamicLighting != NONE) {
float darken = clamp(dot(normalize(czm_viewerPositionWC), lightDirection), czm_fogMinimumBrightness, 1.0);
fogColor *= darken;
}
diff --git a/packages/engine/Specs/Scene/AtmosphereSpec.js b/packages/engine/Specs/Scene/AtmosphereSpec.js
index d23a1db0f4f5..a3ac1165e296 100644
--- a/packages/engine/Specs/Scene/AtmosphereSpec.js
+++ b/packages/engine/Specs/Scene/AtmosphereSpec.js
@@ -34,7 +34,7 @@ describe(
expect(frameStateAtmosphere.mieScaleHeight).toEqual(3200.0);
expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.9);
expect(frameStateAtmosphere.dynamicLighting).toEqual(
- DynamicAtmosphereLightingType.OFF
+ DynamicAtmosphereLightingType.NONE
);
// Now change the settings, render again and check that
diff --git a/packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js b/packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js
index e2fcc864ddd6..71c1802eeca8 100644
--- a/packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js
+++ b/packages/engine/Specs/Scene/DynamicAtmosphereLightingTypeSpec.js
@@ -13,19 +13,19 @@ describe("Scene/DynamicAtmosphereLightingType", function () {
const globe = mockGlobe();
expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
- DynamicAtmosphereLightingType.OFF
+ DynamicAtmosphereLightingType.NONE
);
globe.enableLighting = true;
expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
- DynamicAtmosphereLightingType.OFF
+ DynamicAtmosphereLightingType.NONE
);
globe.enableLighting = false;
globe.dynamicAtmosphereLighting = true;
expect(DynamicAtmosphereLightingType.fromGlobeFlags(globe)).toBe(
- DynamicAtmosphereLightingType.OFF
+ DynamicAtmosphereLightingType.NONE
);
});
diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js
index 3a760805c223..bec39b42978a 100644
--- a/packages/engine/Specs/Scene/Model/ModelSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSpec.js
@@ -4514,7 +4514,7 @@ describe(
scene.camera.frustum.width = 1;
// Grab the color when dynamic lighting is off for comparison
- scene.atmosphere.dynamicLighting = DynamicAtmosphereLightingType.OFF;
+ scene.atmosphere.dynamicLighting = DynamicAtmosphereLightingType.NONE;
const renderOptions = {
scene,
time: sunnyDate,
@@ -4576,7 +4576,7 @@ describe(
viewFog(scene, model);
// Grab the color when dynamic lighting is off for comparison
- scene.atmosphere.dynamicLighting = DynamicAtmosphereLightingType.OFF;
+ scene.atmosphere.dynamicLighting = DynamicAtmosphereLightingType.NONE;
const renderOptions = {
scene,
time: sunnyDate,
diff --git a/packages/engine/Specs/Scene/SkyAtmosphereSpec.js b/packages/engine/Specs/Scene/SkyAtmosphereSpec.js
index f3d62d2f4546..b7be045cfd39 100644
--- a/packages/engine/Specs/Scene/SkyAtmosphereSpec.js
+++ b/packages/engine/Specs/Scene/SkyAtmosphereSpec.js
@@ -85,7 +85,7 @@ describe(
it("draws sky with dynamic lighting off", function () {
const s = new SkyAtmosphere();
- s.setDynamicLighting(DynamicAtmosphereLightingType.OFF);
+ s.setDynamicLighting(DynamicAtmosphereLightingType.NONE);
expect(scene).toRender([0, 0, 0, 255]);
scene.render();
From 7adf61de2e96f0297a11727e314e3dab9478cfc7 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 18 Jan 2024 10:30:22 -0500
Subject: [PATCH 50/55] add ignoreBlackPixels option to czm_applyHSBShift
---
.../Builtin/Functions/applyHSBShift.glsl | 15 +++++++++++++--
packages/engine/Source/Shaders/GlobeFS.glsl | 19 +++----------------
.../Shaders/Model/AtmosphereStageFS.glsl | 5 +++--
3 files changed, 19 insertions(+), 20 deletions(-)
diff --git a/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl b/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
index 30b9424eb4ea..55ee1592a8d6 100644
--- a/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
+++ b/packages/engine/Source/Shaders/Builtin/Functions/applyHSBShift.glsl
@@ -3,10 +3,11 @@
*
* @param {vec3} rgb The color in RGB space.
* @param {vec3} hsbShift The amount to shift each component. The xyz components correspond to hue, saturation, and brightness. Shifting the hue by +/- 1.0 corresponds to shifting the hue by a full cycle. Saturation and brightness are clamped between 0 and 1 after the adjustment
+ * @param {bool} ignoreBlackPixels If true, black pixels will be unchanged. This is necessary in some shaders such as atmosphere-related effects.
*
* @return {vec3} The RGB color after shifting in HSB space and clamping saturation and brightness to a valid range.
*/
-vec3 czm_applyHSBShift(vec3 rgb, vec3 hsbShift) {
+vec3 czm_applyHSBShift(vec3 rgb, vec3 hsbShift, bool ignoreBlackPixels) {
// Convert rgb color to hsb
vec3 hsb = czm_RGBToHSB(rgb);
@@ -14,7 +15,17 @@ vec3 czm_applyHSBShift(vec3 rgb, vec3 hsbShift) {
// Hue cycles around so no clamp is needed.
hsb.x += hsbShift.x; // hue
hsb.y = clamp(hsb.y + hsbShift.y, 0.0, 1.0); // saturation
- hsb.z = clamp(hsb.z + hsbShift.z, 0.0, 1.0); // brightness
+
+ // brightness
+ //
+ // Some shaders such as atmosphere-related effects need to leave black
+ // pixels unchanged
+ if (ignoreBlackPixels) {
+ hsb.z = hsb.z > czm_epsilon7 ? hsb.z + hsbShift.z : 0.0;
+ } else {
+ hsb.z = hsb.z + hsbShift.z;
+ }
+ hsb.z = clamp(hsb.z, 0.0, 1.0);
// Convert shifted hsb back to rgb
return czm_HSBToRGB(hsb);
diff --git a/packages/engine/Source/Shaders/GlobeFS.glsl b/packages/engine/Source/Shaders/GlobeFS.glsl
index a2bf6b48811a..74c568efb406 100644
--- a/packages/engine/Source/Shaders/GlobeFS.glsl
+++ b/packages/engine/Source/Shaders/GlobeFS.glsl
@@ -268,20 +268,6 @@ vec4 sampleAndBlend(
return vec4(outColor, max(outAlpha, 0.0));
}
-vec3 colorCorrect(vec3 rgb) {
-#ifdef COLOR_CORRECT
- // Convert rgb color to hsb
- vec3 hsb = czm_RGBToHSB(rgb);
- // Perform hsb shift
- hsb.x += u_hsbShift.x; // hue
- hsb.y = clamp(hsb.y + u_hsbShift.y, 0.0, 1.0); // saturation
- hsb.z = hsb.z > czm_epsilon7 ? hsb.z + u_hsbShift.z : 0.0; // brightness
- // Convert shifted hsb back to rgb
- rgb = czm_HSBToRGB(hsb);
-#endif
- return rgb;
-}
-
vec4 computeDayColor(vec4 initialColor, vec3 textureCoordinates, float nightBlend);
vec4 computeWaterColor(vec3 positionEyeCoordinates, vec2 textureCoordinates, mat3 enuToEye, vec4 imageryColor, float specularMapValue, float fade);
@@ -473,8 +459,9 @@ void main()
#endif
#ifdef COLOR_CORRECT
- rayleighColor = czm_applyHSBShift(rayleighColor, u_hsbShift);
- mieColor = czm_applyHSBShift(mieColor, u_hsbShift);
+ const bool ignoreBlackPixels = true;
+ rayleighColor = czm_applyHSBShift(rayleighColor, u_hsbShift, ignoreBlackPixels);
+ mieColor = czm_applyHSBShift(mieColor, u_hsbShift, ignoreBlackPixels);
#endif
vec4 groundAtmosphereColor = computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
diff --git a/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
index 7b2dc693415b..3a0b20e86fa0 100644
--- a/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
+++ b/packages/engine/Source/Shaders/Model/AtmosphereStageFS.glsl
@@ -96,8 +96,9 @@ void atmosphereStage(inout vec4 color, in ProcessedAttributes attributes) {
}
//color correct rayleigh and mie colors
- rayleighColor = czm_applyHSBShift(rayleighColor, czm_atmosphereHsbShift);
- mieColor = czm_applyHSBShift(mieColor, czm_atmosphereHsbShift);
+ const bool ignoreBlackPixels = true;
+ rayleighColor = czm_applyHSBShift(rayleighColor, czm_atmosphereHsbShift, ignoreBlackPixels);
+ mieColor = czm_applyHSBShift(mieColor, czm_atmosphereHsbShift, ignoreBlackPixels);
vec4 groundAtmosphereColor = czm_computeAtmosphereColor(positionWC, lightDirection, rayleighColor, mieColor, opacity);
From 6cefbe174af31db1a2cf23ea22904c5205749584 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 18 Jan 2024 10:43:34 -0500
Subject: [PATCH 51/55] use czm_tanh()
---
packages/engine/Source/Shaders/AtmosphereCommon.glsl | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/packages/engine/Source/Shaders/AtmosphereCommon.glsl b/packages/engine/Source/Shaders/AtmosphereCommon.glsl
index e0e23eab78cb..d68274fa5381 100644
--- a/packages/engine/Source/Shaders/AtmosphereCommon.glsl
+++ b/packages/engine/Source/Shaders/AtmosphereCommon.glsl
@@ -11,14 +11,6 @@ const float ATMOSPHERE_THICKNESS = 111e3; // The thickness of the atmosphere in
const int PRIMARY_STEPS_MAX = 16; // Maximum number of times the ray from the camera to the world position (primary ray) is sampled.
const int LIGHT_STEPS_MAX = 4; // Maximum number of times the light is sampled from the light source's intersection with the atmosphere to a sample position on the primary ray.
-/**
- * Rational approximation to tanh(x)
-*/
-float approximateTanh(float x) {
- float x2 = x * x;
- return max(-1.0, min(+1.0, x * (27.0 + x2) / (27.0 + 9.0 * x2)));
-}
-
/**
* This function computes the colors contributed by Rayliegh and Mie scattering on a given ray, as well as
* the transmittance value for the ray.
@@ -65,7 +57,7 @@ void computeScattering(
float x = 1e-7 * primaryRayAtmosphereIntersect.stop / length(primaryRayLength);
// Value close to 0.0: close to the horizon
// Value close to 1.0: above in the sky
- float w_stop_gt_lprl = 0.5 * (1.0 + approximateTanh(x));
+ float w_stop_gt_lprl = 0.5 * (1.0 + czm_approximateTanh(x));
// The ray should start from the first intersection with the outer atmopshere, or from the camera position, if it is inside the atmosphere.
float start_0 = primaryRayAtmosphereIntersect.start;
@@ -77,7 +69,7 @@ void computeScattering(
// (1) from outer space we have to use more ray steps to get a realistic rendering
// (2) within atmosphere we need fewer steps for faster rendering
float x_o_a = start_0 - ATMOSPHERE_THICKNESS; // ATMOSPHERE_THICKNESS used as an ad-hoc constant, no precise meaning here, only the order of magnitude matters
- float w_inside_atmosphere = 1.0 - 0.5 * (1.0 + approximateTanh(x_o_a));
+ float w_inside_atmosphere = 1.0 - 0.5 * (1.0 + czm_approximateTanh(x_o_a));
int PRIMARY_STEPS = PRIMARY_STEPS_MAX - int(w_inside_atmosphere * 12.0); // Number of times the ray from the camera to the world position (primary ray) is sampled.
int LIGHT_STEPS = LIGHT_STEPS_MAX - int(w_inside_atmosphere * 2.0); // Number of times the light is sampled from the light source's intersection with the atmosphere to a sample position on the primary ray.
From ac17f63da8231f23e7bf96929d8707c1c632c825 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 18 Jan 2024 12:26:24 -0500
Subject: [PATCH 52/55] Store the Atmosphere in the FrameState directly
---
.../engine/Source/Renderer/UniformState.js | 6 ++--
packages/engine/Source/Scene/Atmosphere.js | 21 -------------
packages/engine/Source/Scene/FrameState.js | 30 +++----------------
packages/engine/Source/Scene/Scene.js | 2 +-
4 files changed, 9 insertions(+), 50 deletions(-)
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index 7c0c56924c62..12e71c5b1d8b 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -1518,8 +1518,10 @@ UniformState.prototype.update = function (frameState) {
const atmosphere = frameState.atmosphere;
- this._atmosphereHsbShift = Cartesian3.clone(
- atmosphere.hsbShift,
+ this._atmosphereHsbShift = Cartesian3.fromElements(
+ atmosphere.hueShift,
+ atmosphere.saturationShift,
+ atmosphere.brightnessShift,
this._atmosphereHsbShift
);
this._atmosphereLightIntensity = atmosphere.lightIntensity;
diff --git a/packages/engine/Source/Scene/Atmosphere.js b/packages/engine/Source/Scene/Atmosphere.js
index 399cff147ecb..296ad0f19d25 100644
--- a/packages/engine/Source/Scene/Atmosphere.js
+++ b/packages/engine/Source/Scene/Atmosphere.js
@@ -124,25 +124,4 @@ function Atmosphere() {
this.dynamicLighting = DynamicAtmosphereLightingType.NONE;
}
-Atmosphere.prototype.update = function (frameState) {
- const atmosphere = frameState.atmosphere;
- atmosphere.hsbShift.x = this.hueShift;
- atmosphere.hsbShift.y = this.saturationShift;
- atmosphere.hsbShift.z = this.brightnessShift;
- atmosphere.lightIntensity = this.lightIntensity;
- atmosphere.rayleighCoefficient = Cartesian3.clone(
- this.rayleighCoefficient,
- atmosphere.rayleighCoefficient
- );
- atmosphere.rayleighScaleHeight = this.rayleighScaleHeight;
- atmosphere.mieCoefficient = Cartesian3.clone(
- this.mieCoefficient,
- atmosphere.mieCoefficient
- );
- atmosphere.mieScaleHeight = this.mieScaleHeight;
- atmosphere.mieAnisotropy = this.mieAnisotropy;
-
- atmosphere.dynamicLighting = this.dynamicLighting;
-};
-
export default Atmosphere;
diff --git a/packages/engine/Source/Scene/FrameState.js b/packages/engine/Source/Scene/FrameState.js
index 0ad600f948ba..adfffba7958a 100644
--- a/packages/engine/Source/Scene/FrameState.js
+++ b/packages/engine/Source/Scene/FrameState.js
@@ -1,5 +1,4 @@
import SceneMode from "./SceneMode.js";
-import Cartesian3 from "../Core/Cartesian3.js";
/**
* State information about the current frame. An instance of this class
@@ -278,31 +277,10 @@ function FrameState(context, creditDisplay, jobScheduler) {
};
/**
- * @typedef FrameState.Atmosphere
- * @type {object}
- * @property {Cartesian3} hsbShift A color shift to apply to the atmosphere color in HSB.
- * @property {number} lightIntensity The intensity of the light that is used for computing the atmosphere color
- * @property {Cartesian3} rayleighCoefficient The Rayleigh scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
- * @property {number} rayleighScaleHeight The Rayleigh scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
- * @property {Cartesian3} mieCoefficient The Mie scattering coefficient used in the atmospheric scattering equations for the sky atmosphere.
- * @property {number} mieScaleHeight The Mie scale height used in the atmospheric scattering equations for the sky atmosphere, in meters.
- * @property {number} mieAnisotropy The anisotropy of the medium to consider for Mie scattering.
- * @property {DynamicAtmosphereLightingType} dynamicLighting An enum value determining what light source to use for dynamic lighting the atmosphere (if enabled)
- */
-
- /**
- * @type {FrameState.Atmosphere}
- */
- this.atmosphere = {
- hsbShift: new Cartesian3(),
- lightIntensity: undefined,
- rayleighCoefficient: new Cartesian3(),
- rayleighScaleHeight: undefined,
- mieCoefficient: new Cartesian3(),
- mieScaleHeight: undefined,
- mieAnisotropy: undefined,
- dynamicLighting: undefined,
- };
+ * The current Atmosphere
+ * @type {Atmosphere}
+ */
+ this.atmosphere = undefined;
/**
* A scalar used to vertically exaggerate the scene
diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js
index 03f46e1ccbd2..1d56d15d9333 100644
--- a/packages/engine/Source/Scene/Scene.js
+++ b/packages/engine/Source/Scene/Scene.js
@@ -3770,7 +3770,7 @@ function render(scene) {
}
frameState.backgroundColor = backgroundColor;
- scene.atmosphere.update(frameState);
+ frameState.atmosphere = scene.atmosphere;
scene.fog.update(frameState);
us.update(frameState);
From bca110fba369ff9c9ee322081d90606729eb019a Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 18 Jan 2024 13:55:15 -0500
Subject: [PATCH 53/55] Fix automatic uniform specs
---
Specs/createFrameState.js | 3 ++
.../engine/Source/Renderer/UniformState.js | 41 ++++++++++---------
.../Specs/Renderer/AutomaticUniformSpec.js | 25 ++++-------
3 files changed, 32 insertions(+), 37 deletions(-)
diff --git a/Specs/createFrameState.js b/Specs/createFrameState.js
index 672a2376fe58..8f4ba1c1f6b2 100644
--- a/Specs/createFrameState.js
+++ b/Specs/createFrameState.js
@@ -1,4 +1,5 @@
import {
+ Atmosphere,
defaultValue,
GeographicProjection,
JulianDate,
@@ -51,6 +52,8 @@ function createFrameState(context, camera, frameNumber, time) {
frameState.minimumDisableDepthTestDistance = 0.0;
+ frameState.atmosphere = new Atmosphere();
+
return frameState;
}
export default createFrameState;
diff --git a/packages/engine/Source/Renderer/UniformState.js b/packages/engine/Source/Renderer/UniformState.js
index 12e71c5b1d8b..cc5030e6f5d6 100644
--- a/packages/engine/Source/Renderer/UniformState.js
+++ b/packages/engine/Source/Renderer/UniformState.js
@@ -1517,26 +1517,27 @@ UniformState.prototype.update = function (frameState) {
this._fogMinimumBrightness = frameState.fog.minimumBrightness;
const atmosphere = frameState.atmosphere;
-
- this._atmosphereHsbShift = Cartesian3.fromElements(
- atmosphere.hueShift,
- atmosphere.saturationShift,
- atmosphere.brightnessShift,
- this._atmosphereHsbShift
- );
- this._atmosphereLightIntensity = atmosphere.lightIntensity;
- this._atmosphereRayleighCoefficient = Cartesian3.clone(
- atmosphere.rayleighCoefficient,
- this._atmosphereRayleighCoefficient
- );
- this._atmosphereRayleighScaleHeight = atmosphere.rayleighScaleHeight;
- this._atmosphereMieCoefficient = Cartesian3.clone(
- atmosphere.mieCoefficient,
- this._atmosphereMieCoefficient
- );
- this._atmosphereMieScaleHeight = atmosphere.mieScaleHeight;
- this._atmosphereMieAnisotropy = atmosphere.mieAnisotropy;
- this._atmosphereDynamicLighting = atmosphere.dynamicLighting;
+ if (defined(atmosphere)) {
+ this._atmosphereHsbShift = Cartesian3.fromElements(
+ atmosphere.hueShift,
+ atmosphere.saturationShift,
+ atmosphere.brightnessShift,
+ this._atmosphereHsbShift
+ );
+ this._atmosphereLightIntensity = atmosphere.lightIntensity;
+ this._atmosphereRayleighCoefficient = Cartesian3.clone(
+ atmosphere.rayleighCoefficient,
+ this._atmosphereRayleighCoefficient
+ );
+ this._atmosphereRayleighScaleHeight = atmosphere.rayleighScaleHeight;
+ this._atmosphereMieCoefficient = Cartesian3.clone(
+ atmosphere.mieCoefficient,
+ this._atmosphereMieCoefficient
+ );
+ this._atmosphereMieScaleHeight = atmosphere.mieScaleHeight;
+ this._atmosphereMieAnisotropy = atmosphere.mieAnisotropy;
+ this._atmosphereDynamicLighting = atmosphere.dynamicLighting;
+ }
this._invertClassificationColor = frameState.invertClassificationColor;
diff --git a/packages/engine/Specs/Renderer/AutomaticUniformSpec.js b/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
index c93eac8870d3..f6381a39f9ef 100644
--- a/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
+++ b/packages/engine/Specs/Renderer/AutomaticUniformSpec.js
@@ -1,5 +1,4 @@
import {
- Atmosphere,
Cartesian2,
Cartesian3,
Cartographic,
@@ -1848,11 +1847,10 @@ describe(
it("has czm_atmosphereHsbShift", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
atmosphere.hueShift = 1.0;
atmosphere.saturationShift = 2.0;
atmosphere.brightnessShift = 3.0;
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
@@ -1869,9 +1867,8 @@ describe(
it("has czm_atmosphereLightIntensity", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
atmosphere.lightIntensity = 2.0;
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
@@ -1888,9 +1885,8 @@ describe(
it("has czm_atmosphereRayleighCoefficient", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
atmosphere.rayleighCoefficient = new Cartesian3(1.0, 2.0, 3.0);
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
@@ -1907,9 +1903,8 @@ describe(
it("has czm_atmosphereRayleighScaleHeight", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
atmosphere.rayleighScaleHeight = 100.0;
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
@@ -1926,9 +1921,8 @@ describe(
it("has czm_atmosphereMieCoefficient", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
atmosphere.mieCoefficient = new Cartesian3(1.0, 2.0, 3.0);
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
@@ -1945,9 +1939,8 @@ describe(
it("has czm_atmosphereMieScaleHeight", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
atmosphere.mieScaleHeight = 100.0;
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
@@ -1964,9 +1957,8 @@ describe(
it("has czm_atmosphereMieAnisotropy", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
atmosphere.mieAnisotropy = 100.0;
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
@@ -1983,10 +1975,9 @@ describe(
it("has czm_atmosphereDynamicLighting", function () {
const frameState = createFrameState(context, createMockCamera());
- const atmosphere = new Atmosphere();
+ const atmosphere = frameState.atmosphere;
const enumValue = DynamicAtmosphereLightingType.SCENE_LIGHT;
atmosphere.dynamicLighting = enumValue;
- atmosphere.update(frameState);
const us = context.uniformState;
us.update(frameState);
From 3a25627e350d41abd5dccb4c4c7039ce86bc2316 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 18 Jan 2024 13:56:20 -0500
Subject: [PATCH 54/55] Fix sky atmosphere
---
packages/engine/Source/Shaders/SkyAtmosphereFS.glsl | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
index 639730bba374..154964b0d643 100644
--- a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
+++ b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
@@ -43,7 +43,8 @@ void main (void)
#endif
#ifdef COLOR_CORRECT
- color.rgb = czm_applyHSBShift(color.rgb, u_hsbShift);
+ const float ignoreBlackPixels = true;
+ color.rgb = czm_applyHSBShift(color.rgb, u_hsbShift, ignoreBlackPixels);
#endif
// For the parts of the sky atmosphere that are not behind a translucent globe,
From ab972808d700b8f0bef66a6ecff4da479d8590e8 Mon Sep 17 00:00:00 2001
From: Peter Gagliardi
Date: Thu, 18 Jan 2024 14:30:36 -0500
Subject: [PATCH 55/55] Update unit tests
---
.../Source/Shaders/SkyAtmosphereFS.glsl | 2 +-
packages/engine/Specs/Scene/AtmosphereSpec.js | 73 -------------------
packages/engine/Specs/Scene/SceneSpec.js | 23 +++++-
3 files changed, 23 insertions(+), 75 deletions(-)
delete mode 100644 packages/engine/Specs/Scene/AtmosphereSpec.js
diff --git a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
index 154964b0d643..d8f213396452 100644
--- a/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
+++ b/packages/engine/Source/Shaders/SkyAtmosphereFS.glsl
@@ -43,7 +43,7 @@ void main (void)
#endif
#ifdef COLOR_CORRECT
- const float ignoreBlackPixels = true;
+ const bool ignoreBlackPixels = true;
color.rgb = czm_applyHSBShift(color.rgb, u_hsbShift, ignoreBlackPixels);
#endif
diff --git a/packages/engine/Specs/Scene/AtmosphereSpec.js b/packages/engine/Specs/Scene/AtmosphereSpec.js
deleted file mode 100644
index a3ac1165e296..000000000000
--- a/packages/engine/Specs/Scene/AtmosphereSpec.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Cartesian3, DynamicAtmosphereLightingType } from "../../index.js";
-
-import createScene from "../../../../Specs/createScene";
-
-describe(
- "scene/Atmosphere",
- function () {
- let scene;
- beforeEach(function () {
- scene = createScene();
- });
-
- afterEach(function () {
- scene.destroyForSpecs();
- });
-
- it("updates frameState each frame", function () {
- const atmosphere = scene.atmosphere;
- const frameStateAtmosphere = scene.frameState.atmosphere;
-
- // Render and check that scene.atmosphere updated
- // frameState.atmosphere. For the first frame this should
- // be the default settings.
- scene.renderForSpecs();
- expect(frameStateAtmosphere.hsbShift).toEqual(new Cartesian3());
- expect(frameStateAtmosphere.lightIntensity).toEqual(10.0);
- expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
- new Cartesian3(5.5e-6, 13.0e-6, 28.4e-6)
- );
- expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(10000.0);
- expect(frameStateAtmosphere.mieCoefficient).toEqual(
- new Cartesian3(21e-6, 21e-6, 21e-6)
- );
- expect(frameStateAtmosphere.mieScaleHeight).toEqual(3200.0);
- expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.9);
- expect(frameStateAtmosphere.dynamicLighting).toEqual(
- DynamicAtmosphereLightingType.NONE
- );
-
- // Now change the settings, render again and check that
- // the frame state was updated.
- atmosphere.hueShift = 0.5;
- atmosphere.saturationShift = -0.5;
- atmosphere.brightnessShift = 0.25;
- atmosphere.lightIntensity = 5.0;
- atmosphere.rayleighCoefficient = new Cartesian3(1.0, 1.0, 1.0);
- atmosphere.rayleighScaleHeight = 1000;
- atmosphere.mieCoefficient = new Cartesian3(2.0, 2.0, 2.0);
- atmosphere.mieScaleHeight = 100;
- atmosphere.mieAnisotropy = 0.5;
- atmosphere.dynamicLighting = DynamicAtmosphereLightingType.SUNLIGHT;
-
- scene.renderForSpecs();
- expect(frameStateAtmosphere.hsbShift).toEqual(
- new Cartesian3(0.5, -0.5, 0.25)
- );
- expect(frameStateAtmosphere.lightIntensity).toEqual(5.0);
- expect(frameStateAtmosphere.rayleighCoefficient).toEqual(
- new Cartesian3(1.0, 1.0, 1.0)
- );
- expect(frameStateAtmosphere.rayleighScaleHeight).toEqual(1000);
- expect(frameStateAtmosphere.mieCoefficient).toEqual(
- new Cartesian3(2.0, 2.0, 2.0)
- );
- expect(frameStateAtmosphere.mieScaleHeight).toEqual(100.0);
- expect(frameStateAtmosphere.mieAnisotropy).toEqual(0.5);
- expect(frameStateAtmosphere.dynamicLighting).toEqual(
- DynamicAtmosphereLightingType.SUNLIGHT
- );
- });
- },
- "WebGL"
-);
diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js
index 74a8a66a7efb..55ebadab6fe0 100644
--- a/packages/engine/Specs/Scene/SceneSpec.js
+++ b/packages/engine/Specs/Scene/SceneSpec.js
@@ -1,4 +1,5 @@
import {
+ Atmosphere,
BoundingSphere,
Cartesian2,
Cartesian3,
@@ -577,7 +578,7 @@ describe(
});
});
- it("renders sky atmopshere without a globe", function () {
+ it("renders sky atmosphere without a globe", function () {
s.globe = new Globe(Ellipsoid.UNIT_SPHERE);
s.globe.show = false;
s.camera.position = new Cartesian3(1.02, 0.0, 0.0);
@@ -2471,6 +2472,26 @@ describe(
scene.destroyForSpecs();
});
});
+
+ it("updates frameState.atmosphere", function () {
+ const scene = createScene();
+ const frameState = scene.frameState;
+
+ // Before the first render, the atmosphere has not yet been set
+ expect(frameState.atmosphere).toBeUndefined();
+
+ // On the first render, the atmosphere settings are propagated to the
+ // frame state
+ const originalAtmosphere = scene.atmosphere;
+ scene.renderForSpecs();
+ expect(frameState.atmosphere).toBe(originalAtmosphere);
+
+ // If we change the atmosphere to a new object
+ const anotherAtmosphere = new Atmosphere();
+ scene.atmosphere = anotherAtmosphere;
+ scene.renderForSpecs();
+ expect(frameState.atmosphere).toBe(anotherAtmosphere);
+ });
},
"WebGL"