From 5d0e0c8bb4192aad9dd33ad061732918e9c4f1c6 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Sun, 17 May 2020 16:10:22 -0400 Subject: [PATCH 01/18] Add per-fragment atmosphere, improve per-vertex atmosphere --- Source/Scene/SkyAtmosphere.js | 135 ++++++------- Source/Shaders/SkyAtmosphereCommon.glsl | 242 ++++++++++++++++++++++++ Source/Shaders/SkyAtmosphereFS.glsl | 61 ++---- Source/Shaders/SkyAtmosphereVS.glsl | 97 +--------- 4 files changed, 324 insertions(+), 211 deletions(-) create mode 100644 Source/Shaders/SkyAtmosphereCommon.glsl diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index d4b96ba67c7a..37a4522df86b 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -1,5 +1,4 @@ import Cartesian3 from "../Core/Cartesian3.js"; -import Cartesian4 from "../Core/Cartesian4.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; @@ -14,6 +13,7 @@ import RenderState from "../Renderer/RenderState.js"; import ShaderProgram from "../Renderer/ShaderProgram.js"; import ShaderSource from "../Renderer/ShaderSource.js"; import VertexArray from "../Renderer/VertexArray.js"; +import SkyAtmosphereCommon from "../Shaders/SkyAtmosphereCommon.js"; import SkyAtmosphereFS from "../Shaders/SkyAtmosphereFS.js"; import SkyAtmosphereVS from "../Shaders/SkyAtmosphereVS.js"; import BlendingState from "./BlendingState.js"; @@ -51,6 +51,15 @@ function SkyAtmosphere(ellipsoid) { */ this.show = true; + /** + * Compute atmosphere per-fragment instead of per-vertex. + * This produces better looking atmosphere with a slight performance penalty. + * + * @type {Boolean} + * @default true + */ + this.perFragmentAtmosphere = true; + this._ellipsoid = ellipsoid; this._command = new DrawCommand({ owner: this, @@ -58,8 +67,7 @@ function SkyAtmosphere(ellipsoid) { this._spSkyFromSpace = undefined; this._spSkyFromAtmosphere = undefined; - this._spSkyFromSpaceColorCorrect = undefined; - this._spSkyFromAtmosphereColorCorrect = undefined; + this._flags = undefined; /** * The hue shift to apply to the atmosphere. Defaults to 0.0 (no shift). @@ -88,22 +96,22 @@ function SkyAtmosphere(ellipsoid) { this._hueSaturationBrightness = new Cartesian3(); // camera height, outer radius, inner radius, dynamic atmosphere color flag - var cameraAndRadiiAndDynamicAtmosphereColor = new Cartesian4(); + var radiiAndDynamicAtmosphereColor = new Cartesian3(); // Toggles whether the sun position is used. 0 treats the sun as always directly overhead. - cameraAndRadiiAndDynamicAtmosphereColor.w = 0; - cameraAndRadiiAndDynamicAtmosphereColor.y = Cartesian3.maximumComponent( + radiiAndDynamicAtmosphereColor.z = 0; + radiiAndDynamicAtmosphereColor.x = Cartesian3.maximumComponent( Cartesian3.multiplyByScalar(ellipsoid.radii, 1.025, new Cartesian3()) ); - cameraAndRadiiAndDynamicAtmosphereColor.z = ellipsoid.maximumRadius; + radiiAndDynamicAtmosphereColor.y = ellipsoid.maximumRadius; - this._cameraAndRadiiAndDynamicAtmosphereColor = cameraAndRadiiAndDynamicAtmosphereColor; + this._radiiAndDynamicAtmosphereColor = radiiAndDynamicAtmosphereColor; var that = this; this._command.uniformMap = { - u_cameraAndRadiiAndDynamicAtmosphereColor: function () { - return that._cameraAndRadiiAndDynamicAtmosphereColor; + u_radiiAndDynamicAtmosphereColor: function () { + return that._radiiAndDynamicAtmosphereColor; }, u_hsbShift: function () { that._hueSaturationBrightness.x = that.hueShift; @@ -136,11 +144,8 @@ SkyAtmosphere.prototype.setDynamicAtmosphereColor = function ( enableLighting, useSunDirection ) { - this._cameraAndRadiiAndDynamicAtmosphereColor.w = enableLighting - ? useSunDirection - ? 2.0 - : 1.0 - : 0.0; + var lightEnum = enableLighting ? (useSunDirection ? 2.0 : 1.0) : 0.0; + this._radiiAndDynamicAtmosphereColor.z = lightEnum; }; /** @@ -161,11 +166,14 @@ SkyAtmosphere.prototype.update = function (frameState) { return undefined; } + var context = frameState.context; + + var colorCorrect = hasColorCorrection(this); + var perFragmentAtmosphere = this.perFragmentAtmosphere; + var command = this._command; if (!defined(command.vertexArray)) { - var context = frameState.context; - var geometry = EllipsoidGeometry.createGeometry( new EllipsoidGeometry({ radii: Cartesian3.multiplyByScalar( @@ -192,84 +200,71 @@ SkyAtmosphere.prototype.update = function (frameState) { blending: BlendingState.ALPHA_BLEND, depthMask: false, }); + } + + var flags = colorCorrect | (perFragmentAtmosphere << 2); + + if (flags !== this._flags) { + this._flags = flags; + + var defines = []; + + if (colorCorrect) { + defines.push("COLOR_CORRECT"); + } + + if (perFragmentAtmosphere) { + defines.push("PER_FRAGMENT_ATMOSPHERE"); + } var vs = new ShaderSource({ - defines: ["SKY_FROM_SPACE"], - sources: [SkyAtmosphereVS], + defines: defines.concat("SKY_FROM_SPACE"), + sources: [SkyAtmosphereCommon, SkyAtmosphereVS], + }); + + var fs = new ShaderSource({ + defines: defines.concat("SKY_FROM_SPACE"), + sources: [SkyAtmosphereCommon, SkyAtmosphereFS], }); this._spSkyFromSpace = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, - fragmentShaderSource: SkyAtmosphereFS, + fragmentShaderSource: fs, }); vs = new ShaderSource({ - defines: ["SKY_FROM_ATMOSPHERE"], - sources: [SkyAtmosphereVS], + defines: defines.concat("SKY_FROM_ATMOSPHERE"), + sources: [SkyAtmosphereCommon, SkyAtmosphereVS], }); - this._spSkyFromAtmosphere = ShaderProgram.fromCache({ - context: context, - vertexShaderSource: vs, - fragmentShaderSource: SkyAtmosphereFS, - }); - } - // Compile the color correcting versions of the shader on demand - var useColorCorrect = colorCorrect(this); - if ( - useColorCorrect && - (!defined(this._spSkyFromSpaceColorCorrect) || - !defined(this._spSkyFromAtmosphereColorCorrect)) - ) { - var contextColorCorrect = frameState.context; - - var vsColorCorrect = new ShaderSource({ - defines: ["SKY_FROM_SPACE"], - sources: [SkyAtmosphereVS], - }); - var fsColorCorrect = new ShaderSource({ - defines: ["COLOR_CORRECT"], - sources: [SkyAtmosphereFS], + fs = new ShaderSource({ + defines: defines.concat("SKY_FROM_ATMOSPHERE"), + sources: [SkyAtmosphereCommon, SkyAtmosphereFS], }); - this._spSkyFromSpaceColorCorrect = ShaderProgram.fromCache({ - context: contextColorCorrect, - vertexShaderSource: vsColorCorrect, - fragmentShaderSource: fsColorCorrect, - }); - vsColorCorrect = new ShaderSource({ - defines: ["SKY_FROM_ATMOSPHERE"], - sources: [SkyAtmosphereVS], - }); - this._spSkyFromAtmosphereColorCorrect = ShaderProgram.fromCache({ - context: contextColorCorrect, - vertexShaderSource: vsColorCorrect, - fragmentShaderSource: fsColorCorrect, + this._spSkyFromAtmosphere = ShaderProgram.fromCache({ + context: context, + vertexShaderSource: vs, + fragmentShaderSource: fs, }); } var cameraPosition = frameState.camera.positionWC; - var cameraHeight = Cartesian3.magnitude(cameraPosition); - this._cameraAndRadiiAndDynamicAtmosphereColor.x = cameraHeight; - if (cameraHeight > this._cameraAndRadiiAndDynamicAtmosphereColor.y) { + if (cameraHeight > this._radiiAndDynamicAtmosphereColor.x) { // Camera in space - command.shaderProgram = useColorCorrect - ? this._spSkyFromSpaceColorCorrect - : this._spSkyFromSpace; + command.shaderProgram = this._spSkyFromSpace; } else { // Camera in atmosphere - command.shaderProgram = useColorCorrect - ? this._spSkyFromAtmosphereColorCorrect - : this._spSkyFromAtmosphere; + command.shaderProgram = this._spSkyFromAtmosphere; } return command; }; -function colorCorrect(skyAtmosphere) { +function hasColorCorrection(skyAtmosphere) { return !( CesiumMath.equalsEpsilon( skyAtmosphere.hueShift, @@ -325,12 +320,6 @@ SkyAtmosphere.prototype.destroy = function () { this._spSkyFromSpace = this._spSkyFromSpace && this._spSkyFromSpace.destroy(); this._spSkyFromAtmosphere = this._spSkyFromAtmosphere && this._spSkyFromAtmosphere.destroy(); - this._spSkyFromSpaceColorCorrect = - this._spSkyFromSpaceColorCorrect && - this._spSkyFromSpaceColorCorrect.destroy(); - this._spSkyFromAtmosphereColorCorrect = - this._spSkyFromAtmosphereColorCorrect && - this._spSkyFromAtmosphereColorCorrect.destroy(); return destroyObject(this); }; export default SkyAtmosphere; diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl new file mode 100644 index 000000000000..31ee34403ee2 --- /dev/null +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -0,0 +1,242 @@ +/** + * @license + * Copyright (c) 2000-2005, Sean O'Neil (s_p_oneil@hotmail.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Modifications made by Cesium GS, Inc. + */ + + // Code: http://sponeil.net/ + // GPU Gems 2 Article: https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter16.html + +const float Kr = 0.0025; +const float Kr4PI = Kr * 4.0 * czm_pi; +const float Km = 0.0015; +const float Km4PI = Km * 4.0 * czm_pi; +const float ESun = 15.0; +const float KmESun = Km * ESun; +const float KrESun = Kr * ESun; +const vec3 InvWavelength = vec3( + 5.60204474633241, // Red = 1.0 / Math.pow(0.650, 4.0) + 9.473284437923038, // Green = 1.0 / Math.pow(0.570, 4.0) + 19.643802610477206); // Blue = 1.0 / Math.pow(0.475, 4.0) +const float rayleighScaleDepth = 0.25; + +const int nSamples = 2; +const float fSamples = 2.0; + +const float g = -0.95; +const float g2 = g * g; + +#ifdef COLOR_CORRECT +uniform vec3 u_hsbShift; // Hue, saturation, brightness +#endif + +uniform vec3 u_radiiAndDynamicAtmosphereColor; // Camera height, outer radius, inner radius, dynamic atmosphere color flag + +float scale(float cosAngle) +{ + float x = 1.0 - cosAngle; + return rayleighScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); +} + +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 calculateRayScatteringFromSpace(in vec3 positionWC, in vec3 ray, in float outerRadius, inout float far, out vec3 start, out float startOffset) +{ + // Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere) + float cameraHeight = length(positionWC); + float B = 2.0 * dot(positionWC, ray); + float C = cameraHeight * cameraHeight - outerRadius * outerRadius; + float det = max(0.0, B * B - 4.0 * C); + float near = 0.5 * (-B - sqrt(det)); + + // Calculate the ray's starting position, then calculate its scattering offset + start = positionWC + ray * near; + far -= near; + float startAngle = dot(ray, start) / outerRadius; + float startDepth = exp(-1.0 / rayleighScaleDepth); + startOffset = startDepth * scale(startAngle); +} + +void calculateRayScatteringFromGround(in vec3 positionWC, in vec3 ray, in float atmosphereScale, in float innerRadius, out vec3 start, out float startOffset) +{ + // Calculate the ray's starting position, then calculate its scattering offset + float cameraHeight = length(positionWC); + start = positionWC; + float height = length(start); + float depth = exp((atmosphereScale / rayleighScaleDepth ) * (innerRadius - cameraHeight)); + float startAngle = dot(ray, start) / height; + startOffset = depth*scale(startAngle); +} + +czm_raySegment rayEllipsoidIntersection(czm_ray ray, vec3 ellipsoid_center, vec3 ellipsoid_inverseRadii) +{ + vec3 o = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.origin, 1.0)).xyz; + vec3 d = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.direction, 0.0)).xyz; + + float a = dot(d, d); + float b = dot(d, o); + float c = dot(o, o) - 1.0; + float discriminant = b * b - a * c; + if (discriminant < 0.0) + { + return czm_emptyRaySegment; + } + discriminant = sqrt(discriminant); + float t1 = (-b + (-discriminant)) / a; + float t2 = (-b + (discriminant)) / a; + + if (t1 < 0.0 && t2 < 0.0) + { + return czm_emptyRaySegment; + } + + if (t1 < 0.0 && t2 >= 0.0) + { + t1 = 0.0; + } + + return czm_raySegment(t1, t2); +} + +void calculateMieColorAndRayleighColor(vec3 outerPositionWC, float ellipsoidScaleFactor, out vec3 mieColor, out vec3 rayleighColor) +{ + vec3 directionWC = normalize(outerPositionWC - czm_viewerPositionWC); + vec3 directionEC = czm_viewRotation * directionWC; + czm_ray viewRay = czm_ray(vec3(0.0), directionEC); + czm_raySegment raySegment = rayEllipsoidIntersection(viewRay, vec3(czm_view[3]), czm_ellipsoidInverseRadii * ellipsoidScaleFactor); + bool intersectsEllipsoid = raySegment.start >= 0.0; + + vec3 startPositionWC = czm_viewerPositionWC; + if (intersectsEllipsoid) + { + startPositionWC = czm_viewerPositionWC + raySegment.stop * directionWC; + } + + vec3 lightDirection = getLightDirection(startPositionWC); + + // Unpack attributes + float outerRadius = u_radiiAndDynamicAtmosphereColor.x; + float innerRadius = u_radiiAndDynamicAtmosphereColor.y; + + // Get the ray from the start position to the outer position and its length (which is the far point of the ray passing through the atmosphere) + vec3 ray = outerPositionWC - startPositionWC; + float far = length(ray); + ray /= far; + float atmosphereScale = 1.0 / (outerRadius - innerRadius); + + vec3 start; + float startOffset; + +#ifdef SKY_FROM_SPACE + if (intersectsEllipsoid) + { + calculateRayScatteringFromGround(startPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); + } + else + { + calculateRayScatteringFromSpace(startPositionWC, ray, outerRadius, far, start, startOffset); + } +#else + calculateRayScatteringFromGround(startPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); +#endif + + // Initialize the scattering loop variables + float sampleLength = far / fSamples; + float scaledLength = sampleLength * atmosphereScale; + vec3 sampleRay = ray * sampleLength; + vec3 samplePoint = start + sampleRay * 0.5; + + // Now loop through the sample rays + vec3 frontColor = vec3(0.0, 0.0, 0.0); + + for (int i = 0; i czm_epsilon7 ? hsb.z + u_hsbShift.z : 0.0; // brightness + // Convert shifted hsb back to rgb + rgb = czm_HSBToRGB(hsb); +#endif + + float outerRadius = u_radiiAndDynamicAtmosphereColor.x; + float innerRadius = u_radiiAndDynamicAtmosphereColor.y; + float lightEnum = u_radiiAndDynamicAtmosphereColor.z; + + // Alter alpha based on how close the viewer is to the ground (1.0 = on ground, 0.0 = at edge of atmosphere) + float atmosphereAlpha = clamp((outerRadius - length(positionWC)) / (outerRadius - innerRadius), 0.0, 1.0); + + // Alter alpha based on time of day (0.0 = night , 1.0 = day) + float nightAlpha = (lightEnum != 0.0) ? clamp(dot(normalize(positionWC), lightDirection), 0.0, 1.0) : 1.0; + atmosphereAlpha *= pow(nightAlpha, 0.5); + + return vec4(rgb, mix(clamp(rgbExposure.b, 0.0, 1.0), 1.0, atmosphereAlpha) * smoothstep(0.0, 1.0, czm_morphTime)); +} diff --git a/Source/Shaders/SkyAtmosphereFS.glsl b/Source/Shaders/SkyAtmosphereFS.glsl index 9c5625299b81..ee2f6eb37f2d 100644 --- a/Source/Shaders/SkyAtmosphereFS.glsl +++ b/Source/Shaders/SkyAtmosphereFS.glsl @@ -27,64 +27,29 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Modifications made by Analytical Graphics, Inc. + * Modifications made by Cesium GS, Inc. */ // Code: http://sponeil.net/ // GPU Gems 2 Article: https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter16.html -#ifdef COLOR_CORRECT -uniform vec3 u_hsbShift; // Hue, saturation, brightness -#endif - -uniform vec4 u_cameraAndRadiiAndDynamicAtmosphereColor; // Camera height, outer radius, inner radius, dynamic atmosphere color flag - -const float g = -0.95; -const float g2 = g * g; +varying vec3 v_outerPositionWC; +#ifndef PER_FRAGMENT_ATMOSPHERE varying vec3 v_rayleighColor; varying vec3 v_mieColor; -varying vec3 v_toCamera; -varying vec3 v_positionEC; +#endif void main (void) { - float lightEnum = u_cameraAndRadiiAndDynamicAtmosphereColor.w; - vec3 lightDirection = - czm_viewerPositionWC * float(lightEnum == 0.0) + - czm_lightDirectionWC * float(lightEnum == 1.0) + - czm_sunDirectionWC * float(lightEnum == 2.0); - lightDirection = normalize(lightDirection); - - // Extra normalize added for Android - float cosAngle = dot(lightDirection, normalize(v_toCamera)) / length(v_toCamera); - float rayleighPhase = 0.75 * (1.0 + cosAngle * cosAngle); - float miePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cosAngle * cosAngle) / pow(1.0 + g2 - 2.0 * g * cosAngle, 1.5); - - vec3 rgb = rayleighPhase * v_rayleighColor + miePhase * v_mieColor; - -#ifndef HDR - const float exposure = 2.0; - rgb = vec3(1.0) - exp(-exposure * rgb); -#endif - -#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); + vec3 toCamera = czm_viewerPositionWC - v_outerPositionWC; + vec3 lightDirection = getLightDirection(czm_viewerPositionWC); +#ifdef PER_FRAGMENT_ATMOSPHERE + vec3 mieColor; + vec3 rayleighColor; + calculateMieColorAndRayleighColor(v_outerPositionWC, 1.0, mieColor, rayleighColor); + gl_FragColor = calculateFinalColor(czm_viewerPositionWC, toCamera, lightDirection, rayleighColor, mieColor); +#else + gl_FragColor = calculateFinalColor(czm_viewerPositionWC, toCamera, lightDirection, v_rayleighColor, v_mieColor); #endif - - // Alter alpha based on how close the viewer is to the ground (1.0 = on ground, 0.0 = at edge of atmosphere) - float atmosphereAlpha = clamp((u_cameraAndRadiiAndDynamicAtmosphereColor.y - u_cameraAndRadiiAndDynamicAtmosphereColor.x) / (u_cameraAndRadiiAndDynamicAtmosphereColor.y - u_cameraAndRadiiAndDynamicAtmosphereColor.z), 0.0, 1.0); - - // Alter alpha based on time of day (0.0 = night , 1.0 = day) - float nightAlpha = (lightEnum != 0.0) ? clamp(dot(normalize(czm_viewerPositionWC), lightDirection), 0.0, 1.0) : 1.0; - atmosphereAlpha *= pow(nightAlpha, 0.5); - - gl_FragColor = vec4(rgb, mix(rgb.b, 1.0, atmosphereAlpha) * smoothstep(0.0, 1.0, czm_morphTime)); } diff --git a/Source/Shaders/SkyAtmosphereVS.glsl b/Source/Shaders/SkyAtmosphereVS.glsl index bfb0bad8922f..8fd95bb1e581 100644 --- a/Source/Shaders/SkyAtmosphereVS.glsl +++ b/Source/Shaders/SkyAtmosphereVS.glsl @@ -27,7 +27,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Modifications made by Analytical Graphics, Inc. + * Modifications made by Cesium GS, Inc. */ // Code: http://sponeil.net/ @@ -35,101 +35,18 @@ attribute vec4 position; -uniform vec4 u_cameraAndRadiiAndDynamicAtmosphereColor; // Camera height, outer radius, inner radius, dynamic atmosphere color flag - -const float Kr = 0.0025; -const float Kr4PI = Kr * 4.0 * czm_pi; -const float Km = 0.0015; -const float Km4PI = Km * 4.0 * czm_pi; -const float ESun = 15.0; -const float KmESun = Km * ESun; -const float KrESun = Kr * ESun; -const vec3 InvWavelength = vec3( - 5.60204474633241, // Red = 1.0 / Math.pow(0.650, 4.0) - 9.473284437923038, // Green = 1.0 / Math.pow(0.570, 4.0) - 19.643802610477206); // Blue = 1.0 / Math.pow(0.475, 4.0) -const float rayleighScaleDepth = 0.25; - -const int nSamples = 2; -const float fSamples = 2.0; +varying vec3 v_outerPositionWC; +#ifndef PER_FRAGMENT_ATMOSPHERE varying vec3 v_rayleighColor; varying vec3 v_mieColor; -varying vec3 v_toCamera; - -float scale(float cosAngle) -{ - float x = 1.0 - cosAngle; - return rayleighScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); -} +#endif void main(void) { - // Unpack attributes - float cameraHeight = u_cameraAndRadiiAndDynamicAtmosphereColor.x; - float outerRadius = u_cameraAndRadiiAndDynamicAtmosphereColor.y; - float innerRadius = u_cameraAndRadiiAndDynamicAtmosphereColor.z; - - // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere) - vec3 positionV3 = position.xyz; - vec3 ray = positionV3 - czm_viewerPositionWC; - float far = length(ray); - ray /= far; - float atmosphereScale = 1.0 / (outerRadius - innerRadius); - -#ifdef SKY_FROM_SPACE - // Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere) - float B = 2.0 * dot(czm_viewerPositionWC, ray); - float C = cameraHeight * cameraHeight - outerRadius * outerRadius; - float det = max(0.0, B*B - 4.0 * C); - float near = 0.5 * (-B - sqrt(det)); - - // Calculate the ray's starting position, then calculate its scattering offset - vec3 start = czm_viewerPositionWC + ray * near; - far -= near; - float startAngle = dot(ray, start) / outerRadius; - float startDepth = exp(-1.0 / rayleighScaleDepth ); - float startOffset = startDepth*scale(startAngle); -#else // SKY_FROM_ATMOSPHERE - // Calculate the ray's starting position, then calculate its scattering offset - vec3 start = czm_viewerPositionWC; - float height = length(start); - float depth = exp((atmosphereScale / rayleighScaleDepth ) * (innerRadius - cameraHeight)); - float startAngle = dot(ray, start) / height; - float startOffset = depth*scale(startAngle); +#ifndef PER_FRAGMENT_ATMOSPHERE + calculateMieColorAndRayleighColor(position.xyz, 1.002, v_mieColor, v_rayleighColor); #endif - - float lightEnum = u_cameraAndRadiiAndDynamicAtmosphereColor.w; - vec3 lightDirection = - czm_viewerPositionWC * float(lightEnum == 0.0) + - czm_lightDirectionWC * float(lightEnum == 1.0) + - czm_sunDirectionWC * float(lightEnum == 2.0); - lightDirection = normalize(lightDirection); - - // Initialize the scattering loop variables - float sampleLength = far / fSamples; - float scaledLength = sampleLength * atmosphereScale; - vec3 sampleRay = ray * sampleLength; - vec3 samplePoint = start + sampleRay * 0.5; - - // Now loop through the sample rays - vec3 frontColor = vec3(0.0, 0.0, 0.0); - - for(int i=0; i Date: Sun, 17 May 2020 19:39:42 -0400 Subject: [PATCH 02/18] Update CHANGES.md --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 5e7b0392c1d3..ef8281c6d77a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ - Added `Cesium3DTileset.extensions` to get the extensions property from the tileset JSON. [#8829](https://github.com/CesiumGS/cesium/pull/8829) - Added `frustumSplits` option to `DebugCameraPrimitive`. [8849](https://github.com/CesiumGS/cesium/pull/8849) +- Added `SkyAtmosphere.perFragmentAtmosphere` to switch between per-vertex and per-fragment atmosphere shading. Atmosphere is shaded per-fragment by default which looks better but may cause a slight performance penalty. [#8866](https://github.com/CesiumGS/cesium/pull/8866) ##### Fixes :wrench: @@ -13,6 +14,7 @@ - Fixed a bug where a removed billboard could prevent changing of the `TerrainProvider`. [#8766](https://github.com/CesiumGS/cesium/pull/8766) - Fixed an issue with 3D Tiles point cloud styling where `${feature.propertyName}` and `${feature["propertyName"]}` syntax would cause a crash. Also fixed an issue where property names with non-alphanumeric characters would crash. [#8785](https://github.com/CesiumGS/cesium/pull/8785) - Fixed a bug where `DebugCameraPrimitive` was ignoring the near and far planes of the `Camera`. [#8848](https://github.com/CesiumGS/cesium/issues/8848) +- Fixed sky atmosphere artifacts below the horizon. [#8866](https://github.com/CesiumGS/cesium/pull/8866) ### 1.69.0 - 2020-05-01 From 7ac439cd84c29a8cbaec38a1a60a7cafa22993d3 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Sun, 17 May 2020 19:40:56 -0400 Subject: [PATCH 03/18] Fix tests --- Specs/Scene/SkyAtmosphereSpec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Specs/Scene/SkyAtmosphereSpec.js b/Specs/Scene/SkyAtmosphereSpec.js index 71b98e38971f..016f32779427 100644 --- a/Specs/Scene/SkyAtmosphereSpec.js +++ b/Specs/Scene/SkyAtmosphereSpec.js @@ -57,7 +57,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._cameraAndRadiiAndDynamicAtmosphereColor.w).toBe(1); + expect(s._radiiAndDynamicAtmosphereColor.z).toBe(1); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); @@ -72,7 +72,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._cameraAndRadiiAndDynamicAtmosphereColor.w).toBe(2); + expect(s._radiiAndDynamicAtmosphereColor.z).toBe(2); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); @@ -87,7 +87,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._cameraAndRadiiAndDynamicAtmosphereColor.w).toBe(0); + expect(s._radiiAndDynamicAtmosphereColor.z).toBe(0); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); From 2f9f47c19cc6e8b4174dc62ba7f03996ff3ddad0 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 19 May 2020 10:09:02 -0400 Subject: [PATCH 04/18] Updates from review --- Source/Scene/SkyAtmosphere.js | 28 +++++++++------ Source/Shaders/SkyAtmosphereCommon.glsl | 29 +++++++-------- Source/Shaders/SkyAtmosphereFS.glsl | 48 +++++-------------------- Source/Shaders/SkyAtmosphereVS.glsl | 39 ++------------------ 4 files changed, 42 insertions(+), 102 deletions(-) diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 37a4522df86b..ff1234ae0c28 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -1,4 +1,5 @@ import Cartesian3 from "../Core/Cartesian3.js"; +import Cartesian4 from "../Core/Cartesian4.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; @@ -95,23 +96,28 @@ function SkyAtmosphere(ellipsoid) { this._hueSaturationBrightness = new Cartesian3(); - // camera height, outer radius, inner radius, dynamic atmosphere color flag - var radiiAndDynamicAtmosphereColor = new Cartesian3(); + // outer radius, inner radius, dynamic atmosphere color flag, inverse scale + var radiiAndDynamicAtmosphereColorAndInverseScale = new Cartesian4(); - // Toggles whether the sun position is used. 0 treats the sun as always directly overhead. - radiiAndDynamicAtmosphereColor.z = 0; - radiiAndDynamicAtmosphereColor.x = Cartesian3.maximumComponent( + radiiAndDynamicAtmosphereColorAndInverseScale.x = Cartesian3.maximumComponent( Cartesian3.multiplyByScalar(ellipsoid.radii, 1.025, new Cartesian3()) ); - radiiAndDynamicAtmosphereColor.y = ellipsoid.maximumRadius; + radiiAndDynamicAtmosphereColorAndInverseScale.y = ellipsoid.maximumRadius; + + // Toggles whether the sun position is used. 0 treats the sun as always directly overhead. + radiiAndDynamicAtmosphereColorAndInverseScale.z = 0; + + // Controls the distance below the horizon at which atmosphere transitions from its brightest to a more neutral blue color + // Higher values push the transition point further below the horizon + radiiAndDynamicAtmosphereColorAndInverseScale.w = 1.003; - this._radiiAndDynamicAtmosphereColor = radiiAndDynamicAtmosphereColor; + this._radiiAndDynamicAtmosphereColorAndInverseScale = radiiAndDynamicAtmosphereColorAndInverseScale; var that = this; this._command.uniformMap = { - u_radiiAndDynamicAtmosphereColor: function () { - return that._radiiAndDynamicAtmosphereColor; + u_radiiAndDynamicAtmosphereColorAndInverseScale: function () { + return that._radiiAndDynamicAtmosphereColorAndInverseScale; }, u_hsbShift: function () { that._hueSaturationBrightness.x = that.hueShift; @@ -145,7 +151,7 @@ SkyAtmosphere.prototype.setDynamicAtmosphereColor = function ( useSunDirection ) { var lightEnum = enableLighting ? (useSunDirection ? 2.0 : 1.0) : 0.0; - this._radiiAndDynamicAtmosphereColor.z = lightEnum; + this._radiiAndDynamicAtmosphereColorAndInverseScale.z = lightEnum; }; /** @@ -253,7 +259,7 @@ SkyAtmosphere.prototype.update = function (frameState) { var cameraPosition = frameState.camera.positionWC; var cameraHeight = Cartesian3.magnitude(cameraPosition); - if (cameraHeight > this._radiiAndDynamicAtmosphereColor.x) { + if (cameraHeight > this._radiiAndDynamicAtmosphereColorAndInverseScale.x) { // Camera in space command.shaderProgram = this._spSkyFromSpace; } else { diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index 31ee34403ee2..eb0ab513b3c2 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -56,7 +56,7 @@ const float g2 = g * g; uniform vec3 u_hsbShift; // Hue, saturation, brightness #endif -uniform vec3 u_radiiAndDynamicAtmosphereColor; // Camera height, outer radius, inner radius, dynamic atmosphere color flag +uniform vec4 u_radiiAndDynamicAtmosphereColorAndInverseScale; // outer radius, inner radius, dynamic atmosphere color flag, inverse scale float scale(float cosAngle) { @@ -66,7 +66,7 @@ float scale(float cosAngle) vec3 getLightDirection(vec3 positionWC) { - float lightEnum = u_radiiAndDynamicAtmosphereColor.z; + float lightEnum = u_radiiAndDynamicAtmosphereColorAndInverseScale.z; vec3 lightDirection = positionWC * float(lightEnum == 0.0) + czm_lightDirectionWC * float(lightEnum == 1.0) + @@ -116,8 +116,8 @@ czm_raySegment rayEllipsoidIntersection(czm_ray ray, vec3 ellipsoid_center, vec3 return czm_emptyRaySegment; } discriminant = sqrt(discriminant); - float t1 = (-b + (-discriminant)) / a; - float t2 = (-b + (discriminant)) / a; + float t1 = (-b - discriminant) / a; + float t2 = (-b + discriminant) / a; if (t1 < 0.0 && t2 < 0.0) { @@ -132,12 +132,17 @@ czm_raySegment rayEllipsoidIntersection(czm_ray ray, vec3 ellipsoid_center, vec3 return czm_raySegment(t1, t2); } -void calculateMieColorAndRayleighColor(vec3 outerPositionWC, float ellipsoidScaleFactor, out vec3 mieColor, out vec3 rayleighColor) +void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, out vec3 rayleighColor) { + // Unpack attributes + float outerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.x; + float innerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.y; + float inverseScale = u_radiiAndDynamicAtmosphereColorAndInverseScale.w; + vec3 directionWC = normalize(outerPositionWC - czm_viewerPositionWC); vec3 directionEC = czm_viewRotation * directionWC; czm_ray viewRay = czm_ray(vec3(0.0), directionEC); - czm_raySegment raySegment = rayEllipsoidIntersection(viewRay, vec3(czm_view[3]), czm_ellipsoidInverseRadii * ellipsoidScaleFactor); + czm_raySegment raySegment = rayEllipsoidIntersection(viewRay, vec3(czm_view[3]), czm_ellipsoidInverseRadii * inverseScale); bool intersectsEllipsoid = raySegment.start >= 0.0; vec3 startPositionWC = czm_viewerPositionWC; @@ -148,10 +153,6 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, float ellipsoidScal vec3 lightDirection = getLightDirection(startPositionWC); - // Unpack attributes - float outerRadius = u_radiiAndDynamicAtmosphereColor.x; - float innerRadius = u_radiiAndDynamicAtmosphereColor.y; - // Get the ray from the start position to the outer position and its length (which is the far point of the ray passing through the atmosphere) vec3 ray = outerPositionWC - startPositionWC; float far = length(ray); @@ -200,7 +201,7 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, float ellipsoidScal rayleighColor = frontColor * (InvWavelength * KrESun); } -vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, vec3 rayleighColor, vec3 mieColor) +vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, vec3 mieColor, vec3 rayleighColor) { // Extra normalize added for Android float cosAngle = dot(lightDirection, normalize(toCamera)) / length(toCamera); @@ -227,9 +228,9 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve rgb = czm_HSBToRGB(hsb); #endif - float outerRadius = u_radiiAndDynamicAtmosphereColor.x; - float innerRadius = u_radiiAndDynamicAtmosphereColor.y; - float lightEnum = u_radiiAndDynamicAtmosphereColor.z; + float outerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.x; + float innerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.y; + float lightEnum = u_radiiAndDynamicAtmosphereColorAndInverseScale.z; // Alter alpha based on how close the viewer is to the ground (1.0 = on ground, 0.0 = at edge of atmosphere) float atmosphereAlpha = clamp((outerRadius - length(positionWC)) / (outerRadius - innerRadius), 0.0, 1.0); diff --git a/Source/Shaders/SkyAtmosphereFS.glsl b/Source/Shaders/SkyAtmosphereFS.glsl index ee2f6eb37f2d..b80e54cd43c0 100644 --- a/Source/Shaders/SkyAtmosphereFS.glsl +++ b/Source/Shaders/SkyAtmosphereFS.glsl @@ -1,55 +1,23 @@ -/** - * @license - * Copyright (c) 2000-2005, Sean O'Neil (s_p_oneil@hotmail.com) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modifications made by Cesium GS, Inc. - */ - - // Code: http://sponeil.net/ - // GPU Gems 2 Article: https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter16.html - varying vec3 v_outerPositionWC; #ifndef PER_FRAGMENT_ATMOSPHERE -varying vec3 v_rayleighColor; varying vec3 v_mieColor; +varying vec3 v_rayleighColor; #endif void main (void) { vec3 toCamera = czm_viewerPositionWC - v_outerPositionWC; vec3 lightDirection = getLightDirection(czm_viewerPositionWC); -#ifdef PER_FRAGMENT_ATMOSPHERE vec3 mieColor; vec3 rayleighColor; - calculateMieColorAndRayleighColor(v_outerPositionWC, 1.0, mieColor, rayleighColor); - gl_FragColor = calculateFinalColor(czm_viewerPositionWC, toCamera, lightDirection, rayleighColor, mieColor); + +#ifdef PER_FRAGMENT_ATMOSPHERE + calculateMieColorAndRayleighColor(v_outerPositionWC, mieColor, rayleighColor); #else - gl_FragColor = calculateFinalColor(czm_viewerPositionWC, toCamera, lightDirection, v_rayleighColor, v_mieColor); + mieColor = v_mieColor; + rayleighColor = v_rayleighColor; #endif + + gl_FragColor = calculateFinalColor(czm_viewerPositionWC, toCamera, lightDirection, mieColor, rayleighColor); } diff --git a/Source/Shaders/SkyAtmosphereVS.glsl b/Source/Shaders/SkyAtmosphereVS.glsl index 8fd95bb1e581..d86efa653036 100644 --- a/Source/Shaders/SkyAtmosphereVS.glsl +++ b/Source/Shaders/SkyAtmosphereVS.glsl @@ -1,51 +1,16 @@ -/** - * @license - * Copyright (c) 2000-2005, Sean O'Neil (s_p_oneil@hotmail.com) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the project nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modifications made by Cesium GS, Inc. - */ - - // Code: http://sponeil.net/ - // GPU Gems 2 Article: https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter16.html - attribute vec4 position; varying vec3 v_outerPositionWC; #ifndef PER_FRAGMENT_ATMOSPHERE -varying vec3 v_rayleighColor; varying vec3 v_mieColor; +varying vec3 v_rayleighColor; #endif void main(void) { #ifndef PER_FRAGMENT_ATMOSPHERE - calculateMieColorAndRayleighColor(position.xyz, 1.002, v_mieColor, v_rayleighColor); + calculateMieColorAndRayleighColor(position.xyz, v_mieColor, v_rayleighColor); #endif v_outerPositionWC = position.xyz; gl_Position = czm_modelViewProjection * position; From b4e09398e23e1cfda31362c0749f96b216ad1aeb Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 19 May 2020 11:25:18 -0400 Subject: [PATCH 05/18] Tweak atmosphere under ellipsoid so it's less bright --- Source/Shaders/SkyAtmosphereCommon.glsl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index eb0ab513b3c2..7b6778aad94e 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -155,8 +155,12 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, // Get the ray from the start position to the outer position and its length (which is the far point of the ray passing through the atmosphere) vec3 ray = outerPositionWC - startPositionWC; - float far = length(ray); - ray /= far; + float distance = length(ray); + ray /= distance; + + float maxDistance = intersectsEllipsoid ? innerRadius * 0.1 : distance; + float far = min(distance, maxDistance); + float atmosphereScale = 1.0 / (outerRadius - innerRadius); vec3 start; From 3a73f6125c4ef281bc50fd39487db92f18178d7d Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 19 May 2020 15:47:35 -0400 Subject: [PATCH 06/18] Fix tests --- Specs/Scene/SkyAtmosphereSpec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Specs/Scene/SkyAtmosphereSpec.js b/Specs/Scene/SkyAtmosphereSpec.js index 016f32779427..df930a233d78 100644 --- a/Specs/Scene/SkyAtmosphereSpec.js +++ b/Specs/Scene/SkyAtmosphereSpec.js @@ -57,7 +57,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._radiiAndDynamicAtmosphereColor.z).toBe(1); + expect(s._radiiAndDynamicAtmosphereColorAndInverseScale.z).toBe(1); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); @@ -72,7 +72,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._radiiAndDynamicAtmosphereColor.z).toBe(2); + expect(s._radiiAndDynamicAtmosphereColorAndInverseScale.z).toBe(2); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); @@ -87,7 +87,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._radiiAndDynamicAtmosphereColor.z).toBe(0); + expect(s._radiiAndDynamicAtmosphereColorAndInverseScale.z).toBe(0); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); From 1409f1d329335d7523297df85378bb500c5bc254 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 20 May 2020 01:53:23 -0400 Subject: [PATCH 07/18] Temp --- Source/Shaders/SkyAtmosphereCommon.glsl | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index 7b6778aad94e..810652e96b20 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -143,7 +143,7 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, vec3 directionEC = czm_viewRotation * directionWC; czm_ray viewRay = czm_ray(vec3(0.0), directionEC); czm_raySegment raySegment = rayEllipsoidIntersection(viewRay, vec3(czm_view[3]), czm_ellipsoidInverseRadii * inverseScale); - bool intersectsEllipsoid = raySegment.start >= 0.0; + bool intersectsEllipsoid = false;//raySegment.start >= 0.0; vec3 startPositionWC = czm_viewerPositionWC; if (intersectsEllipsoid) @@ -212,7 +212,15 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve float rayleighPhase = 0.75 * (1.0 + cosAngle * cosAngle); float miePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cosAngle * cosAngle) / pow(1.0 + g2 - 2.0 * g * cosAngle, 1.5); - vec3 rgb = rayleighPhase * rayleighColor + miePhase * mieColor; + vec3 rayleighFinal = rayleighPhase * rayleighColor; + vec3 mieFinal = miePhase * mieColor; + vec3 rgb = rayleighFinal + mieFinal; + + if (rgb.b > 1000000.0) + { + // Discard colors that exceed some large number value to prevent against NaN's from the exponent calculation below + return vec4(0.0); + } const float exposure = 2.0; vec3 rgbExposure = vec3(1.0) - exp(-exposure * rgb); @@ -243,5 +251,14 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve float nightAlpha = (lightEnum != 0.0) ? clamp(dot(normalize(positionWC), lightDirection), 0.0, 1.0) : 1.0; atmosphereAlpha *= pow(nightAlpha, 0.5); - return vec4(rgb, mix(clamp(rgbExposure.b, 0.0, 1.0), 1.0, atmosphereAlpha) * smoothstep(0.0, 1.0, czm_morphTime)); + vec4 finalColor = vec4(rgb, mix(clamp(rgbExposure.b, 0.0, 1.0), 1.0, atmosphereAlpha) * smoothstep(0.0, 1.0, czm_morphTime)); + + float strength = mieFinal.b * rayleighColor.b * 0.05; + float minStrength = 0.0; + float maxStrength = 1.0; + strength = clamp(strength, minStrength, maxStrength); + float alpha = (strength - minStrength) / maxStrength; + finalColor.a = (1.0 - alpha); + + return finalColor; } From 1664cb5c57be4ee49a38051d848b864beb6c5005 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 20 May 2020 02:42:45 -0400 Subject: [PATCH 08/18] Align ellipsoid geometry to camera --- Source/Scene/SkyAtmosphere.js | 43 +++++++++++++++++++++++++---- Source/Shaders/SkyAtmosphereVS.glsl | 6 ++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index ff1234ae0c28..55ab230b644f 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -7,6 +7,8 @@ import Ellipsoid from "../Core/Ellipsoid.js"; import EllipsoidGeometry from "../Core/EllipsoidGeometry.js"; import GeometryPipeline from "../Core/GeometryPipeline.js"; import CesiumMath from "../Core/Math.js"; +import Matrix3 from "../Core/Matrix3.js"; +import Matrix4 from "../Core/Matrix4.js"; import VertexFormat from "../Core/VertexFormat.js"; import BufferUsage from "../Renderer/BufferUsage.js"; import DrawCommand from "../Renderer/DrawCommand.js"; @@ -17,6 +19,7 @@ import VertexArray from "../Renderer/VertexArray.js"; import SkyAtmosphereCommon from "../Shaders/SkyAtmosphereCommon.js"; import SkyAtmosphereFS from "../Shaders/SkyAtmosphereFS.js"; import SkyAtmosphereVS from "../Shaders/SkyAtmosphereVS.js"; +import Axis from "./Axis.js"; import BlendingState from "./BlendingState.js"; import CullFace from "./CullFace.js"; import SceneMode from "./SceneMode.js"; @@ -62,8 +65,18 @@ function SkyAtmosphere(ellipsoid) { this.perFragmentAtmosphere = true; this._ellipsoid = ellipsoid; + + var scaleVector = Cartesian3.multiplyByScalar( + ellipsoid.radii, + 1.025, + new Cartesian3() + ); + this._scaleMatrix = Matrix4.fromScale(scaleVector); + this._modelMatrix = new Matrix4(); + this._command = new DrawCommand({ owner: this, + modelMatrix: this._modelMatrix, }); this._spSkyFromSpace = undefined; this._spSkyFromAtmosphere = undefined; @@ -154,6 +167,9 @@ SkyAtmosphere.prototype.setDynamicAtmosphereColor = function ( this._radiiAndDynamicAtmosphereColorAndInverseScale.z = lightEnum; }; +var scratchRotationMatrix = new Matrix3(); +var scratchModelMatrix = new Matrix4(); + /** * @private */ @@ -172,6 +188,27 @@ SkyAtmosphere.prototype.update = function (frameState) { return undefined; } + // Align the ellipsoid geometry so it always faces the same direction as the + // camera to reduce artifacts when rendering atmosphere per-vertex + var view = frameState.context.uniformState.viewRotation; + var inverseView = Matrix3.inverse(view, scratchRotationMatrix); + var rotationMatrix = Matrix4.fromRotationTranslation( + inverseView, + Cartesian3.ZERO, + scratchModelMatrix + ); + var rotationOffsetMatrix = Matrix4.multiplyTransformation( + rotationMatrix, + Axis.Y_UP_TO_Z_UP, + scratchModelMatrix + ); + var modelMatrix = Matrix4.multiply( + this._scaleMatrix, + rotationOffsetMatrix, + scratchModelMatrix + ); + Matrix4.clone(modelMatrix, this._modelMatrix); + var context = frameState.context; var colorCorrect = hasColorCorrection(this); @@ -182,11 +219,7 @@ SkyAtmosphere.prototype.update = function (frameState) { if (!defined(command.vertexArray)) { var geometry = EllipsoidGeometry.createGeometry( new EllipsoidGeometry({ - radii: Cartesian3.multiplyByScalar( - this._ellipsoid.radii, - 1.025, - new Cartesian3() - ), + radii: new Cartesian3(1.0, 1.0, 1.0), slicePartitions: 256, stackPartitions: 256, vertexFormat: VertexFormat.POSITION_ONLY, diff --git a/Source/Shaders/SkyAtmosphereVS.glsl b/Source/Shaders/SkyAtmosphereVS.glsl index d86efa653036..3c66fd31b37d 100644 --- a/Source/Shaders/SkyAtmosphereVS.glsl +++ b/Source/Shaders/SkyAtmosphereVS.glsl @@ -9,9 +9,11 @@ varying vec3 v_rayleighColor; void main(void) { + vec4 positionWC = czm_model * position; + #ifndef PER_FRAGMENT_ATMOSPHERE - calculateMieColorAndRayleighColor(position.xyz, v_mieColor, v_rayleighColor); + calculateMieColorAndRayleighColor(positionWC.xyz, v_mieColor, v_rayleighColor); #endif - v_outerPositionWC = position.xyz; + v_outerPositionWC = positionWC.xyz; gl_Position = czm_modelViewProjection * position; } From c57fc8bef1c084d12dab73239e1a55fa2eb07190 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 20 May 2020 02:44:50 -0400 Subject: [PATCH 09/18] Default perFragmentAtmosphere to false --- CHANGES.md | 2 +- Source/Scene/SkyAtmosphere.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 198c0eb9ea64..0bac6bd527f1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ - Added `Cesium3DTileset.extensions` to get the extensions property from the tileset JSON. [#8829](https://github.com/CesiumGS/cesium/pull/8829) - Added `frustumSplits` option to `DebugCameraPrimitive`. [8849](https://github.com/CesiumGS/cesium/pull/8849) -- Added `SkyAtmosphere.perFragmentAtmosphere` to switch between per-vertex and per-fragment atmosphere shading. Atmosphere is shaded per-fragment by default which looks better but may cause a slight performance penalty. [#8866](https://github.com/CesiumGS/cesium/pull/8866) +- Added `SkyAtmosphere.perFragmentAtmosphere` to switch between per-vertex and per-fragment atmosphere shading. [#8866](https://github.com/CesiumGS/cesium/pull/8866) ##### Fixes :wrench: diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 55ab230b644f..4e34e549b8e8 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -60,9 +60,9 @@ function SkyAtmosphere(ellipsoid) { * This produces better looking atmosphere with a slight performance penalty. * * @type {Boolean} - * @default true + * @default false */ - this.perFragmentAtmosphere = true; + this.perFragmentAtmosphere = false; this._ellipsoid = ellipsoid; From 42d08d590d3e081ea28df8477f23d59a6d4e0f5b Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 21 May 2020 15:32:02 -0400 Subject: [PATCH 10/18] Same blueness everywhere --- Source/Scene/SkyAtmosphere.js | 5 ++--- Source/Shaders/GroundAtmosphere.glsl | 2 +- Source/Shaders/SkyAtmosphereCommon.glsl | 24 +++++++++++++++--------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 4e34e549b8e8..0dddd4bd161a 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -112,9 +112,8 @@ function SkyAtmosphere(ellipsoid) { // outer radius, inner radius, dynamic atmosphere color flag, inverse scale var radiiAndDynamicAtmosphereColorAndInverseScale = new Cartesian4(); - radiiAndDynamicAtmosphereColorAndInverseScale.x = Cartesian3.maximumComponent( - Cartesian3.multiplyByScalar(ellipsoid.radii, 1.025, new Cartesian3()) - ); + radiiAndDynamicAtmosphereColorAndInverseScale.x = + ellipsoid.maximumRadius * 1.025; radiiAndDynamicAtmosphereColorAndInverseScale.y = ellipsoid.maximumRadius; // Toggles whether the sun position is used. 0 treats the sun as always directly overhead. diff --git a/Source/Shaders/GroundAtmosphere.glsl b/Source/Shaders/GroundAtmosphere.glsl index 1e11d2d3e9fb..36c15058b766 100644 --- a/Source/Shaders/GroundAtmosphere.glsl +++ b/Source/Shaders/GroundAtmosphere.glsl @@ -78,7 +78,7 @@ AtmosphereColor computeGroundAtmosphereFromSpace(vec3 v3Pos, bool dynamicLightin float fFar = length(v3Ray); v3Ray /= fFar; - float fCameraHeight = length(czm_viewerPositionWC); + float fCameraHeight = czm_eyeHeight + fInnerRadius; float fCameraHeight2 = fCameraHeight * fCameraHeight; // This next line is an ANGLE workaround. It is equivalent to B = 2.0 * dot(czm_viewerPositionWC, v3Ray), diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index 810652e96b20..217907c8a695 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -74,17 +74,19 @@ vec3 getLightDirection(vec3 positionWC) return normalize(lightDirection); } -void calculateRayScatteringFromSpace(in vec3 positionWC, in vec3 ray, in float outerRadius, inout float far, out vec3 start, out float startOffset) +void calculateRayScatteringFromSpace(in vec3 positionWC, in vec3 ray, in float innerRadius, in float outerRadius, inout float far, out vec3 start, out float startOffset) { // Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere) - float cameraHeight = length(positionWC); - float B = 2.0 * dot(positionWC, ray); + float cameraHeight = czm_eyeHeight + innerRadius; + vec3 adjustedPositionWC = normalize(positionWC) * cameraHeight; + + float B = 2.0 * dot(adjustedPositionWC, ray); float C = cameraHeight * cameraHeight - outerRadius * outerRadius; float det = max(0.0, B * B - 4.0 * C); float near = 0.5 * (-B - sqrt(det)); // Calculate the ray's starting position, then calculate its scattering offset - start = positionWC + ray * near; + start = adjustedPositionWC + ray * near; far -= near; float startAngle = dot(ray, start) / outerRadius; float startDepth = exp(-1.0 / rayleighScaleDepth); @@ -94,8 +96,8 @@ void calculateRayScatteringFromSpace(in vec3 positionWC, in vec3 ray, in float o void calculateRayScatteringFromGround(in vec3 positionWC, in vec3 ray, in float atmosphereScale, in float innerRadius, out vec3 start, out float startOffset) { // Calculate the ray's starting position, then calculate its scattering offset - float cameraHeight = length(positionWC); - start = positionWC; + float cameraHeight = czm_eyeHeight + innerRadius; + start = normalize(positionWC) * cameraHeight; float height = length(start); float depth = exp((atmosphereScale / rayleighScaleDepth ) * (innerRadius - cameraHeight)); float startAngle = dot(ray, start) / height; @@ -173,7 +175,7 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, } else { - calculateRayScatteringFromSpace(startPositionWC, ray, outerRadius, far, start, startOffset); + calculateRayScatteringFromSpace(startPositionWC, ray, innerRadius, outerRadius, far, start, startOffset); } #else calculateRayScatteringFromGround(startPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); @@ -244,11 +246,15 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve float innerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.y; float lightEnum = u_radiiAndDynamicAtmosphereColorAndInverseScale.z; + float cameraHeight = czm_eyeHeight + innerRadius; + vec3 adjustedPositionWC = normalize(positionWC) * cameraHeight; + + // Alter alpha based on how close the viewer is to the ground (1.0 = on ground, 0.0 = at edge of atmosphere) - float atmosphereAlpha = clamp((outerRadius - length(positionWC)) / (outerRadius - innerRadius), 0.0, 1.0); + float atmosphereAlpha = clamp((outerRadius - length(adjustedPositionWC)) / (outerRadius - innerRadius), 0.0, 1.0); // Alter alpha based on time of day (0.0 = night , 1.0 = day) - float nightAlpha = (lightEnum != 0.0) ? clamp(dot(normalize(positionWC), lightDirection), 0.0, 1.0) : 1.0; + float nightAlpha = (lightEnum != 0.0) ? clamp(dot(normalize(adjustedPositionWC), lightDirection), 0.0, 1.0) : 1.0; atmosphereAlpha *= pow(nightAlpha, 0.5); vec4 finalColor = vec4(rgb, mix(clamp(rgbExposure.b, 0.0, 1.0), 1.0, atmosphereAlpha) * smoothstep(0.0, 1.0, czm_morphTime)); From d086bdf06d0768777f973621697898db56dfc52e Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 21 May 2020 21:39:33 -0400 Subject: [PATCH 11/18] Improve alpha --- Source/Shaders/SkyAtmosphereCommon.glsl | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index 217907c8a695..5055925961fc 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -205,6 +205,10 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader mieColor = frontColor * KmESun; rayleighColor = frontColor * (InvWavelength * KrESun); + + mieColor = min(mieColor, vec3(100.0)); + rayleighColor = min(rayleighColor, vec3(100.0)); + } vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, vec3 mieColor, vec3 rayleighColor) @@ -218,12 +222,6 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve vec3 mieFinal = miePhase * mieColor; vec3 rgb = rayleighFinal + mieFinal; - if (rgb.b > 1000000.0) - { - // Discard colors that exceed some large number value to prevent against NaN's from the exponent calculation below - return vec4(0.0); - } - const float exposure = 2.0; vec3 rgbExposure = vec3(1.0) - exp(-exposure * rgb); @@ -249,7 +247,6 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve float cameraHeight = czm_eyeHeight + innerRadius; vec3 adjustedPositionWC = normalize(positionWC) * cameraHeight; - // Alter alpha based on how close the viewer is to the ground (1.0 = on ground, 0.0 = at edge of atmosphere) float atmosphereAlpha = clamp((outerRadius - length(adjustedPositionWC)) / (outerRadius - innerRadius), 0.0, 1.0); @@ -259,12 +256,15 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve vec4 finalColor = vec4(rgb, mix(clamp(rgbExposure.b, 0.0, 1.0), 1.0, atmosphereAlpha) * smoothstep(0.0, 1.0, czm_morphTime)); - float strength = mieFinal.b * rayleighColor.b * 0.05; - float minStrength = 0.0; - float maxStrength = 1.0; - strength = clamp(strength, minStrength, maxStrength); - float alpha = (strength - minStrength) / maxStrength; - finalColor.a = (1.0 - alpha); + if (mieColor.b > 1.0) + { + float strength = mieFinal.b * rayleighFinal.b * 0.05; + float minStrength = 0.0; + float maxStrength = 1.0; + strength = clamp(strength, minStrength, maxStrength); + float alpha = 1.0 - (strength - minStrength) / (maxStrength - minStrength); + finalColor.a = alpha; + } return finalColor; } From 53d264d921e979f9db9840f831b25b433010a07e Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 21 May 2020 22:54:40 -0400 Subject: [PATCH 12/18] Tweak brightness by fade distance --- Source/Shaders/SkyAtmosphereCommon.glsl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index 5055925961fc..cecadc2146f0 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -206,8 +206,9 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, mieColor = frontColor * KmESun; rayleighColor = frontColor * (InvWavelength * KrESun); - mieColor = min(mieColor, vec3(100.0)); - rayleighColor = min(rayleighColor, vec3(100.0)); + // Cap mie and rayleigh colors to prevent NaNs when vertex interpolation happens + mieColor = min(mieColor, vec3(10000000.0)); + rayleighColor = min(rayleighColor, vec3(10000000.0)); } @@ -245,24 +246,27 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve float lightEnum = u_radiiAndDynamicAtmosphereColorAndInverseScale.z; float cameraHeight = czm_eyeHeight + innerRadius; - vec3 adjustedPositionWC = normalize(positionWC) * cameraHeight; // Alter alpha based on how close the viewer is to the ground (1.0 = on ground, 0.0 = at edge of atmosphere) - float atmosphereAlpha = clamp((outerRadius - length(adjustedPositionWC)) / (outerRadius - innerRadius), 0.0, 1.0); + float atmosphereAlpha = clamp((outerRadius - cameraHeight) / (outerRadius - innerRadius), 0.0, 1.0); // Alter alpha based on time of day (0.0 = night , 1.0 = day) - float nightAlpha = (lightEnum != 0.0) ? clamp(dot(normalize(adjustedPositionWC), lightDirection), 0.0, 1.0) : 1.0; + float nightAlpha = (lightEnum != 0.0) ? clamp(dot(normalize(positionWC), lightDirection), 0.0, 1.0) : 1.0; atmosphereAlpha *= pow(nightAlpha, 0.5); vec4 finalColor = vec4(rgb, mix(clamp(rgbExposure.b, 0.0, 1.0), 1.0, atmosphereAlpha) * smoothstep(0.0, 1.0, czm_morphTime)); if (mieColor.b > 1.0) { - float strength = mieFinal.b * rayleighFinal.b * 0.05; - float minStrength = 0.0; - float maxStrength = 1.0; - strength = clamp(strength, minStrength, maxStrength); - float alpha = 1.0 - (strength - minStrength) / (maxStrength - minStrength); + // Fade atmosphere below the ellipsoid. As the camera zooms further away from the ellipsoid draw + // a larger atmosphere ring to cover empty space of lower LOD globe tiles. + float strength = mieColor.b; + float minDistance = outerRadius; + float maxDistance = outerRadius * 3.0; + float maxStrengthLerp = 1.0 - clamp((maxDistance - cameraHeight) / (maxDistance - minDistance), 0.0, 1.0); + float maxStrength = mix(100.0, 10000000.0, maxStrengthLerp); + strength = min(strength, maxStrength); + float alpha = 1.0 - (strength / maxStrength); finalColor.a = alpha; } From f755ea6733538fd06c5d51dc3e18821ab3715453 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 21 May 2020 22:58:43 -0400 Subject: [PATCH 13/18] Revert ground atmosphere change to fix issues when zoomed out really far --- Source/Shaders/GroundAtmosphere.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/GroundAtmosphere.glsl b/Source/Shaders/GroundAtmosphere.glsl index 36c15058b766..1e11d2d3e9fb 100644 --- a/Source/Shaders/GroundAtmosphere.glsl +++ b/Source/Shaders/GroundAtmosphere.glsl @@ -78,7 +78,7 @@ AtmosphereColor computeGroundAtmosphereFromSpace(vec3 v3Pos, bool dynamicLightin float fFar = length(v3Ray); v3Ray /= fFar; - float fCameraHeight = czm_eyeHeight + fInnerRadius; + float fCameraHeight = length(czm_viewerPositionWC); float fCameraHeight2 = fCameraHeight * fCameraHeight; // This next line is an ANGLE workaround. It is equivalent to B = 2.0 * dot(czm_viewerPositionWC, v3Ray), From 3c9af3ef27b3979d56fac49106156d928258dfa7 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 21 May 2020 23:02:05 -0400 Subject: [PATCH 14/18] Updates from review --- Source/Scene/SkyAtmosphere.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 0dddd4bd161a..327dd0715ce0 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -166,7 +166,6 @@ SkyAtmosphere.prototype.setDynamicAtmosphereColor = function ( this._radiiAndDynamicAtmosphereColorAndInverseScale.z = lightEnum; }; -var scratchRotationMatrix = new Matrix3(); var scratchModelMatrix = new Matrix4(); /** @@ -189,10 +188,8 @@ SkyAtmosphere.prototype.update = function (frameState) { // Align the ellipsoid geometry so it always faces the same direction as the // camera to reduce artifacts when rendering atmosphere per-vertex - var view = frameState.context.uniformState.viewRotation; - var inverseView = Matrix3.inverse(view, scratchRotationMatrix); var rotationMatrix = Matrix4.fromRotationTranslation( - inverseView, + frameState.context.uniformState.inverseViewRotation, Cartesian3.ZERO, scratchModelMatrix ); From 1234027329bcaad8dca9d2f16a54b7201961cb71 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 21 May 2020 23:09:18 -0400 Subject: [PATCH 15/18] Remove old intersection code --- Source/Scene/SkyAtmosphere.js | 27 +++----- Source/Shaders/SkyAtmosphereCommon.glsl | 84 +++++-------------------- Specs/Scene/SkyAtmosphereSpec.js | 6 +- 3 files changed, 27 insertions(+), 90 deletions(-) diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 327dd0715ce0..71dc233b0322 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -1,5 +1,4 @@ import Cartesian3 from "../Core/Cartesian3.js"; -import Cartesian4 from "../Core/Cartesian4.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; @@ -7,7 +6,6 @@ import Ellipsoid from "../Core/Ellipsoid.js"; import EllipsoidGeometry from "../Core/EllipsoidGeometry.js"; import GeometryPipeline from "../Core/GeometryPipeline.js"; import CesiumMath from "../Core/Math.js"; -import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import VertexFormat from "../Core/VertexFormat.js"; import BufferUsage from "../Renderer/BufferUsage.js"; @@ -109,27 +107,22 @@ function SkyAtmosphere(ellipsoid) { this._hueSaturationBrightness = new Cartesian3(); - // outer radius, inner radius, dynamic atmosphere color flag, inverse scale - var radiiAndDynamicAtmosphereColorAndInverseScale = new Cartesian4(); + // outer radius, inner radius, dynamic atmosphere color flag + var radiiAndDynamicAtmosphereColor = new Cartesian3(); - radiiAndDynamicAtmosphereColorAndInverseScale.x = - ellipsoid.maximumRadius * 1.025; - radiiAndDynamicAtmosphereColorAndInverseScale.y = ellipsoid.maximumRadius; + radiiAndDynamicAtmosphereColor.x = ellipsoid.maximumRadius * 1.025; + radiiAndDynamicAtmosphereColor.y = ellipsoid.maximumRadius; // Toggles whether the sun position is used. 0 treats the sun as always directly overhead. - radiiAndDynamicAtmosphereColorAndInverseScale.z = 0; + radiiAndDynamicAtmosphereColor.z = 0; - // Controls the distance below the horizon at which atmosphere transitions from its brightest to a more neutral blue color - // Higher values push the transition point further below the horizon - radiiAndDynamicAtmosphereColorAndInverseScale.w = 1.003; - - this._radiiAndDynamicAtmosphereColorAndInverseScale = radiiAndDynamicAtmosphereColorAndInverseScale; + this._radiiAndDynamicAtmosphereColor = radiiAndDynamicAtmosphereColor; var that = this; this._command.uniformMap = { - u_radiiAndDynamicAtmosphereColorAndInverseScale: function () { - return that._radiiAndDynamicAtmosphereColorAndInverseScale; + u_radiiAndDynamicAtmosphereColor: function () { + return that._radiiAndDynamicAtmosphereColor; }, u_hsbShift: function () { that._hueSaturationBrightness.x = that.hueShift; @@ -163,7 +156,7 @@ SkyAtmosphere.prototype.setDynamicAtmosphereColor = function ( useSunDirection ) { var lightEnum = enableLighting ? (useSunDirection ? 2.0 : 1.0) : 0.0; - this._radiiAndDynamicAtmosphereColorAndInverseScale.z = lightEnum; + this._radiiAndDynamicAtmosphereColor.z = lightEnum; }; var scratchModelMatrix = new Matrix4(); @@ -288,7 +281,7 @@ SkyAtmosphere.prototype.update = function (frameState) { var cameraPosition = frameState.camera.positionWC; var cameraHeight = Cartesian3.magnitude(cameraPosition); - if (cameraHeight > this._radiiAndDynamicAtmosphereColorAndInverseScale.x) { + if (cameraHeight > this._radiiAndDynamicAtmosphereColor.x) { // Camera in space command.shaderProgram = this._spSkyFromSpace; } else { diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index cecadc2146f0..b5a591e5557c 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -56,7 +56,7 @@ const float g2 = g * g; uniform vec3 u_hsbShift; // Hue, saturation, brightness #endif -uniform vec4 u_radiiAndDynamicAtmosphereColorAndInverseScale; // outer radius, inner radius, dynamic atmosphere color flag, inverse scale +uniform vec3 u_radiiAndDynamicAtmosphereColor; // outer radius, inner radius, dynamic atmosphere color flag float scale(float cosAngle) { @@ -66,7 +66,7 @@ float scale(float cosAngle) vec3 getLightDirection(vec3 positionWC) { - float lightEnum = u_radiiAndDynamicAtmosphereColorAndInverseScale.z; + float lightEnum = u_radiiAndDynamicAtmosphereColor.z; vec3 lightDirection = positionWC * float(lightEnum == 0.0) + czm_lightDirectionWC * float(lightEnum == 1.0) + @@ -104,64 +104,18 @@ void calculateRayScatteringFromGround(in vec3 positionWC, in vec3 ray, in float startOffset = depth*scale(startAngle); } -czm_raySegment rayEllipsoidIntersection(czm_ray ray, vec3 ellipsoid_center, vec3 ellipsoid_inverseRadii) -{ - vec3 o = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.origin, 1.0)).xyz; - vec3 d = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.direction, 0.0)).xyz; - - float a = dot(d, d); - float b = dot(d, o); - float c = dot(o, o) - 1.0; - float discriminant = b * b - a * c; - if (discriminant < 0.0) - { - return czm_emptyRaySegment; - } - discriminant = sqrt(discriminant); - float t1 = (-b - discriminant) / a; - float t2 = (-b + discriminant) / a; - - if (t1 < 0.0 && t2 < 0.0) - { - return czm_emptyRaySegment; - } - - if (t1 < 0.0 && t2 >= 0.0) - { - t1 = 0.0; - } - - return czm_raySegment(t1, t2); -} - void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, out vec3 rayleighColor) { // Unpack attributes - float outerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.x; - float innerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.y; - float inverseScale = u_radiiAndDynamicAtmosphereColorAndInverseScale.w; - - vec3 directionWC = normalize(outerPositionWC - czm_viewerPositionWC); - vec3 directionEC = czm_viewRotation * directionWC; - czm_ray viewRay = czm_ray(vec3(0.0), directionEC); - czm_raySegment raySegment = rayEllipsoidIntersection(viewRay, vec3(czm_view[3]), czm_ellipsoidInverseRadii * inverseScale); - bool intersectsEllipsoid = false;//raySegment.start >= 0.0; - - vec3 startPositionWC = czm_viewerPositionWC; - if (intersectsEllipsoid) - { - startPositionWC = czm_viewerPositionWC + raySegment.stop * directionWC; - } + float outerRadius = u_radiiAndDynamicAtmosphereColor.x; + float innerRadius = u_radiiAndDynamicAtmosphereColor.y; - vec3 lightDirection = getLightDirection(startPositionWC); + vec3 lightDirection = getLightDirection(czm_viewerPositionWC); // Get the ray from the start position to the outer position and its length (which is the far point of the ray passing through the atmosphere) - vec3 ray = outerPositionWC - startPositionWC; - float distance = length(ray); - ray /= distance; - - float maxDistance = intersectsEllipsoid ? innerRadius * 0.1 : distance; - float far = min(distance, maxDistance); + vec3 ray = outerPositionWC - czm_viewerPositionWC; + float far = length(ray); + ray /= far; float atmosphereScale = 1.0 / (outerRadius - innerRadius); @@ -169,16 +123,9 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, float startOffset; #ifdef SKY_FROM_SPACE - if (intersectsEllipsoid) - { - calculateRayScatteringFromGround(startPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); - } - else - { - calculateRayScatteringFromSpace(startPositionWC, ray, innerRadius, outerRadius, far, start, startOffset); - } + calculateRayScatteringFromSpace(czm_viewerPositionWC, ray, innerRadius, outerRadius, far, start, startOffset); #else - calculateRayScatteringFromGround(startPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); + calculateRayScatteringFromGround(czm_viewerPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); #endif // Initialize the scattering loop variables @@ -209,7 +156,6 @@ void calculateMieColorAndRayleighColor(vec3 outerPositionWC, out vec3 mieColor, // Cap mie and rayleigh colors to prevent NaNs when vertex interpolation happens mieColor = min(mieColor, vec3(10000000.0)); rayleighColor = min(rayleighColor, vec3(10000000.0)); - } vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, vec3 mieColor, vec3 rayleighColor) @@ -219,9 +165,7 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve float rayleighPhase = 0.75 * (1.0 + cosAngle * cosAngle); float miePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cosAngle * cosAngle) / pow(1.0 + g2 - 2.0 * g * cosAngle, 1.5); - vec3 rayleighFinal = rayleighPhase * rayleighColor; - vec3 mieFinal = miePhase * mieColor; - vec3 rgb = rayleighFinal + mieFinal; + vec3 rgb = rayleighPhase * rayleighColor + miePhase * mieColor; const float exposure = 2.0; vec3 rgbExposure = vec3(1.0) - exp(-exposure * rgb); @@ -241,9 +185,9 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve rgb = czm_HSBToRGB(hsb); #endif - float outerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.x; - float innerRadius = u_radiiAndDynamicAtmosphereColorAndInverseScale.y; - float lightEnum = u_radiiAndDynamicAtmosphereColorAndInverseScale.z; + float outerRadius = u_radiiAndDynamicAtmosphereColor.x; + float innerRadius = u_radiiAndDynamicAtmosphereColor.y; + float lightEnum = u_radiiAndDynamicAtmosphereColor.z; float cameraHeight = czm_eyeHeight + innerRadius; diff --git a/Specs/Scene/SkyAtmosphereSpec.js b/Specs/Scene/SkyAtmosphereSpec.js index df930a233d78..016f32779427 100644 --- a/Specs/Scene/SkyAtmosphereSpec.js +++ b/Specs/Scene/SkyAtmosphereSpec.js @@ -57,7 +57,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._radiiAndDynamicAtmosphereColorAndInverseScale.z).toBe(1); + expect(s._radiiAndDynamicAtmosphereColor.z).toBe(1); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); @@ -72,7 +72,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._radiiAndDynamicAtmosphereColorAndInverseScale.z).toBe(2); + expect(s._radiiAndDynamicAtmosphereColor.z).toBe(2); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); @@ -87,7 +87,7 @@ describe( var command = s.update(scene.frameState); expect(command).toBeDefined(); - expect(s._radiiAndDynamicAtmosphereColorAndInverseScale.z).toBe(0); + expect(s._radiiAndDynamicAtmosphereColor.z).toBe(0); command.execute(scene.context); // Not reliable enough across browsers to test pixels s.destroy(); From 0a422f18e3a22a037eafb8ee1f1308d59696b5e0 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Thu, 21 May 2020 23:26:15 -0400 Subject: [PATCH 16/18] Fix lighting crash --- Source/Shaders/GlobeFS.glsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Shaders/GlobeFS.glsl b/Source/Shaders/GlobeFS.glsl index 50cec0797269..0d16cd51290a 100644 --- a/Source/Shaders/GlobeFS.glsl +++ b/Source/Shaders/GlobeFS.glsl @@ -458,8 +458,8 @@ vec3 computeGroundAtmosphereColor(vec3 fogColor, vec4 finalColor, vec3 atmospher groundAtmosphereColor = vec3(1.0) - exp(-fExposure * groundAtmosphereColor); #endif - fadeInDist = u_nightFadeDistance.x; - fadeOutDist = u_nightFadeDistance.y; + float fadeInDist = u_nightFadeDistance.x; + float fadeOutDist = u_nightFadeDistance.y; float sunlitAtmosphereIntensity = clamp((cameraDist - fadeOutDist) / (fadeInDist - fadeOutDist), 0.0, 1.0); From 64b21a3ff27c789ccfb3713dc982244cc1bca8d7 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 22 May 2020 10:33:59 -0400 Subject: [PATCH 17/18] Tweak strength to prevent ring artifact in enableLighting mode --- Source/Shaders/SkyAtmosphereCommon.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index b5a591e5557c..af5463456892 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -208,7 +208,7 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve float minDistance = outerRadius; float maxDistance = outerRadius * 3.0; float maxStrengthLerp = 1.0 - clamp((maxDistance - cameraHeight) / (maxDistance - minDistance), 0.0, 1.0); - float maxStrength = mix(100.0, 10000000.0, maxStrengthLerp); + float maxStrength = mix(100.0, 10000.0, maxStrengthLerp); strength = min(strength, maxStrength); float alpha = 1.0 - (strength / maxStrength); finalColor.a = alpha; From c157fd3bf4a5d34efbb5a86050aab1356031b04d Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Mon, 25 May 2020 11:32:26 -0400 Subject: [PATCH 18/18] Define constant once --- Source/Scene/SkyAtmosphere.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 71dc233b0322..583dd73e49b2 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -64,9 +64,10 @@ function SkyAtmosphere(ellipsoid) { this._ellipsoid = ellipsoid; + var outerEllipsoidScale = 1.025; var scaleVector = Cartesian3.multiplyByScalar( ellipsoid.radii, - 1.025, + outerEllipsoidScale, new Cartesian3() ); this._scaleMatrix = Matrix4.fromScale(scaleVector); @@ -110,7 +111,8 @@ function SkyAtmosphere(ellipsoid) { // outer radius, inner radius, dynamic atmosphere color flag var radiiAndDynamicAtmosphereColor = new Cartesian3(); - radiiAndDynamicAtmosphereColor.x = ellipsoid.maximumRadius * 1.025; + radiiAndDynamicAtmosphereColor.x = + ellipsoid.maximumRadius * outerEllipsoidScale; radiiAndDynamicAtmosphereColor.y = ellipsoid.maximumRadius; // Toggles whether the sun position is used. 0 treats the sun as always directly overhead.