diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 0fe9281af64a..4e7508f16639 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -633,7 +633,6 @@ function Scene(options) { isSunVisible: false, isMoonVisible: false, - isReadyForAtmosphere: false, isSkyAtmosphereVisible: false, clearGlobeDepth: false, @@ -3311,11 +3310,11 @@ Scene.prototype.updateEnvironment = function () { globe.enableLighting && globe.dynamicAtmosphereLighting, globe.dynamicAtmosphereLightingFromSun ); - environmentState.isReadyForAtmosphere = - environmentState.isReadyForAtmosphere || - globe._surface._tilesToRender.length > 0; } - environmentState.skyAtmosphereCommand = skyAtmosphere.update(frameState); + environmentState.skyAtmosphereCommand = skyAtmosphere.update( + frameState, + this._globe + ); if (defined(environmentState.skyAtmosphereCommand)) { this.updateDerivedCommands(environmentState.skyAtmosphereCommand); } @@ -3342,6 +3341,7 @@ Scene.prototype.updateEnvironment = function () { var clearGlobeDepth = (environmentState.clearGlobeDepth = defined(globe) && + globe.show && (!globe.depthTestAgainstTerrain || this.mode === SceneMode.SCENE2D)); var useDepthPlane = (environmentState.useDepthPlane = clearGlobeDepth && @@ -3373,9 +3373,9 @@ Scene.prototype.updateEnvironment = function () { cullingVolume = scratchCullingVolume; // Determine visibility of celestial and terrestrial environment effects. - environmentState.isSkyAtmosphereVisible = - defined(environmentState.skyAtmosphereCommand) && - environmentState.isReadyForAtmosphere; + environmentState.isSkyAtmosphereVisible = defined( + environmentState.skyAtmosphereCommand + ); environmentState.isSunVisible = this.isVisible( environmentState.sunDrawCommand, cullingVolume, diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 42aec545921a..95a8941600ba 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.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, @@ -142,7 +151,7 @@ SkyAtmosphere.prototype.setDynamicAtmosphereColor = function ( /** * @private */ -SkyAtmosphere.prototype.update = function (frameState) { +SkyAtmosphere.prototype.update = function (frameState, globe) { if (!this.show) { return undefined; } @@ -160,7 +169,12 @@ SkyAtmosphere.prototype.update = function (frameState) { var context = frameState.context; var colorCorrect = hasColorCorrection(this); - var globeTranslucent = frameState.globeTranslucencyState.translucent; + var translucent = frameState.globeTranslucencyState.translucent; + var perFragmentAtmosphere = + this.perFragmentAtmosphere || + translucent || + (defined(globe) && + (!globe.show || globe._surface._tilesToRender.length === 0)); var command = this._command; @@ -193,7 +207,7 @@ SkyAtmosphere.prototype.update = function (frameState) { }); } - var flags = colorCorrect | (globeTranslucent << 2); + var flags = colorCorrect | (perFragmentAtmosphere << 2); if (flags !== this._flags) { this._flags = flags; @@ -204,8 +218,8 @@ SkyAtmosphere.prototype.update = function (frameState) { defines.push("COLOR_CORRECT"); } - if (globeTranslucent) { - defines.push("GLOBE_TRANSLUCENT"); + if (perFragmentAtmosphere) { + defines.push("PER_FRAGMENT_ATMOSPHERE"); } var vs = new ShaderSource({ diff --git a/Source/Shaders/SkyAtmosphereCommon.glsl b/Source/Shaders/SkyAtmosphereCommon.glsl index 7e27cd4304ac..31ee34403ee2 100644 --- a/Source/Shaders/SkyAtmosphereCommon.glsl +++ b/Source/Shaders/SkyAtmosphereCommon.glsl @@ -102,16 +102,58 @@ void calculateRayScatteringFromGround(in vec3 positionWC, in vec3 ray, in float startOffset = depth*scale(startAngle); } -void calculateMieColorAndRayleighColor(vec3 positionWC, vec3 outerPosition, vec3 lightDirection, bool intersectsEllipsoid, out vec3 mieColor, out vec3 rayleighColor) +czm_raySegment rayEllipsoidIntersection(czm_ray ray, vec3 ellipsoid_center, vec3 ellipsoid_inverseRadii) { - float cameraHeight = length(positionWC); + 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 = outerPosition - positionWC; + vec3 ray = outerPositionWC - startPositionWC; float far = length(ray); ray /= far; float atmosphereScale = 1.0 / (outerRadius - innerRadius); @@ -122,14 +164,14 @@ void calculateMieColorAndRayleighColor(vec3 positionWC, vec3 outerPosition, vec3 #ifdef SKY_FROM_SPACE if (intersectsEllipsoid) { - calculateRayScatteringFromGround(positionWC, ray, atmosphereScale, innerRadius, start, startOffset); + calculateRayScatteringFromGround(startPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); } else { - calculateRayScatteringFromSpace(positionWC, ray, outerRadius, far, start, startOffset); + calculateRayScatteringFromSpace(startPositionWC, ray, outerRadius, far, start, startOffset); } #else - calculateRayScatteringFromGround(positionWC, ray, atmosphereScale, innerRadius, start, startOffset); + calculateRayScatteringFromGround(startPositionWC, ray, atmosphereScale, innerRadius, start, startOffset); #endif // Initialize the scattering loop variables @@ -167,12 +209,6 @@ vec4 calculateFinalColor(vec3 positionWC, vec3 toCamera, vec3 lightDirection, ve vec3 rgb = rayleighPhase * rayleighColor + miePhase * mieColor; - 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); diff --git a/Source/Shaders/SkyAtmosphereFS.glsl b/Source/Shaders/SkyAtmosphereFS.glsl index bec83d6da4a9..ee2f6eb37f2d 100644 --- a/Source/Shaders/SkyAtmosphereFS.glsl +++ b/Source/Shaders/SkyAtmosphereFS.glsl @@ -35,49 +35,21 @@ varying vec3 v_outerPositionWC; -#ifndef GLOBE_TRANSLUCENT +#ifndef PER_FRAGMENT_ATMOSPHERE varying vec3 v_rayleighColor; varying vec3 v_mieColor; #endif -// Enlarge the ellipsoid slightly to avoid atmosphere artifacts when the camera is slightly below the ellipsoid -const float epsilon = 1.000001; - void main (void) { -#ifdef GLOBE_TRANSLUCENT - vec3 outerPositionWC = v_outerPositionWC; - vec3 directionWC = normalize(outerPositionWC - czm_viewerPositionWC); - vec3 directionEC = czm_viewRotation * directionWC; - czm_ray viewRay = czm_ray(vec3(0.0), directionEC); - czm_raySegment raySegment = czm_rayEllipsoidIntersectionInterval(viewRay, vec3(czm_view[3]), czm_ellipsoidInverseRadii * epsilon); - bool intersectsEllipsoid = raySegment.start >= 0.0; - - vec3 startPositionWC = czm_viewerPositionWC; - if (intersectsEllipsoid) - { - startPositionWC = czm_viewerPositionWC + raySegment.stop * directionWC; - } - - vec3 toCamera = startPositionWC - outerPositionWC; - vec3 lightDirection = getLightDirection(startPositionWC); - + vec3 toCamera = czm_viewerPositionWC - v_outerPositionWC; + vec3 lightDirection = getLightDirection(czm_viewerPositionWC); +#ifdef PER_FRAGMENT_ATMOSPHERE vec3 mieColor; vec3 rayleighColor; - - calculateMieColorAndRayleighColor( - startPositionWC, - outerPositionWC, - lightDirection, - intersectsEllipsoid, - mieColor, - rayleighColor - ); - - gl_FragColor = calculateFinalColor(startPositionWC, toCamera, lightDirection, rayleighColor, mieColor); + calculateMieColorAndRayleighColor(v_outerPositionWC, 1.0, mieColor, rayleighColor); + gl_FragColor = calculateFinalColor(czm_viewerPositionWC, toCamera, lightDirection, rayleighColor, mieColor); #else - vec3 toCamera = czm_viewerPositionWC - v_outerPositionWC; - vec3 lightDirection = getLightDirection(czm_viewerPositionWC); gl_FragColor = calculateFinalColor(czm_viewerPositionWC, toCamera, lightDirection, v_rayleighColor, v_mieColor); #endif } diff --git a/Source/Shaders/SkyAtmosphereVS.glsl b/Source/Shaders/SkyAtmosphereVS.glsl index 68776fa605bb..8fd95bb1e581 100644 --- a/Source/Shaders/SkyAtmosphereVS.glsl +++ b/Source/Shaders/SkyAtmosphereVS.glsl @@ -37,22 +37,15 @@ attribute vec4 position; varying vec3 v_outerPositionWC; -#ifndef GLOBE_TRANSLUCENT +#ifndef PER_FRAGMENT_ATMOSPHERE varying vec3 v_rayleighColor; varying vec3 v_mieColor; #endif void main(void) { -#ifndef GLOBE_TRANSLUCENT - calculateMieColorAndRayleighColor( - czm_viewerPositionWC, - position.xyz, - getLightDirection(czm_viewerPositionWC), - false, - v_mieColor, - v_rayleighColor - ); +#ifndef PER_FRAGMENT_ATMOSPHERE + calculateMieColorAndRayleighColor(position.xyz, 1.002, v_mieColor, v_rayleighColor); #endif v_outerPositionWC = position.xyz; gl_Position = czm_modelViewProjection * position;