diff --git a/CHANGES.md b/CHANGES.md index dc89a88e91b..fc46763846e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,14 @@ Change Log ### 1.45 - 2018-05-01 +##### Breaking Changes :mega: +* `Camera.distanceToBoundingSphere` now returns the signed distance to the bounding sphere. Positive values indicate that the bounding sphere is in the positive half-plane of the camera position and view direction while a negative value indicates it is in the negative half-plane. + +##### Additions :tada: +* Added option `logDepthBuffer` to `Viewer`. With this option there is typically a single frustum using logarithmic depth rendered. This increases performance by issuing less draw calls to the GPU and helps to avoid artifacts on the connection of two frustums. [#5851](https://github.com/AnalyticalGraphicsInc/cesium/pull/5851) +* When a log depth buffer is supported, the frustum near and far planes default to `0.1` and `1e10` respectively. +* Added `Math.log2` to compute the base 2 logarithm of a number. + ##### Fixes :wrench: * Fixed bugs in `TimeIntervalCollection.removeInterval`. [#6418](https://github.com/AnalyticalGraphicsInc/cesium/pull/6418). * Fixed glTF support to handle meshes with and without tangent vectors, and with/without morph targets, sharing one material. [#6421](https://github.com/AnalyticalGraphicsInc/cesium/pull/6421) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6e7bb89fe51..6d86fc59fdf 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -73,6 +73,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu * [Dave Whipps](https://github.com/dwhipps) * [Geoscan](https://www.geoscan.aero) * [Andrey Orlov](https://github.com/AndreyOrlov) + * [George Vinokhodov](https://github.com/Vineg) * [The Imagineers](https://www.theimagineers.com/) * [Heerco Grond](https://github.com/HeercoGrond) * [Camptocamp SA](https://www.camptocamp.com/) diff --git a/Source/Core/Math.js b/Source/Core/Math.js index 04631d63609..a498300c9ac 100644 --- a/Source/Core/Math.js +++ b/Source/Core/Math.js @@ -849,6 +849,18 @@ define([ */ CesiumMath.cbrt = defined(Math.cbrt) ? Math.cbrt : cbrt; + function log2(x) { + return Math.log(x) * Math.LOG2E; + } + + /** + * Finds the base 2 logarithm of a number. + * + * @param {Number} number The number. + * @returns {Number} The result. + */ + CesiumMath.log2 = defined(Math.log2) ? Math.log2 : log2; + /** * @private */ diff --git a/Source/Core/OrthographicFrustum.js b/Source/Core/OrthographicFrustum.js index b3377b99fde..b2dd5ef7e07 100644 --- a/Source/Core/OrthographicFrustum.js +++ b/Source/Core/OrthographicFrustum.js @@ -259,7 +259,7 @@ define([ * @returns {Boolean} true if they are equal, false otherwise. */ OrthographicFrustum.prototype.equals = function(other) { - if (!defined(other)) { + if (!defined(other) || !(other instanceof OrthographicFrustum)) { return false; } diff --git a/Source/Core/OrthographicOffCenterFrustum.js b/Source/Core/OrthographicOffCenterFrustum.js index 63dd0c40dfb..09877fba03b 100644 --- a/Source/Core/OrthographicOffCenterFrustum.js +++ b/Source/Core/OrthographicOffCenterFrustum.js @@ -361,7 +361,7 @@ define([ * @returns {Boolean} true if they are equal, false otherwise. */ OrthographicOffCenterFrustum.prototype.equals = function(other) { - return (defined(other) && + return (defined(other) && other instanceof OrthographicOffCenterFrustum && this.right === other.right && this.left === other.left && this.top === other.top && diff --git a/Source/Core/PerspectiveFrustum.js b/Source/Core/PerspectiveFrustum.js index 5926554f69d..3186170bbd7 100644 --- a/Source/Core/PerspectiveFrustum.js +++ b/Source/Core/PerspectiveFrustum.js @@ -354,7 +354,7 @@ define([ * @returns {Boolean} true if they are equal, false otherwise. */ PerspectiveFrustum.prototype.equals = function(other) { - if (!defined(other)) { + if (!defined(other) || !(other instanceof PerspectiveFrustum)) { return false; } diff --git a/Source/Core/PerspectiveOffCenterFrustum.js b/Source/Core/PerspectiveOffCenterFrustum.js index 7b031d85e77..30319a5b255 100644 --- a/Source/Core/PerspectiveOffCenterFrustum.js +++ b/Source/Core/PerspectiveOffCenterFrustum.js @@ -412,7 +412,7 @@ define([ * @returns {Boolean} true if they are equal, false otherwise. */ PerspectiveOffCenterFrustum.prototype.equals = function(other) { - return (defined(other) && + return (defined(other) && other instanceof PerspectiveOffCenterFrustum && this.right === other.right && this.left === other.left && this.top === other.top && diff --git a/Source/Core/barycentricCoordinates.js b/Source/Core/barycentricCoordinates.js index 6316440fc47..085223fd239 100644 --- a/Source/Core/barycentricCoordinates.js +++ b/Source/Core/barycentricCoordinates.js @@ -2,12 +2,14 @@ define([ './Cartesian2', './Cartesian3', './Check', - './defined' + './defined', + './Math' ], function( Cartesian2, Cartesian3, Check, - defined) { + defined, + CesiumMath) { 'use strict'; var scratchCartesian1 = new Cartesian3(); @@ -47,34 +49,61 @@ define([ } // Implementation based on http://www.blackpawn.com/texts/pointinpoly/default.html. - var v0, v1, v2; - var dot00, dot01, dot02, dot11, dot12; + var v0; + var v1; + var v2; + var dot00; + var dot01; + var dot02; + var dot11; + var dot12; if(!defined(p0.z)) { - v0 = Cartesian2.subtract(p1, p0, scratchCartesian1); - v1 = Cartesian2.subtract(p2, p0, scratchCartesian2); - v2 = Cartesian2.subtract(point, p0, scratchCartesian3); + if (Cartesian2.equalsEpsilon(point, p0, CesiumMath.EPSILON14)) { + return Cartesian3.clone(Cartesian3.UNIT_X, result); + } + if (Cartesian2.equalsEpsilon(point, p1, CesiumMath.EPSILON14)) { + return Cartesian3.clone(Cartesian3.UNIT_Y, result); + } + if (Cartesian2.equalsEpsilon(point, p2, CesiumMath.EPSILON14)) { + return Cartesian3.clone(Cartesian3.UNIT_Z, result); + } - dot00 = Cartesian2.dot(v0, v0); - dot01 = Cartesian2.dot(v0, v1); - dot02 = Cartesian2.dot(v0, v2); - dot11 = Cartesian2.dot(v1, v1); - dot12 = Cartesian2.dot(v1, v2); + v0 = Cartesian2.subtract(p1, p0, scratchCartesian1); + v1 = Cartesian2.subtract(p2, p0, scratchCartesian2); + v2 = Cartesian2.subtract(point, p0, scratchCartesian3); + + dot00 = Cartesian2.dot(v0, v0); + dot01 = Cartesian2.dot(v0, v1); + dot02 = Cartesian2.dot(v0, v2); + dot11 = Cartesian2.dot(v1, v1); + dot12 = Cartesian2.dot(v1, v2); } else { - v0 = Cartesian3.subtract(p1, p0, scratchCartesian1); - v1 = Cartesian3.subtract(p2, p0, scratchCartesian2); - v2 = Cartesian3.subtract(point, p0, scratchCartesian3); + if (Cartesian3.equalsEpsilon(point, p0, CesiumMath.EPSILON14)) { + return Cartesian3.clone(Cartesian3.UNIT_X, result); + } + if (Cartesian3.equalsEpsilon(point, p1, CesiumMath.EPSILON14)) { + return Cartesian3.clone(Cartesian3.UNIT_Y, result); + } + if (Cartesian3.equalsEpsilon(point, p2, CesiumMath.EPSILON14)) { + return Cartesian3.clone(Cartesian3.UNIT_Z, result); + } + + v0 = Cartesian3.subtract(p1, p0, scratchCartesian1); + v1 = Cartesian3.subtract(p2, p0, scratchCartesian2); + v2 = Cartesian3.subtract(point, p0, scratchCartesian3); - dot00 = Cartesian3.dot(v0, v0); - dot01 = Cartesian3.dot(v0, v1); - dot02 = Cartesian3.dot(v0, v2); - dot11 = Cartesian3.dot(v1, v1); - dot12 = Cartesian3.dot(v1, v2); + dot00 = Cartesian3.dot(v0, v0); + dot01 = Cartesian3.dot(v0, v1); + dot02 = Cartesian3.dot(v0, v2); + dot11 = Cartesian3.dot(v1, v1); + dot12 = Cartesian3.dot(v1, v2); } - var q = 1.0 / (dot00 * dot11 - dot01 * dot01); - result.y = (dot11 * dot02 - dot01 * dot12) * q; - result.z = (dot00 * dot12 - dot01 * dot02) * q; + var q = dot00 * dot11 - dot01 * dot01; + var invQ = 1.0 / q; + result.y = (dot11 * dot02 - dot01 * dot12) * invQ; + result.z = (dot00 * dot12 - dot01 * dot02) * invQ; result.x = 1.0 - result.y - result.z; return result; } diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js index 194af4c1a20..1556c5b2ad9 100644 --- a/Source/Renderer/AutomaticUniforms.js +++ b/Source/Renderer/AutomaticUniforms.js @@ -1135,6 +1135,22 @@ define([ } }), + /** + * The log of the current frustums far plane. Used for computing the log depth. + * + * @alias czm_logFarDistance + * @glslUniform + * + * @private + */ + czm_logFarDistance : new AutomaticUniform({ + size : 1, + datatype : WebGLConstants.FLOAT, + getValue : function(uniformState) { + return uniformState.logFarDistance; + } + }), + /** * An automatic GLSL uniform representing the sun position in world coordinates. * diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 56951672c09..1c6d1a405a2 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -61,6 +61,7 @@ define([ this._entireFrustum = new Cartesian2(); this._currentFrustum = new Cartesian2(); this._frustumPlanes = new Cartesian4(); + this._logFarDistance = undefined; this._frameState = undefined; this._temeToPseudoFixed = Matrix3.clone(Matrix4.IDENTITY); @@ -641,6 +642,17 @@ define([ } }, + /** + * The log of the current frustum's far distance. Used to compute the log depth. + * @memberof UniformState.prototype + * @type {Number} + */ + logFarDistance : { + get : function() { + return this._logFarDistance; + } + }, + /** * The the height (x) and the height squared (y) * in meters of the camera above the 2D world plane. This uniform is only valid @@ -987,6 +999,8 @@ define([ this._currentFrustum.x = frustum.near; this._currentFrustum.y = frustum.far; + this._logFarDistance = 2.0 / CesiumMath.log2(frustum.far + 1.0); + if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index e7e7719b898..915ca86bc00 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -2574,10 +2574,13 @@ define([ var scratchProj = new Cartesian3(); /** - * Return the distance from the camera to the front of the bounding sphere. + * Return the signed distance from the camera to the front of the bounding sphere. + *

+ * Positive values indicate that the bounding sphere is in the positive half-plane of the camera position and view direction while a negative value indicates it is in the negative half-plane. + *

* * @param {BoundingSphere} boundingSphere The bounding sphere in world coordinates. - * @returns {Number} The distance to the bounding sphere. + * @returns {Number} The signed distance to the bounding sphere. */ Camera.prototype.distanceToBoundingSphere = function(boundingSphere) { //>>includeStart('debug', pragmas.debug); @@ -2587,8 +2590,10 @@ define([ //>>includeEnd('debug'); var toCenter = Cartesian3.subtract(this.positionWC, boundingSphere.center, scratchToCenter); - var proj = Cartesian3.multiplyByScalar(this.directionWC, Cartesian3.dot(toCenter, this.directionWC), scratchProj); - return Math.max(0.0, Cartesian3.magnitude(proj) - boundingSphere.radius); + var distance = -Cartesian3.dot(toCenter, this.directionWC); + var proj = Cartesian3.multiplyByScalar(this.directionWC, distance, scratchProj); + var unsignedDistance = Math.max(0.0, Cartesian3.magnitude(proj) - boundingSphere.radius); + return distance < 0.0 ? -unsignedDistance : unsignedDistance; }; var scratchPixelSize = new Cartesian2(); @@ -2615,6 +2620,9 @@ define([ //>>includeEnd('debug'); var distance = this.distanceToBoundingSphere(boundingSphere); + if (distance < 0.0) { + return 0.0; + } var pixelSize = this.frustum.getPixelDimensions(drawingBufferWidth, drawingBufferHeight, distance, scratchPixelSize); return Math.max(pixelSize.x, pixelSize.y); }; @@ -3229,6 +3237,7 @@ define([ Cartesian3.clone(camera.right, result.right); Matrix4.clone(camera._transform, result.transform); result._transformChanged = true; + result.frustum = camera.frustum.clone(); return result; }; diff --git a/Source/Scene/Cesium3DTileBatchTable.js b/Source/Scene/Cesium3DTileBatchTable.js index d1a834b5809..1b553ed4c5b 100644 --- a/Source/Scene/Cesium3DTileBatchTable.js +++ b/Source/Scene/Cesium3DTileBatchTable.js @@ -1351,7 +1351,7 @@ define([ if (bivariateVisibilityTest) { if (command.pass !== Pass.TRANSLUCENT && !finalResolution) { if (!defined(derivedCommands.zback)) { - derivedCommands.zback = deriveZBackfaceCommand(derivedCommands.originalCommand); + derivedCommands.zback = deriveZBackfaceCommand(frameState.context, derivedCommands.originalCommand); } tileset._backfaceCommands.push(derivedCommands.zback); } @@ -1436,7 +1436,24 @@ define([ return derivedCommand; } - function deriveZBackfaceCommand(command) { + function getDisableLogDepthFragmentShaderProgram(context, shaderProgram) { + var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, 'zBackfaceLogDepth'); + if (!defined(shader)) { + var fs = shaderProgram.fragmentShaderSource.clone(); + fs.defines = defined(fs.defines) ? fs.defines.slice(0) : []; + fs.defines.push('DISABLE_LOG_DEPTH_FRAGMENT_WRITE'); + + shader = context.shaderCache.createDerivedShaderProgram(shaderProgram, 'zBackfaceLogDepth', { + vertexShaderSource : shaderProgram.vertexShaderSource, + fragmentShaderSource : fs, + attributeLocations : shaderProgram._attributeLocations + }); + } + + return shader; + } + + function deriveZBackfaceCommand(context, command) { // Write just backface depth of unresolved tiles so resolved stenciled tiles do not appear in front var derivedCommand = DrawCommand.shallowClone(command); var rs = clone(derivedCommand.renderState, true); @@ -1459,6 +1476,9 @@ define([ derivedCommand.renderState = RenderState.fromCache(rs); derivedCommand.castShadows = false; derivedCommand.receiveShadows = false; + // Disable the depth writes in the fragment shader. The back face commands were causing the higher resolution + // tiles to disappear. + derivedCommand.shaderProgram = getDisableLogDepthFragmentShaderProgram(context, command.shaderProgram); return derivedCommand; } diff --git a/Source/Scene/ClassificationModel.js b/Source/Scene/ClassificationModel.js index f5955f6bb81..2308c1bebdf 100644 --- a/Source/Scene/ClassificationModel.js +++ b/Source/Scene/ClassificationModel.js @@ -663,34 +663,38 @@ define([ } function createProgram(model) { - var positionName = ModelUtility.getAttributeOrUniformBySemantic(model.gltf, 'POSITION'); - var batchIdName = ModelUtility.getAttributeOrUniformBySemantic(model.gltf, '_BATCHID'); + var gltf = model.gltf; + + var positionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'POSITION'); + var batchIdName = ModelUtility.getAttributeOrUniformBySemantic(gltf, '_BATCHID'); var attributeLocations = {}; attributeLocations[positionName] = 0; attributeLocations[batchIdName] = 1; - var modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic(model.gltf, 'MODELVIEWPROJECTION'); + var modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEWPROJECTION'); var uniformDecl; - var computePosition; + var toClip; if (!defined(modelViewProjectionName)) { - var projectionName = ModelUtility.getAttributeOrUniformBySemantic(model.gltf, 'PROJECTION'); - var modelViewName = ModelUtility.getAttributeOrUniformBySemantic(model.gltf, 'MODELVIEW'); + var projectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'PROJECTION'); + var modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEW'); if (!defined(modelViewName)) { - modelViewName = ModelUtility.getAttributeOrUniformBySemantic(model.gltf, 'CESIUM_RTC_MODELVIEW'); + modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'CESIUM_RTC_MODELVIEW'); } uniformDecl = 'uniform mat4 ' + modelViewName + ';\n' + 'uniform mat4 ' + projectionName + ';\n'; - computePosition = ' vec4 positionInClipCoords = ' + projectionName + ' * ' + modelViewName + ' * vec4(' + positionName + ', 1.0);\n'; + toClip = projectionName + ' * ' + modelViewName + ' * vec4(' + positionName + ', 1.0)'; } else { uniformDecl = 'uniform mat4 ' + modelViewProjectionName + ';\n'; - computePosition = ' vec4 positionInClipCoords = ' + modelViewProjectionName + ' * vec4(' + positionName + ', 1.0);\n'; + toClip = modelViewProjectionName + ' * vec4(' + positionName + ', 1.0)'; } + var computePosition = ' vec4 positionInClipCoords = ' + toClip + ';\n'; + var vs = 'attribute vec3 ' + positionName + ';\n' + 'attribute float ' + batchIdName + ';\n' + @@ -716,6 +720,9 @@ define([ var drawVS = modifyShader(vs, model._vertexShaderLoaded); var drawFS = modifyShader(fs, model._classificationShaderLoaded); + drawVS = ModelUtility.modifyVertexShaderForLogDepth(drawVS, toClip); + drawFS = ModelUtility.modifyFragmentShaderForLogDepth(drawFS); + model._shaderProgram = { vertexShaderSource : drawVS, fragmentShaderSource : drawFS, @@ -726,6 +733,9 @@ define([ var pickVS = modifyShader(vs, model._pickVertexShaderLoaded); var pickFS = modifyShader(fs, model._pickFragmentShaderLoaded); + pickVS = ModelUtility.modifyVertexShaderForLogDepth(pickVS, toClip); + pickFS = ModelUtility.modifyFragmentShaderForLogDepth(pickFS); + model._pickShaderProgram = { vertexShaderSource : pickVS, fragmentShaderSource : pickFS, diff --git a/Source/Scene/DepthPlane.js b/Source/Scene/DepthPlane.js index 1a4e59a8991..4df50d2663e 100644 --- a/Source/Scene/DepthPlane.js +++ b/Source/Scene/DepthPlane.js @@ -12,6 +12,7 @@ define([ '../Renderer/Pass', '../Renderer/RenderState', '../Renderer/ShaderProgram', + '../Renderer/ShaderSource', '../Renderer/VertexArray', '../Shaders/DepthPlaneFS', '../Shaders/DepthPlaneVS', @@ -30,6 +31,7 @@ define([ Pass, RenderState, ShaderProgram, + ShaderSource, VertexArray, DepthPlaneFS, DepthPlaneVS, @@ -45,6 +47,7 @@ define([ this._va = undefined; this._command = undefined; this._mode = undefined; + this._useLogDepth = false; } var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; @@ -100,7 +103,7 @@ define([ return depthQuadScratch; } - DepthPlane.prototype.update = function(frameState) { + DepthPlane.prototype.update = function(frameState, useLogDepth) { this._mode = frameState.mode; if (frameState.mode !== SceneMode.SCENE3D) { return; @@ -125,24 +128,48 @@ define([ } }); - this._sp = ShaderProgram.fromCache({ - context : context, - vertexShaderSource : DepthPlaneVS, - fragmentShaderSource : DepthPlaneFS, - attributeLocations : { - position : 0 - } - }); - this._command = new DrawCommand({ renderState : this._rs, - shaderProgram : this._sp, boundingVolume : new BoundingSphere(Cartesian3.ZERO, ellipsoid.maximumRadius), pass : Pass.OPAQUE, owner : this }); } + if (!defined(this._sp) || this._useLogDepth !== useLogDepth) { + this._useLogDepth = useLogDepth; + + var vs = new ShaderSource({ + sources : [DepthPlaneVS] + }); + var fs = new ShaderSource({ + sources : [DepthPlaneFS] + }); + if (useLogDepth) { + var extension = + '#ifdef GL_EXT_frag_depth \n' + + '#extension GL_EXT_frag_depth : enable \n' + + '#endif \n\n'; + + fs.sources.push(extension); + fs.defines.push('LOG_DEPTH'); + vs.defines.push('LOG_DEPTH'); + vs.defines.push('DISABLE_GL_POSITION_LOG_DEPTH'); + } + + this._sp = ShaderProgram.replaceCache({ + shaderProgram : this._sp, + context : context, + vertexShaderSource : vs, + fragmentShaderSource : fs, + attributeLocations : { + position : 0 + } + }); + + this._command.shaderProgram = this._sp; + } + // update depth plane var depthQuad = computeDepthQuad(ellipsoid, frameState); diff --git a/Source/Scene/DerivedCommand.js b/Source/Scene/DerivedCommand.js new file mode 100644 index 00000000000..9a8c77dcdf1 --- /dev/null +++ b/Source/Scene/DerivedCommand.js @@ -0,0 +1,217 @@ +define([ + '../Core/defined', + '../Renderer/DrawCommand', + '../Renderer/RenderState', + '../Renderer/ShaderSource' + ], function( + defined, + DrawCommand, + RenderState, + ShaderSource) { + 'use strict'; + + /** + * @private + */ + function DerivedCommand() {} + + var fragDepthRegex = /\bgl_FragDepthEXT\b/; + var discardRegex = /\bdiscard\b/; + + function getDepthOnlyShaderProgram(context, shaderProgram) { + var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, 'depthOnly'); + if (!defined(shader)) { + var attributeLocations = shaderProgram._attributeLocations; + var fs = shaderProgram.fragmentShaderSource; + + var writesDepthOrDiscards = false; + var sources = fs.sources; + var length = sources.length; + for (var i = 0; i < length; ++i) { + if (fragDepthRegex.test(sources[i]) || discardRegex.test(sources[i])) { + writesDepthOrDiscards = true; + break; + } + } + + if (!writesDepthOrDiscards) { + fs = new ShaderSource({ + sources : ['void main() { gl_FragColor = vec4(1.0); }'] + }); + } + + shader = context.shaderCache.createDerivedShaderProgram(shaderProgram, 'depthOnly', { + vertexShaderSource : shaderProgram.vertexShaderSource, + fragmentShaderSource : fs, + attributeLocations : attributeLocations + }); + } + + return shader; + } + + function getDepthOnlyRenderState(scene, renderState) { + var cache = scene._depthOnlyRenderStateCache; + var depthOnlyState = cache[renderState.id]; + if (!defined(depthOnlyState)) { + var rs = RenderState.getState(renderState); + rs.depthMask = true; + rs.colorMask = { + red : false, + green : false, + blue : false, + alpha : false + }; + + depthOnlyState = RenderState.fromCache(rs); + cache[renderState.id] = depthOnlyState; + } + + return depthOnlyState; + } + + DerivedCommand.createDepthOnlyDerivedCommand = function(scene, command, context, result) { + // For a depth only pass, we bind a framebuffer with only a depth attachment (no color attachments), + // do not write color, and write depth. If the fragment shader doesn't modify the fragment depth + // or discard, the driver can replace the fragment shader with a pass-through shader. We're unsure if this + // actually happens so we modify the shader to use a pass-through fragment shader. + + if (!defined(result)) { + result = {}; + } + + var shader; + var renderState; + if (defined(result.depthOnlyCommand)) { + shader = result.depthOnlyCommand.shaderProgram; + renderState = result.depthOnlyCommand.renderState; + } + + result.depthOnlyCommand = DrawCommand.shallowClone(command, result.depthOnlyCommand); + + if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) { + result.depthOnlyCommand.shaderProgram = getDepthOnlyShaderProgram(context, command.shaderProgram); + result.depthOnlyCommand.renderState = getDepthOnlyRenderState(scene, command.renderState); + result.shaderProgramId = command.shaderProgram.id; + } else { + result.depthOnlyCommand.shaderProgram = shader; + result.depthOnlyCommand.renderState = renderState; + } + + return result; + }; + + var writeLogDepthRegex = /\s+czm_writeLogDepth\(/; + var vertexlogDepthRegex = /\s+czm_vertexLogDepth\(/; + var extensionRegex = /\s*#extension\s+GL_EXT_frag_depth\s*:\s*enable/; + + function getLogDepthShaderProgram(context, shaderProgram) { + var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, 'logDepth'); + if (!defined(shader)) { + var attributeLocations = shaderProgram._attributeLocations; + var vs = shaderProgram.vertexShaderSource.clone(); + var fs = shaderProgram.fragmentShaderSource.clone(); + + vs.defines = defined(vs.defines) ? vs.defines.slice(0) : []; + vs.defines.push('LOG_DEPTH'); + fs.defines = defined(fs.defines) ? fs.defines.slice(0) : []; + fs.defines.push('LOG_DEPTH'); + + var i; + var logMain; + var writesLogDepth = false; + var sources = vs.sources; + var length = sources.length; + for (i = 0; i < length; ++i) { + if (vertexlogDepthRegex.test(sources[i])) { + writesLogDepth = true; + break; + } + } + + if (!writesLogDepth) { + for (i = 0; i < length; ++i) { + sources[i] = ShaderSource.replaceMain(sources[i], 'czm_log_depth_main'); + } + + logMain = + '\n\n' + + 'void main() \n' + + '{ \n' + + ' czm_log_depth_main(); \n' + + ' czm_vertexLogDepth(); \n' + + '} \n'; + sources.push(logMain); + } + + var addExtension = true; + writesLogDepth = false; + sources = fs.sources; + length = sources.length; + for (i = 0; i < length; ++i) { + if (writeLogDepthRegex.test(sources[i])) { + writesLogDepth = true; + } + if (extensionRegex.test(sources[i])) { + addExtension = false; + } + } + + var logSource = ''; + if (addExtension) { + logSource += + '#ifdef GL_EXT_frag_depth \n' + + '#extension GL_EXT_frag_depth : enable \n' + + '#endif \n\n'; + } + + if (!writesLogDepth) { + for (i = 0; i < length; i++) { + sources[i] = ShaderSource.replaceMain(sources[i], 'czm_log_depth_main'); + } + + logSource += + '\n' + + 'void main() \n' + + '{ \n' + + ' czm_log_depth_main(); \n' + + ' czm_writeLogDepth(); \n' + + '} \n'; + } + + sources.push(logSource); + + shader = context.shaderCache.createDerivedShaderProgram(shaderProgram, 'logDepth', { + vertexShaderSource : vs, + fragmentShaderSource : fs, + attributeLocations : attributeLocations + }); + } + + return shader; + } + + DerivedCommand.createLogDepthCommand = function(command, context, result) { + if (!defined(result)) { + result = {}; + } + + var shader; + if (defined(result.command)) { + shader = result.command.shaderProgram; + } + + result.command = DrawCommand.shallowClone(command, result.command); + + if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) { + result.command.shaderProgram = getLogDepthShaderProgram(context, command.shaderProgram); + result.shaderProgramId = command.shaderProgram.id; + } else { + result.command.shaderProgram = shader; + } + + return result; + }; + + return DerivedCommand; +}); diff --git a/Source/Scene/EllipsoidPrimitive.js b/Source/Scene/EllipsoidPrimitive.js index ae455d5347d..79607551370 100644 --- a/Source/Scene/EllipsoidPrimitive.js +++ b/Source/Scene/EllipsoidPrimitive.js @@ -8,6 +8,8 @@ define([ '../Core/destroyObject', '../Core/DeveloperError', '../Core/Matrix4', + '../Core/OrthographicFrustum', + '../Core/OrthographicOffCenterFrustum', '../Core/VertexFormat', '../Renderer/BufferUsage', '../Renderer/DrawCommand', @@ -32,6 +34,8 @@ define([ destroyObject, DeveloperError, Matrix4, + OrthographicFrustum, + OrthographicOffCenterFrustum, VertexFormat, BufferUsage, DrawCommand, @@ -196,6 +200,8 @@ define([ */ this._depthTestEnabled = defaultValue(options.depthTestEnabled, true); + this._useLogDepth = false; + this._sp = undefined; this._rs = undefined; this._va = undefined; @@ -251,6 +257,11 @@ define([ return vertexArray; } + var logDepthExtension = + '#ifdef GL_EXT_frag_depth \n' + + '#extension GL_EXT_frag_depth : enable \n' + + '#endif \n\n'; + /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. @@ -343,11 +354,20 @@ define([ var lightingChanged = this.onlySunLighting !== this._onlySunLighting; this._onlySunLighting = this.onlySunLighting; + var frustum = frameState.camera.frustum; + var useLogDepth = context.fragmentDepth && !(frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum); + var useLogDepthChanged = this._useLogDepth !== useLogDepth; + this._useLogDepth = useLogDepth; + var colorCommand = this._colorCommand; + var vs; var fs; // Recompile shader when material, lighting, or translucency changes - if (materialChanged || lightingChanged || translucencyChanged) { + if (materialChanged || lightingChanged || translucencyChanged || useLogDepthChanged) { + vs = new ShaderSource({ + sources : [EllipsoidVS] + }); fs = new ShaderSource({ sources : [this.material.shaderSource, EllipsoidFS] }); @@ -357,11 +377,16 @@ define([ if (!translucent && context.fragmentDepth) { fs.defines.push('WRITE_DEPTH'); } + if (this._useLogDepth) { + vs.defines.push('LOG_DEPTH'); + fs.defines.push('LOG_DEPTH'); + fs.sources.push(logDepthExtension); + } this._sp = ShaderProgram.replaceCache({ context : context, shaderProgram : this._sp, - vertexShaderSource : EllipsoidVS, + vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations : attributeLocations }); @@ -398,7 +423,10 @@ define([ } // Recompile shader when material changes - if (materialChanged || lightingChanged || !defined(this._pickSP)) { + if (materialChanged || lightingChanged || !defined(this._pickSP) || useLogDepthChanged) { + vs = new ShaderSource({ + sources : [EllipsoidVS] + }); fs = new ShaderSource({ sources : [this.material.shaderSource, EllipsoidFS], pickColorQualifier : 'uniform' @@ -409,11 +437,16 @@ define([ if (!translucent && context.fragmentDepth) { fs.defines.push('WRITE_DEPTH'); } + if (this._useLogDepth) { + vs.defines.push('LOG_DEPTH'); + fs.defines.push('LOG_DEPTH'); + fs.sources.push(logDepthExtension); + } this._pickSP = ShaderProgram.replaceCache({ context : context, shaderProgram : this._pickSP, - vertexShaderSource : EllipsoidVS, + vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations : attributeLocations }); diff --git a/Source/Scene/Expression.js b/Source/Scene/Expression.js index bdc71866311..02a958f6747 100644 --- a/Source/Scene/Expression.js +++ b/Source/Scene/Expression.js @@ -279,7 +279,7 @@ define([ } function log2(number) { - return CesiumMath.logBase(number, 2.0); + return CesiumMath.log2(number); } function getEvaluateUnaryComponentwise(operation) { diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 04369056877..2c6b74bdce2 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -8,6 +8,7 @@ define([ '../Renderer/Framebuffer', '../Renderer/PixelDatatype', '../Renderer/RenderState', + '../Renderer/ShaderSource', '../Renderer/Texture', '../Shaders/PostProcessFilters/PassThrough' ], function( @@ -20,6 +21,7 @@ define([ Framebuffer, PixelDatatype, RenderState, + ShaderSource, Texture, PassThrough) { 'use strict'; @@ -45,23 +47,30 @@ define([ this._useScissorTest = false; this._scissorRectangle = undefined; + this._useLogDepth = undefined; + this._debugGlobeDepthViewportCommand = undefined; } - function executeDebugGlobeDepth(globeDepth, context, passState) { - if (!defined(globeDepth._debugGlobeDepthViewportCommand)) { - var fs = + function executeDebugGlobeDepth(globeDepth, context, passState, useLogDepth) { + if (!defined(globeDepth._debugGlobeDepthViewportCommand) || useLogDepth !== globeDepth._useLogDepth) { + var fsSource = 'uniform sampler2D u_texture;\n' + 'varying vec2 v_textureCoordinates;\n' + 'void main()\n' + '{\n' + ' float z_window = czm_unpackDepth(texture2D(u_texture, v_textureCoordinates));\n' + + ' z_window = czm_reverseLogDepth(z_window); \n' + ' float n_range = czm_depthRange.near;\n' + ' float f_range = czm_depthRange.far;\n' + ' float z_ndc = (2.0 * z_window - n_range - f_range) / (f_range - n_range);\n' + ' float scale = pow(z_ndc * 0.5 + 0.5, 8.0);\n' + ' gl_FragColor = vec4(mix(vec3(0.0), vec3(1.0), scale), 1.0);\n' + '}\n'; + var fs = new ShaderSource({ + defines : [useLogDepth ? 'LOG_DEPTH' : ''], + sources : [fsSource] + }); globeDepth._debugGlobeDepthViewportCommand = context.createViewportQuadCommand(fs, { uniformMap : { @@ -71,6 +80,8 @@ define([ }, owner : globeDepth }); + + globeDepth._useLogDepth = useLogDepth; } globeDepth._debugGlobeDepthViewportCommand.execute(context, passState); @@ -207,8 +218,8 @@ define([ globeDepth._clearColorCommand.framebuffer = globeDepth.framebuffer; } - GlobeDepth.prototype.executeDebugGlobeDepth = function(context, passState) { - executeDebugGlobeDepth(this, context, passState); + GlobeDepth.prototype.executeDebugGlobeDepth = function(context, passState, useLogDepth) { + executeDebugGlobeDepth(this, context, passState, useLogDepth); }; GlobeDepth.prototype.update = function(context, passState) { diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 57c931ef4b5..9aa1769fef2 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1978,6 +1978,7 @@ define([ var program = model._sourcePrograms[id]; var shaders = model._sourceShaders; var quantizedVertexShaders = model._quantizedVertexShaders; + var toClipCoordinatesGLSL = model._toClipCoordinatesGLSL[id]; var vs = shaders[program.vertexShader].extras._pipeline.source; var fs = shaders[program.fragmentShader].extras._pipeline.source; @@ -1994,6 +1995,9 @@ define([ var drawVS = modifyShader(vs, id, model._vertexShaderLoaded); var drawFS = modifyShader(fs, id, model._fragmentShaderLoaded); + drawVS = ModelUtility.modifyVertexShaderForLogDepth(drawVS, toClipCoordinatesGLSL); + drawFS = ModelUtility.modifyFragmentShaderForLogDepth(drawFS); + var pickFS, pickVS; if (model.allowPicking) { // PERFORMANCE_IDEA: Can optimize this shader with a glTF hint. https://github.com/KhronosGroup/glTF/issues/181 @@ -2003,6 +2007,9 @@ define([ if (!model._pickFragmentShaderLoaded) { pickFS = ShaderSource.createPickFragmentShaderSource(fs, 'uniform'); } + + pickVS = ModelUtility.modifyVertexShaderForLogDepth(pickVS, toClipCoordinatesGLSL); + pickFS = ModelUtility.modifyFragmentShaderForLogDepth(pickFS); } createAttributesAndProgram(id, drawFS, drawVS, pickFS, pickVS, model, context); } @@ -2011,6 +2018,7 @@ define([ var program = model._sourcePrograms[id]; var shaders = model._sourceShaders; var quantizedVertexShaders = model._quantizedVertexShaders; + var toClipCoordinatesGLSL = model._toClipCoordinatesGLSL[id]; var clippingPlaneCollection = model.clippingPlanes; var addClippingPlaneCode = isClippingEnabled(model); @@ -2033,6 +2041,9 @@ define([ var drawVS = modifyShader(vs, id, model._vertexShaderLoaded); var drawFS = modifyShader(finalFS, id, model._fragmentShaderLoaded); + drawVS = ModelUtility.modifyVertexShaderForLogDepth(drawVS, toClipCoordinatesGLSL); + drawFS = ModelUtility.modifyFragmentShaderForLogDepth(drawFS); + var pickFS, pickVS; if (model.allowPicking) { // PERFORMANCE_IDEA: Can optimize this shader with a glTF hint. https://github.com/KhronosGroup/glTF/issues/181 @@ -2046,6 +2057,9 @@ define([ if (addClippingPlaneCode) { pickFS = modifyShaderForClippingPlanes(pickFS, clippingPlaneCollection); } + + pickVS = ModelUtility.modifyVertexShaderForLogDepth(pickVS, toClipCoordinatesGLSL); + pickFS = ModelUtility.modifyFragmentShaderForLogDepth(pickFS); } createAttributesAndProgram(id, drawFS, drawVS, pickFS, pickVS, model, context); } @@ -3366,10 +3380,30 @@ define([ var scene3DOnly = frameState.scene3DOnly; // Retain references to updated source shaders and programs for rebuilding as needed - model._sourcePrograms = model.gltf.programs; - model._sourceShaders = model.gltf.shaders; + var programs = model._sourcePrograms = model.gltf.programs; + var shaders = model._sourceShaders = model.gltf.shaders; model._hasPremultipliedAlpha = hasPremultipliedAlpha(model); + var quantizedVertexShaders = model._quantizedVertexShaders; + var toClipCoordinates = model._toClipCoordinatesGLSL = {}; + for (var id in programs) { + if (programs.hasOwnProperty(id)) { + var program = programs[id]; + var shader = shaders[program.vertexShader].extras._pipeline.source; + if (model.extensionsUsed.WEB3D_quantized_attributes || model._dequantizeInShader) { + var quantizedVS = quantizedVertexShaders[id]; + if (!defined(quantizedVS)) { + quantizedVS = modifyShaderForQuantizedAttributes(shader, id, model); + quantizedVertexShaders[id] = quantizedVS; + } + shader = quantizedVS; + } + + shader = modifyShader(shader, id, model._vertexShaderLoaded); + toClipCoordinates[id] = ModelUtility.toClipCoordinatesGLSL(model.gltf, shader); + } + } + ModelUtility.checkSupportedGlExtensions(model.gltf.glExtensionsUsed, context); if (model._loadRendererResourcesFromCache) { var resources = model._rendererResources; diff --git a/Source/Scene/ModelUtility.js b/Source/Scene/ModelUtility.js index 49a6af5f94b..4b5384773e7 100644 --- a/Source/Scene/ModelUtility.js +++ b/Source/Scene/ModelUtility.js @@ -52,7 +52,7 @@ define([ }; }; - ModelUtility.getAttributeOrUniformBySemantic = function(gltf, semantic, programId) { + ModelUtility.getAttributeOrUniformBySemantic = function(gltf, semantic, programId, ignoreNodes) { var techniques = gltf.techniques; var parameter; for (var techniqueName in techniques) { @@ -67,7 +67,7 @@ define([ for (var attributeName in attributes) { if (attributes.hasOwnProperty(attributeName)) { parameter = parameters[attributes[attributeName]]; - if (defined(parameter) && parameter.semantic === semantic) { + if (defined(parameter) && parameter.semantic === semantic && (!ignoreNodes || !defined(parameter.node))) { return attributeName; } } @@ -75,7 +75,7 @@ define([ for (var uniformName in uniforms) { if (uniforms.hasOwnProperty(uniformName)) { parameter = parameters[uniforms[uniformName]]; - if (defined(parameter) && parameter.semantic === semantic) { + if (defined(parameter) && parameter.semantic === semantic && (!ignoreNodes || !defined(parameter.node))) { return uniformName; } } @@ -347,6 +347,54 @@ define([ }; }; + ModelUtility.toClipCoordinatesGLSL = function(gltf, shader) { + var positionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'POSITION'); + var decodedPositionName = positionName.replace('a_', 'gltf_a_dec_'); + if (shader.indexOf(decodedPositionName) !== -1) { + positionName = decodedPositionName; + } + + var modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEWPROJECTION', undefined, true); + if (!defined(modelViewProjectionName) || shader.indexOf(modelViewProjectionName) === -1) { + var projectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'PROJECTION', undefined, true); + var modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEW', undefined, true); + if (shader.indexOf('czm_instanced_modelView ') !== -1) { + modelViewName = 'czm_instanced_modelView'; + } else if (!defined(modelViewName)) { + modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'CESIUM_RTC_MODELVIEW', undefined, true); + } + modelViewProjectionName = projectionName + ' * ' + modelViewName; + } + + return modelViewProjectionName + ' * vec4(' + positionName + '.xyz, 1.0)'; + }; + + ModelUtility.modifyFragmentShaderForLogDepth = function(shader) { + shader = ShaderSource.replaceMain(shader, 'czm_depth_main'); + shader += + '\n' + + 'void main() \n' + + '{ \n' + + ' czm_depth_main(); \n' + + ' czm_writeLogDepth(); \n' + + '} \n'; + + return shader; + }; + + ModelUtility.modifyVertexShaderForLogDepth = function(shader, toClipCoordinatesGLSL) { + shader = ShaderSource.replaceMain(shader, 'czm_depth_main'); + shader += + '\n' + + 'void main() \n' + + '{ \n' + + ' czm_depth_main(); \n' + + ' czm_vertexLogDepth(' + toClipCoordinatesGLSL + '); \n' + + '} \n'; + + return shader; + }; + function getScalarUniformFunction(value) { var that = { value : value, diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index 1e90699d313..77c005a115f 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -552,12 +552,14 @@ define([ for (j = 0; j < length; ++j) { command = commands[j]; + command = defined(command.derivedCommands.logDepth) ? command.derivedCommands.logDepth.command : command; derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } if (defined(invertClassification)) { command = invertClassification.unclassifiedCommand; + command = defined(command.derivedCommands.logDepth) ? command.derivedCommands.logDepth.command : command; derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } @@ -566,12 +568,14 @@ define([ for (j = 0; j < length; ++j) { command = commands[j]; + command = defined(command.derivedCommands.logDepth) ? command.derivedCommands.logDepth.command : command; derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.alphaCommand : command.derivedCommands.oit.alphaCommand; executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } if (defined(invertClassification)) { command = invertClassification.unclassifiedCommand; + command = defined(command.derivedCommands.logDepth) ? command.derivedCommands.logDepth.command : command; derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.alphaCommand : command.derivedCommands.oit.alphaCommand; executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } @@ -597,12 +601,14 @@ define([ for (var j = 0; j < length; ++j) { command = commands[j]; + command = defined(command.derivedCommands.logDepth) ? command.derivedCommands.logDepth.command : command; derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } if (defined(invertClassification)) { command = invertClassification.unclassifiedCommand; + command = defined(command.derivedCommands.logDepth) ? command.derivedCommands.logDepth.command : command; derivedCommand = (shadowsEnabled && command.receiveShadows) ? command.derivedCommands.oit.shadows.translucentCommand : command.derivedCommands.oit.translucentCommand; executeFunction(derivedCommand, scene, context, passState, debugFramebuffer); } diff --git a/Source/Scene/PickDepth.js b/Source/Scene/PickDepth.js index 16486ec01ca..f812a34317e 100644 --- a/Source/Scene/PickDepth.js +++ b/Source/Scene/PickDepth.js @@ -5,6 +5,7 @@ define([ '../Renderer/Framebuffer', '../Renderer/PixelDatatype', '../Renderer/RenderState', + '../Renderer/ShaderSource', '../Renderer/Texture' ], function( defined, @@ -13,6 +14,7 @@ define([ Framebuffer, PixelDatatype, RenderState, + ShaderSource, Texture) { 'use strict'; @@ -26,23 +28,30 @@ define([ this._textureToCopy = undefined; this._copyDepthCommand = undefined; + this._useLogDepth = undefined; + this._debugPickDepthViewportCommand = undefined; } - function executeDebugPickDepth(pickDepth, context, passState) { - if (!defined(pickDepth._debugPickDepthViewportCommand)) { - var fs = + function executeDebugPickDepth(pickDepth, context, passState, useLogDepth) { + if (!defined(pickDepth._debugPickDepthViewportCommand) || useLogDepth !== pickDepth._useLogDepth) { + var fsSource = 'uniform sampler2D u_texture;\n' + 'varying vec2 v_textureCoordinates;\n' + 'void main()\n' + '{\n' + ' float z_window = czm_unpackDepth(texture2D(u_texture, v_textureCoordinates));\n' + + ' z_window = czm_reverseLogDepth(z_window); \n' + ' float n_range = czm_depthRange.near;\n' + ' float f_range = czm_depthRange.far;\n' + ' float z_ndc = (2.0 * z_window - n_range - f_range) / (f_range - n_range);\n' + ' float scale = pow(z_ndc * 0.5 + 0.5, 8.0);\n' + ' gl_FragColor = vec4(mix(vec3(0.0), vec3(1.0), scale), 1.0);\n' + '}\n'; + var fs = new ShaderSource({ + defines : [useLogDepth ? 'LOG_DEPTH' : ''], + sources : [fsSource] + }); pickDepth._debugPickDepthViewportCommand = context.createViewportQuadCommand(fs, { uniformMap : { @@ -52,6 +61,8 @@ define([ }, owner : pickDepth }); + + pickDepth._useLogDepth = useLogDepth; } pickDepth._debugPickDepthViewportCommand.execute(context, passState); @@ -123,8 +134,8 @@ define([ pickDepth._copyDepthCommand.framebuffer = pickDepth.framebuffer; } - PickDepth.prototype.executeDebugPickDepth = function(context, passState) { - executeDebugPickDepth(this, context, passState); + PickDepth.prototype.executeDebugPickDepth = function(context, passState, useLogDepth) { + executeDebugPickDepth(this, context, passState, useLogDepth); }; PickDepth.prototype.update = function(context, depthTexture) { diff --git a/Source/Scene/Primitive.js b/Source/Scene/Primitive.js index fb5cafcb962..97597b00ba9 100644 --- a/Source/Scene/Primitive.js +++ b/Source/Scene/Primitive.js @@ -1011,7 +1011,7 @@ define([ 'varying float v_WindowZ;\n' + 'void main() {\n' + ' czm_non_depth_clamp_main();\n' + - '#ifdef GL_EXT_frag_depth\n' + + '#if defined(GL_EXT_frag_depth) && !defined(LOG_DEPTH)\n' + ' gl_FragDepthEXT = min(v_WindowZ * gl_FragCoord.w, 1.0);\n' + '#endif\n' + '}\n'; diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index adde5f6855a..da28e35093e 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -55,6 +55,7 @@ define([ './CreditDisplay', './DebugCameraPrimitive', './DepthPlane', + './DerivedCommand', './DeviceOrientationCameraController', './Fog', './FrameState', @@ -134,6 +135,7 @@ define([ CreditDisplay, DebugCameraPrimitive, DepthPlane, + DerivedCommand, DeviceOrientationCameraController, Fog, FrameState, @@ -287,6 +289,9 @@ define([ this._primitives = new PrimitiveCollection(); this._groundPrimitives = new PrimitiveCollection(); + this._logDepthBuffer = context.fragmentDepth; + this._logDepthBufferDirty = true; + this._tweens = new TweenCollection(); this._shaderFrameCount = 0; @@ -455,18 +460,38 @@ define([ * @default 1.0 */ this.morphTime = 1.0; + /** - * The far-to-near ratio of the multi-frustum. The default is 1,000.0. + * The far-to-near ratio of the multi-frustum when using a normal depth buffer. + *

+ * This value is used to create the near and far values for each frustum of the multi-frustum. It is only used + * when {@link Scene#logarithmicDepthBuffer} is false. When logarithmicDepthBuffer is + * true, use {@link Scene#logarithmicDepthFarToNearRatio}. + *

* * @type {Number} * @default 1000.0 */ this.farToNearRatio = 1000.0; + /** + * The far-to-near ratio of the multi-frustum when using a logarithmic depth buffer. + *

+ * This value is used to create the near and far values for each frustum of the multi-frustum. It is only used + * when {@link Scene#logarithmicDepthBuffer} is true. When logarithmicDepthBuffer is + * false, use {@link Scene#farToNearRatio}. + *

+ * + * @type {Number} + * @default 1e9 + */ + this.logarithmicDepthFarToNearRatio = 1e9; + /** * Determines the uniform depth size in meters of each frustum of the multifrustum in 2D. If a primitive or model close * to the surface shows z-fighting, decreasing this will eliminate the artifact, but decrease performance. On the - * other hand, increasing this will increase performance but may cause z-fighting among primitives close to thesurface. + * other hand, increasing this will increase performance but may cause z-fighting among primitives close to the surface. + * * @type {Number} * @default 1.75e6 */ @@ -690,10 +715,17 @@ define([ var camera = new Camera(this); this._camera = camera; - this._cameraClone = Camera.clone(camera); this._screenSpaceCameraController = new ScreenSpaceCameraController(this); this._mapMode2D = defaultValue(options.mapMode2D, MapMode2D.INFINITE_SCROLL); + if (this._logDepthBuffer) { + this._camera.frustum.near = 0.1; + this._camera.frustum.far = 10000000000.0; + } + + this._cameraClone = Camera.clone(camera); + this._frustumChanged = true; + // Keeps track of the state of a frame. FrameState is the state across // the primitives of the scene. This state is for internally keeping track // of celestial and environment effects that need to be updated/rendered in @@ -764,16 +796,16 @@ define([ // initial guess at frustums. var near = camera.frustum.near; var far = camera.frustum.far; - var numFrustums = Math.ceil(Math.log(far / near) / Math.log(this.farToNearRatio)); - updateFrustums(near, far, this.farToNearRatio, numFrustums, this._frustumCommandsList, false, undefined); + var farToNearRatio = this._logDepthBuffer ? this.logarithmicDepthFarToNearRatio : this.farToNearRatio; + + var numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio)); + updateFrustums(near, far, farToNearRatio, numFrustums, this._logDepthBuffer, this._frustumCommandsList, false, undefined); // give frameState, camera, and screen space camera controller initial state before rendering updateFrameState(this, 0.0, JulianDate.now()); this.initializeFrame(); } - var OPAQUE_FRUSTUM_NEAR_OFFSET = 0.9999; - function updateGlobeListeners(scene, globe) { for (var i = 0; i < scene._removeGlobeCallbacks.length; ++i) { scene._removeGlobeCallbacks[i](); @@ -1368,6 +1400,32 @@ define([ //>>includeEnd('debug'); this._minimumDisableDepthTestDistance = value; } + }, + + /** + * Whether or not to use a logarithmic depth buffer. Enabling this option will allow for less frustums in the multi-frustum, + * increasing performance. This property relies on {@link Context#fragmentDepth} being supported. + */ + logarithmicDepthBuffer : { + get : function() { + return this._logDepthBuffer; + }, + set : function(value) { + value = this._context.fragmentDepth && value; + if (this._logDepthBuffer !== value) { + this._logDepthBuffer = value; + this._logDepthBufferDirty = true; + } + } + }, + + /** + * @private + */ + opaqueFrustumNearOffset : { + get : function() { + return this._logDepthBuffer ? 0.9 : 0.9999; + } } }); @@ -1421,24 +1479,48 @@ define([ } var derivedCommands = command.derivedCommands; - if (command.dirty && defined(derivedCommands)) { + if ((scene._logDepthBufferDirty || scene._frustumChanged || command.dirty) && defined(derivedCommands)) { command.dirty = false; + var frustum = scene.camera.frustum; + var useLogDepth = scene._logDepthBuffer && !(frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum); + var logDepthCommand; + var logDepthDerivedCommands; + if (useLogDepth) { + derivedCommands.logDepth = DerivedCommand.createLogDepthCommand(command, context, derivedCommands.logDepth); + logDepthCommand = derivedCommands.logDepth.command; + logDepthDerivedCommands = logDepthCommand.derivedCommands; + } else { + derivedCommands.logDepth = undefined; + } + + if (scene.frameState.passes.pick) { + return; + } + if (shadowsEnabled && (command.receiveShadows || command.castShadows)) { derivedCommands.shadows = ShadowMap.createDerivedCommands(shadowMaps, lightShadowMaps, command, shadowsDirty, context, derivedCommands.shadows); + if (useLogDepth) { + logDepthDerivedCommands.shadows = ShadowMap.createDerivedCommands(shadowMaps, lightShadowMaps, logDepthCommand, shadowsDirty, context, logDepthDerivedCommands.shadows); + } + } + + if (useLogDepth) { + command = logDepthCommand; + derivedCommands = logDepthDerivedCommands; } var oit = scene._oit; if (command.pass === Pass.TRANSLUCENT && defined(oit) && oit.isSupported()) { if (lightShadowsEnabled && command.receiveShadows) { derivedCommands.oit = defined(derivedCommands.oit) ? derivedCommands.oit : {}; - derivedCommands.oit.shadows = oit.createDerivedCommands(command.derivedCommands.shadows.receiveCommand, context, derivedCommands.oit.shadows); + derivedCommands.oit.shadows = oit.createDerivedCommands(derivedCommands.shadows.receiveCommand, context, derivedCommands.oit.shadows); } else { derivedCommands.oit = oit.createDerivedCommands(command, context, derivedCommands.oit); } } - derivedCommands.depth = createDepthOnlyDerivedCommand(scene, command, context, derivedCommands.depth); + derivedCommands.depth = DerivedCommand.createDepthOnlyDerivedCommand(scene, command, context, derivedCommands.depth); } } @@ -1501,7 +1583,7 @@ define([ clearPasses(frameState.passes); } - function updateFrustums(near, far, farToNearRatio, numFrustums, frustumCommandsList, is2D, nearToFarDistance2D) { + function updateFrustums(near, far, farToNearRatio, numFrustums, logDepth, frustumCommandsList, is2D, nearToFarDistance2D) { frustumCommandsList.length = numFrustums; for (var m = 0; m < numFrustums; ++m) { var curNear; @@ -1509,7 +1591,10 @@ define([ if (!is2D) { curNear = Math.max(near, Math.pow(farToNearRatio, m) * near); - curFar = Math.min(far, farToNearRatio * curNear); + curFar = farToNearRatio * curNear; + if (!logDepth) { + curFar = Math.min(far, curFar); + } } else { curNear = Math.min(far - nearToFarDistance2D, near + m * nearToFarDistance2D); curFar = Math.min(far, curNear + nearToFarDistance2D); @@ -1530,10 +1615,6 @@ define([ command.debugOverlappingFrustums = 0; } - if (!scene.frameState.passes.pick) { - updateDerivedCommands(scene, command); - } - var frustumCommandsList = scene._frustumCommandsList; var length = frustumCommandsList.length; @@ -1568,6 +1649,8 @@ define([ cf[command.debugOverlappingFrustums] = defined(cf[command.debugOverlappingFrustums]) ? cf[command.debugOverlappingFrustums] + 1 : 1; ++scene._debugFrustumStatistics.totalCommands; } + + updateDerivedCommands(scene, command); } var scratchCullingVolume = new CullingVolume(); @@ -1704,15 +1787,16 @@ define([ // Exploit temporal coherence. If the frustums haven't changed much, use the frustums computed // last frame, else compute the new frustums and sort them by frustum again. var is2D = scene.mode === SceneMode.SCENE2D; - var farToNearRatio = scene.farToNearRatio; - + var logDepth = scene._logDepthBuffer && !(camera.frustum instanceof OrthographicFrustum || camera.frustum instanceof OrthographicOffCenterFrustum); + var farToNearRatio = logDepth ? scene.logarithmicDepthFarToNearRatio : scene.farToNearRatio; var numFrustums; + if (!is2D) { // The multifrustum for 3D/CV is non-uniformly distributed. numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio)); } else { // The multifrustum for 2D is uniformly distributed. To avoid z-fighting in 2D, - // the camera i smoved to just before the frustum and the frustum depth is scaled + // the camera is moved to just before the frustum and the frustum depth is scaled // to be in [1.0, nearToFarDistance2D]. far = Math.min(far, camera.position.z + scene.nearToFarDistance2D); near = Math.min(near, far); @@ -1720,8 +1804,8 @@ define([ } if (near !== Number.MAX_VALUE && (numFrustums !== numberOfFrustums || (frustumCommandsList.length !== 0 && - (near < frustumCommandsList[0].near || (far > frustumCommandsList[numberOfFrustums - 1].far && !CesiumMath.equalsEpsilon(far, frustumCommandsList[numberOfFrustums - 1].far, CesiumMath.EPSILON8)))))) { - updateFrustums(near, far, farToNearRatio, numFrustums, frustumCommandsList, is2D, scene.nearToFarDistance2D); + (near < frustumCommandsList[0].near || (far > frustumCommandsList[numberOfFrustums - 1].far && (logDepth || !CesiumMath.equalsEpsilon(far, frustumCommandsList[numberOfFrustums - 1].far, CesiumMath.EPSILON8))))))) { + updateFrustums(near, far, farToNearRatio, numFrustums, logDepth, frustumCommandsList, is2D, scene.nearToFarDistance2D); createPotentiallyVisibleSet(scene); } @@ -1841,6 +1925,10 @@ define([ var shadowsEnabled = scene.frameState.shadowHints.shadowsEnabled; var lightShadowsEnabled = shadowsEnabled && (scene.frameState.shadowHints.lightShadowMaps.length > 0); + if (scene._logDepthBuffer && defined(command.derivedCommands.logDepth)) { + command = command.derivedCommands.logDepth.command; + } + if (scene.debugShowCommands || scene.debugShowFrustums) { executeDebugCommand(command, scene, passState); } else if (lightShadowsEnabled && command.receiveShadows && defined(command.derivedCommands.shadows)) { @@ -2086,7 +2174,7 @@ define([ us.updateFrustum(frustum); } else { // Avoid tearing artifacts between adjacent frustums in the opaque passes - frustum.near = index !== 0 ? frustumCommands.near * OPAQUE_FRUSTUM_NEAR_OFFSET : frustumCommands.near; + frustum.near = index !== 0 ? frustumCommands.near * scene.opaqueFrustumNearOffset : frustumCommands.near; frustum.far = frustumCommands.far; us.updateFrustum(frustum); } @@ -2656,10 +2744,11 @@ define([ var clearGlobeDepth = environmentState.clearGlobeDepth = defined(globe) && (!globe.depthTestAgainstTerrain || scene.mode === SceneMode.SCENE2D); var useDepthPlane = environmentState.useDepthPlane = clearGlobeDepth && scene.mode === SceneMode.SCENE3D; if (useDepthPlane) { + var useLogDepth = scene._logDepthBuffer && !(scene.camera.frustum instanceof OrthographicFrustum || scene.camera.frustum instanceof OrthographicOffCenterFrustum); // Update the depth plane that is rendered in 3D when the primitives are // not depth tested against terrain so primitives on the backface // of the globe are not picked. - scene._depthPlane.update(frameState); + scene._depthPlane.update(frameState, useLogDepth); } var occluder = (frameState.mode === SceneMode.SCENE3D) ? frameState.occluder: undefined; @@ -2847,19 +2936,9 @@ define([ var context = scene._context; var environmentState = scene._environmentState; - var useGlobeDepthFramebuffer = environmentState.useGlobeDepthFramebuffer; - if (scene.debugShowGlobeDepth && useGlobeDepthFramebuffer) { - var gd = getDebugGlobeDepth(scene, scene.debugShowDepthFrustum - 1); - gd.executeDebugGlobeDepth(context, passState); - } - - if (scene.debugShowPickDepth && useGlobeDepthFramebuffer) { - var pd = getPickDepth(scene, scene.debugShowDepthFrustum - 1); - pd.executeDebugPickDepth(context, passState); - } - var useOIT = environmentState.useOIT; var useFXAA = environmentState.useFXAA; + var useGlobeDepthFramebuffer = environmentState.useGlobeDepthFramebuffer; if (useOIT) { passState.framebuffer = useFXAA ? scene._fxaa.getColorFramebuffer() : undefined; @@ -2880,6 +2959,19 @@ define([ passState.framebuffer = environmentState.originalFramebuffer; scene._globeDepth.executeCopyColor(context, passState); } + + var frustum = scene.camera.frustum; + var useLogDepth = scene._logDepthBuffer && !(frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum); + + if (scene.debugShowGlobeDepth && useGlobeDepthFramebuffer) { + var gd = getDebugGlobeDepth(scene, scene.debugShowDepthFrustum - 1); + gd.executeDebugGlobeDepth(context, passState, useLogDepth); + } + + if (scene.debugShowPickDepth && useGlobeDepthFramebuffer) { + var pd = getPickDepth(scene, scene.debugShowDepthFrustum - 1); + pd.executeDebugPickDepth(context, passState, useLogDepth); + } } function callAfterRenderFunctions(scene) { @@ -3052,8 +3144,10 @@ define([ tryAndCatchError(this, time, update); this._postUpdate.raiseEvent(this, time); + this._frustumChanged = !this._camera.frustum.equals(this._cameraClone.frustum); + var cameraChanged = checkForCameraUpdates(this); - var shouldRender = !this.requestRenderMode || this._renderRequested || cameraChanged || (this.mode === SceneMode.MORPHING); + var shouldRender = !this.requestRenderMode || this._renderRequested || cameraChanged || this._frustumChanged || this._logDepthBufferDirty || (this.mode === SceneMode.MORPHING); if (!shouldRender && defined(this.maximumRenderTimeChange) && defined(this._lastRenderTime)) { var difference = Math.abs(JulianDate.secondsDifference(this._lastRenderTime, time)); shouldRender = shouldRender || difference > this.maximumRenderTimeChange; @@ -3062,6 +3156,7 @@ define([ if (shouldRender) { this._lastRenderTime = JulianDate.clone(time, this._lastRenderTime); this._renderRequested = false; + this._logDepthBufferDirty = false; // Render this._preRender.raiseEvent(this, time); @@ -3272,92 +3367,6 @@ define([ return object; }; - var fragDepthRegex = /\bgl_FragDepthEXT\b/; - var discardRegex = /\bdiscard\b/; - - function getDepthOnlyShaderProgram(context, shaderProgram) { - var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, 'depthOnly'); - if (!defined(shader)) { - var attributeLocations = shaderProgram._attributeLocations; - var fs = shaderProgram.fragmentShaderSource; - - var writesDepthOrDiscards = false; - var sources = fs.sources; - var length = sources.length; - for (var i = 0; i < length; ++i) { - if (fragDepthRegex.test(sources[i]) || discardRegex.test(sources[i])) { - writesDepthOrDiscards = true; - break; - } - } - - if (!writesDepthOrDiscards) { - fs = new ShaderSource({ - sources : ['void main() { gl_FragColor = vec4(1.0); }'] - }); - } - - shader = context.shaderCache.createDerivedShaderProgram(shaderProgram, 'depthOnly', { - vertexShaderSource : shaderProgram.vertexShaderSource, - fragmentShaderSource : fs, - attributeLocations : attributeLocations - }); - } - - return shader; - } - - function getDepthOnlyRenderState(scene, renderState) { - var cache = scene._depthOnlyRenderStateCache; - var depthOnlyState = cache[renderState.id]; - if (!defined(depthOnlyState)) { - var rs = RenderState.getState(renderState); - rs.depthMask = true; - rs.colorMask = { - red : false, - green : false, - blue : false, - alpha : false - }; - - depthOnlyState = RenderState.fromCache(rs); - cache[renderState.id] = depthOnlyState; - } - - return depthOnlyState; - } - - function createDepthOnlyDerivedCommand(scene, command, context, result) { - // For a depth only pass, we bind a framebuffer with only a depth attachment (no color attachments), - // do not write color, and write depth. If the fragment shader doesn't modify the fragment depth - // or discard, the driver can replace the fragment shader with a pass-through shader. We're unsure if this - // actually happens so we modify the shader to use a pass-through fragment shader. - - if (!defined(result)) { - result = {}; - } - - var shader; - var renderState; - if (defined(result.depthOnlyCommand)) { - shader = result.depthOnlyCommand.shaderProgram; - renderState = result.depthOnlyCommand.renderState; - } - - result.depthOnlyCommand = DrawCommand.shallowClone(command, result.depthOnlyCommand); - - if (!defined(shader) || result.shaderProgramId !== command.shaderProgram.id) { - result.depthOnlyCommand.shaderProgram = getDepthOnlyShaderProgram(context, command.shaderProgram); - result.depthOnlyCommand.renderState = getDepthOnlyRenderState(scene, command.renderState); - result.shaderProgramId = command.shaderProgram.id; - } else { - result.depthOnlyCommand.shaderProgram = shader; - result.depthOnlyCommand.renderState = renderState; - } - - return result; - } - function renderTranslucentDepthForPick(scene, drawingBufferPosition) { // PERFORMANCE_IDEA: render translucent only and merge with the previous frame var context = scene._context; @@ -3508,7 +3517,7 @@ define([ uniformState.update(this.frameState); uniformState.updateFrustum(frustum); } else { - frustum.near = renderedFrustum.near * (i !== 0 ? OPAQUE_FRUSTUM_NEAR_OFFSET : 1.0); + frustum.near = renderedFrustum.near * (i !== 0 ? this.opaqueFrustumNearOffset : 1.0); frustum.far = renderedFrustum.far; uniformState.updateFrustum(frustum); } diff --git a/Source/Scene/SceneTransforms.js b/Source/Scene/SceneTransforms.js index 8dbb0a652fb..96c32e4f738 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -302,6 +302,19 @@ define([ var context = scene.context; var uniformState = context.uniformState; + var currentFrustum = uniformState.currentFrustum; + var near = currentFrustum.x; + var far = currentFrustum.y; + + if (scene._logDepthBuffer && !(scene.camera.frustum instanceof OrthographicFrustum || scene.camera.frustum instanceof OrthographicOffCenterFrustum)) { + // transforming logarithmic depth of form + // log2(z + 1) / log2( far + 1); + // to perspective form + // (far - far * near / z) / (far - near) + depth = Math.pow(2.0, depth * CesiumMath.log2(far + 1.0)) - 1.0; + depth = far * (1.0 - near / depth) / (far - near); + } + var viewport = scene._passState.viewport; var ndc = Cartesian4.clone(Cartesian4.UNIT_W, scratchNDC); ndc.x = (drawingBufferPosition.x - viewport.x) / viewport.width * 2.0 - 1.0; @@ -315,11 +328,10 @@ define([ if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } - var currentFrustum = uniformState.currentFrustum; worldCoords = scratchWorldCoords; worldCoords.x = (ndc.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5; worldCoords.y = (ndc.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) * 0.5; - worldCoords.z = (ndc.z * (currentFrustum.x - currentFrustum.y) - currentFrustum.x - currentFrustum.y) * 0.5; + worldCoords.z = (ndc.z * (near - far) - near - far) * 0.5; worldCoords.w = 1.0; worldCoords = Matrix4.multiplyByVector(uniformState.inverseView, worldCoords, worldCoords); @@ -330,7 +342,6 @@ define([ var w = 1.0 / worldCoords.w; Cartesian3.multiplyByScalar(worldCoords, w, worldCoords); } - return Cartesian3.fromCartesian4(worldCoords, result); }; diff --git a/Source/Scene/SceneTransitioner.js b/Source/Scene/SceneTransitioner.js index 318182e4e8f..5cb7f5b77e9 100644 --- a/Source/Scene/SceneTransitioner.js +++ b/Source/Scene/SceneTransitioner.js @@ -857,10 +857,10 @@ define([ destroyMorphHandler(transitioner); + var camera = scene.camera; if (transitioner._previousMode !== SceneMode.MORPHING || transitioner._morphCancelled) { transitioner._morphCancelled = false; - var camera = scene.camera; Cartesian3.clone(camera3D.position, camera.position); Cartesian3.clone(camera3D.direction, camera.direction); Cartesian3.clone(camera3D.up, camera.up); @@ -870,6 +870,12 @@ define([ camera.frustum = camera3D.frustum.clone(); } + var frustum = camera.frustum; + if (scene._logDepthBuffer && !(frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum)) { + frustum.near = 0.1; + frustum.far = 10000000000.0; + } + var wasMorphing = defined(transitioner._completeMorph); transitioner._completeMorph = undefined; scene.camera.update(scene.mode); @@ -910,10 +916,10 @@ define([ destroyMorphHandler(transitioner); + var camera = scene.camera; if (transitioner._previousModeMode !== SceneMode.MORPHING || transitioner._morphCancelled) { transitioner._morphCancelled = false; - var camera = scene.camera; Cartesian3.clone(cameraCV.position, camera.position); Cartesian3.clone(cameraCV.direction, camera.direction); Cartesian3.clone(cameraCV.up, camera.up); @@ -921,6 +927,12 @@ define([ Cartesian3.normalize(camera.right, camera.right); } + var frustum = camera.frustum; + if (scene._logDepthBuffer && !(frustum instanceof OrthographicFrustum || frustum instanceof OrthographicOffCenterFrustum)) { + frustum.near = 0.1; + frustum.far = 10000000000.0; + } + var wasMorphing = defined(transitioner._completeMorph); transitioner._completeMorph = undefined; scene.camera.update(scene.mode); diff --git a/Source/Scene/ShadowMapShader.js b/Source/Scene/ShadowMapShader.js index 8f2142b2822..0cf1cd5cc45 100644 --- a/Source/Scene/ShadowMapShader.js +++ b/Source/Scene/ShadowMapShader.js @@ -202,17 +202,30 @@ define([ fsSource += 'uniform sampler2D shadowMap_texture; \n'; } + var returnPositionEC; + if (hasPositionVarying) { + returnPositionEC = ' return vec4(' + positionVaryingName + ', 1.0); \n'; + } else { + returnPositionEC = + '#ifndef LOG_DEPTH \n' + + ' return czm_windowToEyeCoordinates(gl_FragCoord); \n' + + '#else \n' + + ' return vec4(v_logPositionEC, 1.0); \n' + + '#endif \n'; + } + fsSource += 'uniform mat4 shadowMap_matrix; \n' + 'uniform vec3 shadowMap_lightDirectionEC; \n' + 'uniform vec4 shadowMap_lightPositionEC; \n' + 'uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness; \n' + 'uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth; \n' + + '#ifdef LOG_DEPTH \n' + + 'varying vec3 v_logPositionEC; \n' + + '#endif \n' + 'vec4 getPositionEC() \n' + '{ \n' + - (hasPositionVarying ? - ' return vec4(' + positionVaryingName + ', 1.0); \n' : - ' return czm_windowToEyeCoordinates(gl_FragCoord); \n') + + returnPositionEC + '} \n' + 'vec3 getNormalEC() \n' + '{ \n' + diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 3ea52c8b403..644e781b262 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -199,7 +199,8 @@ define([ enabled : true, face : CullFace.FRONT }, - blending : BlendingState.ALPHA_BLEND + blending : BlendingState.ALPHA_BLEND, + depthMask : false }); var vs = new ShaderSource({ diff --git a/Source/Shaders/Appearances/BasicMaterialAppearanceFS.glsl b/Source/Shaders/Appearances/BasicMaterialAppearanceFS.glsl index 95be0556c01..aaf9e7efcdc 100644 --- a/Source/Shaders/Appearances/BasicMaterialAppearanceFS.glsl +++ b/Source/Shaders/Appearances/BasicMaterialAppearanceFS.glsl @@ -3,7 +3,7 @@ varying vec3 v_normalEC; void main() { - vec3 positionToEyeEC = -v_positionEC; + vec3 positionToEyeEC = -v_positionEC; vec3 normalEC = normalize(v_normalEC); #ifdef FACE_FORWARD @@ -14,8 +14,8 @@ void main() materialInput.normalEC = normalEC; materialInput.positionToEyeEC = positionToEyeEC; czm_material material = czm_getMaterial(materialInput); - -#ifdef FLAT + +#ifdef FLAT gl_FragColor = vec4(material.diffuse + material.emission, material.alpha); #else gl_FragColor = czm_phong(normalize(positionToEyeEC), material); diff --git a/Source/Shaders/Appearances/BasicMaterialAppearanceVS.glsl b/Source/Shaders/Appearances/BasicMaterialAppearanceVS.glsl index 0d496a877e7..d17aa17009b 100644 --- a/Source/Shaders/Appearances/BasicMaterialAppearanceVS.glsl +++ b/Source/Shaders/Appearances/BasicMaterialAppearanceVS.glsl @@ -6,12 +6,12 @@ attribute float batchId; varying vec3 v_positionEC; varying vec3 v_normalEC; -void main() +void main() { vec4 p = czm_computePosition(); v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates v_normalEC = czm_normal * normal; // normal in eye coordinates - + gl_Position = czm_modelViewProjectionRelativeToEye * p; } diff --git a/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceFS.glsl b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceFS.glsl index 7b1054b4acc..095ea101bd2 100644 --- a/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceFS.glsl +++ b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceFS.glsl @@ -5,27 +5,27 @@ varying vec2 v_st; void main() { czm_materialInput materialInput; - + vec3 normalEC = normalize(czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0))); #ifdef FACE_FORWARD normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC); #endif - + materialInput.s = v_st.s; materialInput.st = v_st; materialInput.str = vec3(v_st, 0.0); - + // Convert tangent space material normal to eye space materialInput.normalEC = normalEC; materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC); - + // Convert view vector to world space - vec3 positionToEyeEC = -v_positionEC; + vec3 positionToEyeEC = -v_positionEC; materialInput.positionToEyeEC = positionToEyeEC; czm_material material = czm_getMaterial(materialInput); - -#ifdef FLAT + +#ifdef FLAT gl_FragColor = vec4(material.diffuse + material.emission, material.alpha); #else gl_FragColor = czm_phong(normalize(positionToEyeEC), material); diff --git a/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceVS.glsl b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceVS.glsl index 1bd13c9a538..6f4e11300a7 100644 --- a/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceVS.glsl +++ b/Source/Shaders/Appearances/EllipsoidSurfaceAppearanceVS.glsl @@ -7,13 +7,13 @@ varying vec3 v_positionMC; varying vec3 v_positionEC; varying vec2 v_st; -void main() +void main() { vec4 p = czm_computePosition(); v_positionMC = position3DHigh + position3DLow; // position in model coordinates v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates v_st = st; - + gl_Position = czm_modelViewProjectionRelativeToEye * p; } diff --git a/Source/Shaders/Appearances/PerInstanceColorAppearanceFS.glsl b/Source/Shaders/Appearances/PerInstanceColorAppearanceFS.glsl index 6bef5b43865..c2709e847ea 100644 --- a/Source/Shaders/Appearances/PerInstanceColorAppearanceFS.glsl +++ b/Source/Shaders/Appearances/PerInstanceColorAppearanceFS.glsl @@ -5,18 +5,18 @@ varying vec4 v_color; void main() { vec3 positionToEyeEC = -v_positionEC; - + vec3 normalEC = normalize(v_normalEC); #ifdef FACE_FORWARD normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC); #endif - + czm_materialInput materialInput; materialInput.normalEC = normalEC; materialInput.positionToEyeEC = positionToEyeEC; czm_material material = czm_getDefaultMaterial(materialInput); material.diffuse = v_color.rgb; material.alpha = v_color.a; - + gl_FragColor = czm_phong(normalize(positionToEyeEC), material); } diff --git a/Source/Shaders/Appearances/PerInstanceColorAppearanceVS.glsl b/Source/Shaders/Appearances/PerInstanceColorAppearanceVS.glsl index b429e64c689..5d0711b6894 100644 --- a/Source/Shaders/Appearances/PerInstanceColorAppearanceVS.glsl +++ b/Source/Shaders/Appearances/PerInstanceColorAppearanceVS.glsl @@ -8,13 +8,13 @@ varying vec3 v_positionEC; varying vec3 v_normalEC; varying vec4 v_color; -void main() +void main() { vec4 p = czm_computePosition(); v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates v_normalEC = czm_normal * normal; // normal in eye coordinates v_color = color; - + gl_Position = czm_modelViewProjectionRelativeToEye * p; } diff --git a/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceVS.glsl b/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceVS.glsl index ce015c67842..f38d44046db 100644 --- a/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceVS.glsl +++ b/Source/Shaders/Appearances/PerInstanceFlatColorAppearanceVS.glsl @@ -5,11 +5,11 @@ attribute float batchId; varying vec4 v_color; -void main() +void main() { vec4 p = czm_computePosition(); v_color = color; - + gl_Position = czm_modelViewProjectionRelativeToEye * p; } diff --git a/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl b/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl index 5bb9d018882..17ce3ec48e2 100644 --- a/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl +++ b/Source/Shaders/Appearances/PolylineColorAppearanceVS.glsl @@ -25,4 +25,8 @@ void main() float angle; vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, angle); gl_Position = czm_viewportOrthographic * positionWC; + +#ifdef LOG_DEPTH + czm_vertexLogDepth(czm_modelViewProjectionRelativeToEye * p); +#endif } diff --git a/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl b/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl index 6b61f09369c..bdfae0e2756 100644 --- a/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl +++ b/Source/Shaders/Appearances/PolylineMaterialAppearanceVS.glsl @@ -27,4 +27,8 @@ void main() vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, v_polylineAngle); gl_Position = czm_viewportOrthographic * positionWC; + +#ifdef LOG_DEPTH + czm_vertexLogDepth(czm_modelViewProjectionRelativeToEye * p); +#endif } diff --git a/Source/Shaders/Appearances/TexturedMaterialAppearanceFS.glsl b/Source/Shaders/Appearances/TexturedMaterialAppearanceFS.glsl index 1a52fc8d7e8..92dcf9dbc37 100644 --- a/Source/Shaders/Appearances/TexturedMaterialAppearanceFS.glsl +++ b/Source/Shaders/Appearances/TexturedMaterialAppearanceFS.glsl @@ -4,9 +4,9 @@ varying vec2 v_st; void main() { - vec3 positionToEyeEC = -v_positionEC; + vec3 positionToEyeEC = -v_positionEC; - vec3 normalEC = normalize(v_normalEC);; + vec3 normalEC = normalize(v_normalEC); #ifdef FACE_FORWARD normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC); #endif @@ -16,8 +16,8 @@ void main() materialInput.positionToEyeEC = positionToEyeEC; materialInput.st = v_st; czm_material material = czm_getMaterial(materialInput); - -#ifdef FLAT + +#ifdef FLAT gl_FragColor = vec4(material.diffuse + material.emission, material.alpha); #else gl_FragColor = czm_phong(normalize(positionToEyeEC), material); diff --git a/Source/Shaders/Appearances/TexturedMaterialAppearanceVS.glsl b/Source/Shaders/Appearances/TexturedMaterialAppearanceVS.glsl index 19c102bba32..7ceb6e0b523 100644 --- a/Source/Shaders/Appearances/TexturedMaterialAppearanceVS.glsl +++ b/Source/Shaders/Appearances/TexturedMaterialAppearanceVS.glsl @@ -8,13 +8,13 @@ varying vec3 v_positionEC; varying vec3 v_normalEC; varying vec2 v_st; -void main() +void main() { vec4 p = czm_computePosition(); v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates v_normalEC = czm_normal * normal; // normal in eye coordinates v_st = st; - + gl_Position = czm_modelViewProjectionRelativeToEye * p; } diff --git a/Source/Shaders/BillboardCollectionFS.glsl b/Source/Shaders/BillboardCollectionFS.glsl index cb13d864efc..79a38c69f00 100644 --- a/Source/Shaders/BillboardCollectionFS.glsl +++ b/Source/Shaders/BillboardCollectionFS.glsl @@ -53,4 +53,6 @@ void main() #else gl_FragColor = color; #endif + + czm_writeLogDepth(); } diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl index 432138aa60b..9707d718b7f 100644 --- a/Source/Shaders/BillboardCollectionVS.glsl +++ b/Source/Shaders/BillboardCollectionVS.glsl @@ -39,7 +39,7 @@ const float SHIFT_RIGHT3 = 1.0 / 8.0; const float SHIFT_RIGHT2 = 1.0 / 4.0; const float SHIFT_RIGHT1 = 1.0 / 2.0; -vec4 computePositionWindowCoordinates(vec4 positionEC, vec2 imageSize, float scale, vec2 direction, vec2 origin, vec2 translate, vec2 pixelOffset, vec3 alignedAxis, bool validAlignedAxis, float rotation, bool sizeInMeters) +vec4 addScreenSpaceOffset(vec4 positionEC, vec2 imageSize, float scale, vec2 direction, vec2 origin, vec2 translate, vec2 pixelOffset, vec3 alignedAxis, bool validAlignedAxis, float rotation, bool sizeInMeters) { // Note the halfSize cannot be computed in JavaScript because it is sent via // compressed vertex attributes that coerce it to an integer. @@ -71,23 +71,23 @@ vec4 computePositionWindowCoordinates(vec4 positionEC, vec2 imageSize, float sca positionEC.xy += halfSize; } - vec4 positionWC = czm_eyeToWindowCoordinates(positionEC); + float mpp = czm_metersPerPixel(positionEC); - if (sizeInMeters) + if (!sizeInMeters) { - originTranslate /= czm_metersPerPixel(positionEC); + originTranslate *= mpp; } - positionWC.xy += originTranslate; + positionEC.xy += originTranslate; if (!sizeInMeters) { - positionWC.xy += halfSize; + positionEC.xy += halfSize * mpp; } - positionWC.xy += translate; - positionWC.xy += (pixelOffset * czm_resolutionScale); + positionEC.xy += translate * mpp; + positionEC.xy += (pixelOffset * czm_resolutionScale) * mpp; - return positionWC; + return positionEC; } void main() @@ -255,10 +255,14 @@ void main() } #endif - vec4 positionWC = computePositionWindowCoordinates(positionEC, imageSize, scale, direction, origin, translate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters); - gl_Position = czm_viewportOrthographic * vec4(positionWC.xy, -positionWC.z, 1.0); + positionEC = addScreenSpaceOffset(positionEC, imageSize, scale, direction, origin, translate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters); + gl_Position = czm_projection * positionEC; v_textureCoordinates = textureCoordinates; +#ifdef LOG_DEPTH + czm_vertexLogDepth(); +#endif + #ifdef DISABLE_DEPTH_DISTANCE float disableDepthTestDistance = distanceDisplayConditionAndDisableDepth.z; if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0) @@ -275,6 +279,9 @@ void main() { // Position z on the near plane. gl_Position.z = -gl_Position.w; +#ifdef LOG_DEPTH + czm_vertexLogDepth(vec4(czm_currentFrustum.x)); +#endif } } #endif diff --git a/Source/Shaders/Builtin/Functions/depthClampFarPlane.glsl b/Source/Shaders/Builtin/Functions/depthClampFarPlane.glsl index 76940c00575..bec29502403 100644 --- a/Source/Shaders/Builtin/Functions/depthClampFarPlane.glsl +++ b/Source/Shaders/Builtin/Functions/depthClampFarPlane.glsl @@ -1,5 +1,7 @@ // emulated noperspective +#ifndef LOG_DEPTH varying float v_WindowZ; +#endif /** * Clamps a vertex to the far plane. @@ -17,7 +19,9 @@ varying float v_WindowZ; */ vec4 czm_depthClampFarPlane(vec4 coords) { +#ifndef LOG_DEPTH v_WindowZ = (0.5 * (coords.z / coords.w) + 0.5) * coords.w; coords.z = min(coords.z, coords.w); +#endif return coords; } diff --git a/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl b/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl new file mode 100644 index 00000000000..e8719f24a9b --- /dev/null +++ b/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl @@ -0,0 +1,10 @@ +float czm_reverseLogDepth(float logZ) +{ +#ifdef LOG_DEPTH + float near = czm_currentFrustum.x; + float far = czm_currentFrustum.y; + logZ = pow(2.0, logZ * log2(far + 1.0)) - 1.0; + logZ = far * (1.0 - near / logZ) / (far - near); +#endif + return logZ; +} diff --git a/Source/Shaders/Builtin/Functions/vertexLogDepth.glsl b/Source/Shaders/Builtin/Functions/vertexLogDepth.glsl new file mode 100644 index 00000000000..1a16d725f5c --- /dev/null +++ b/Source/Shaders/Builtin/Functions/vertexLogDepth.glsl @@ -0,0 +1,49 @@ +#ifdef LOG_DEPTH +varying float v_logZ; +varying vec3 v_logPositionEC; +#endif + +void czm_updatePositionDepth() { +#if defined(LOG_DEPTH) && !defined(DISABLE_GL_POSITION_LOG_DEPTH) + v_logPositionEC = (czm_inverseProjection * gl_Position).xyz; + + gl_Position.z = log2(max(1e-6, 1.0 + gl_Position.w)) * czm_logFarDistance - 1.0; + gl_Position.z *= gl_Position.w; +#endif +} + +/** + * Writes the logarithmic depth to gl_Position using the already computed gl_Position. + * + * @name czm_vertexLogDepth + * @glslFunction + */ +void czm_vertexLogDepth() +{ +#ifdef LOG_DEPTH + v_logZ = 1.0 + gl_Position.w; + czm_updatePositionDepth(); +#endif +} + +/** + * Writes the logarithmic depth to gl_Position using the provided clip coordinates. + *

+ * An example use case for this function would be moving the vertex in window coordinates + * before converting back to clip coordinates. Use the original vertex clip coordinates. + *

+ * @name czm_vertexLogDepth + * @glslFunction + * + * @param {vec4} clipCoords The vertex in clip coordinates. + * + * @example + * czm_vertexLogDepth(czm_projection * vec4(positionEyeCoordinates, 1.0)); + */ +void czm_vertexLogDepth(vec4 clipCoords) +{ +#ifdef LOG_DEPTH + v_logZ = 1.0 + clipCoords.w; + czm_updatePositionDepth(); +#endif +} diff --git a/Source/Shaders/Builtin/Functions/writeDepthClampedToFarPlane.glsl b/Source/Shaders/Builtin/Functions/writeDepthClampedToFarPlane.glsl index 3425bc38762..bf8cf3209b4 100644 --- a/Source/Shaders/Builtin/Functions/writeDepthClampedToFarPlane.glsl +++ b/Source/Shaders/Builtin/Functions/writeDepthClampedToFarPlane.glsl @@ -1,6 +1,7 @@ // emulated noperspective +#ifndef LOG_DEPTH varying float v_WindowZ; - +#endif /** * Clamps a vertex to the far plane by writing the fragments depth. *

@@ -18,7 +19,7 @@ varying float v_WindowZ; */ void czm_writeDepthClampedToFarPlane() { -#ifdef GL_EXT_frag_depth +#if defined(GL_EXT_frag_depth) && !defined(LOG_DEPTH) gl_FragDepthEXT = min(v_WindowZ * gl_FragCoord.w, 1.0); #endif } diff --git a/Source/Shaders/Builtin/Functions/writeLogDepth.glsl b/Source/Shaders/Builtin/Functions/writeLogDepth.glsl new file mode 100644 index 00000000000..49d01bd2e72 --- /dev/null +++ b/Source/Shaders/Builtin/Functions/writeLogDepth.glsl @@ -0,0 +1,44 @@ +#ifdef LOG_DEPTH +varying float v_logZ; +#endif + +/** + * Writes the fragment depth to the logarithmic depth buffer. + *

+ * Use this when the vertex shader does not calls {@link czm_vertexlogDepth}, for example, when + * ray-casting geometry using a full screen quad. + *

+ * @name czm_writeLogDepth + * @glslFunction + * + * @param {float} logZ The w coordinate of the vertex in clip coordinates plus 1.0. + * + * @example + * czm_writeLogDepth((czm_projection * v_positionEyeCoordinates).w + 1.0); + */ +void czm_writeLogDepth(float logZ) +{ +#if defined(GL_EXT_frag_depth) && defined(LOG_DEPTH) && !defined(DISABLE_LOG_DEPTH_FRAGMENT_WRITE) + float halfLogFarDistance = czm_logFarDistance * 0.5; + float depth = log2(logZ); + if (depth < log2(czm_currentFrustum.x)) { + discard; + } + gl_FragDepthEXT = depth * halfLogFarDistance; +#endif +} + +/** + * Writes the fragment depth to the logarithmic depth buffer. + *

+ * Use this when the vertex shader calls {@link czm_vertexlogDepth}. + *

+ * + * @name czm_writeLogDepth + * @glslFunction + */ +void czm_writeLogDepth() { +#ifdef LOG_DEPTH + czm_writeLogDepth(v_logZ); +#endif +} diff --git a/Source/Shaders/DepthPlaneFS.glsl b/Source/Shaders/DepthPlaneFS.glsl index 4d898e11871..41385729ae4 100644 --- a/Source/Shaders/DepthPlaneFS.glsl +++ b/Source/Shaders/DepthPlaneFS.glsl @@ -4,10 +4,10 @@ void main() { // TODO: make arbitrary ellipsoid czm_ellipsoid ellipsoid = czm_getWgs84EllipsoidEC(); - + vec3 direction = normalize(positionEC.xyz); czm_ray ray = czm_ray(vec3(0.0), direction); - + czm_raySegment intersection = czm_rayEllipsoidIntersectionInterval(ray, ellipsoid); if (!czm_isEmpty(intersection)) { @@ -17,4 +17,6 @@ void main() { discard; } + + czm_writeLogDepth(); } diff --git a/Source/Shaders/DepthPlaneVS.glsl b/Source/Shaders/DepthPlaneVS.glsl index e28f3767ba6..028b56c4370 100644 --- a/Source/Shaders/DepthPlaneVS.glsl +++ b/Source/Shaders/DepthPlaneVS.glsl @@ -6,4 +6,6 @@ void main() { positionEC = czm_modelView * position; gl_Position = czm_projection * positionEC; + + czm_vertexLogDepth(); } diff --git a/Source/Shaders/EllipsoidFS.glsl b/Source/Shaders/EllipsoidFS.glsl index a982dbcddd1..d028e502b96 100644 --- a/Source/Shaders/EllipsoidFS.glsl +++ b/Source/Shaders/EllipsoidFS.glsl @@ -42,17 +42,17 @@ void main() // PERFORMANCE_TODO: When dynamic branching is available, compute ratio of maximum and minimum radii // in the vertex shader. Only when it is larger than some constant, march along the ray. // Otherwise perform one intersection test which will be the common case. - + // Test if the ray intersects a sphere with the ellipsoid's maximum radius. // For very oblate ellipsoids, using the ellipsoid's radii for an intersection test // may cause false negatives. This will discard fragments before marching the ray forward. float maxRadius = max(u_radii.x, max(u_radii.y, u_radii.z)) * 1.5; vec3 direction = normalize(v_positionEC); vec3 ellipsoidCenter = czm_modelView[3].xyz; - + float t1 = -1.0; float t2 = -1.0; - + float b = -2.0 * dot(direction, ellipsoidCenter); float c = dot(ellipsoidCenter, ellipsoidCenter) - maxRadius * maxRadius; @@ -61,22 +61,22 @@ void main() t1 = (-b - sqrt(discriminant)) * 0.5; t2 = (-b + sqrt(discriminant)) * 0.5; } - + if (t1 < 0.0 && t2 < 0.0) { discard; } - + float t = min(t1, t2); if (t < 0.0) { t = 0.0; } - + // March ray forward to intersection with larger sphere and find // actual intersection point with ellipsoid. czm_ellipsoid ellipsoid = czm_ellipsoidNew(ellipsoidCenter, u_radii); czm_ray ray = czm_ray(t * direction, direction); czm_raySegment intersection = czm_rayEllipsoidIntersectionInterval(ray, ellipsoid); - + if (czm_isEmpty(intersection)) { discard; @@ -90,18 +90,22 @@ void main() gl_FragColor = mix(insideFaceColor, outsideFaceColor, outsideFaceColor.a); gl_FragColor.a = 1.0 - (1.0 - insideFaceColor.a) * (1.0 - outsideFaceColor.a); - + #ifdef WRITE_DEPTH #ifdef GL_EXT_frag_depth t = (intersection.start != 0.0) ? intersection.start : intersection.stop; vec3 positionEC = czm_pointAlongRay(ray, t); vec4 positionCC = czm_projection * vec4(positionEC, 1.0); +#ifdef LOG_DEPTH + czm_writeLogDepth(1.0 + positionCC.w); +#else float z = positionCC.z / positionCC.w; - + float n = czm_depthRange.near; float f = czm_depthRange.far; - + gl_FragDepthEXT = (z * (f - n) + f + n) * 0.5; #endif #endif +#endif } diff --git a/Source/Shaders/EllipsoidVS.glsl b/Source/Shaders/EllipsoidVS.glsl index af801899fd6..98c1269cd66 100644 --- a/Source/Shaders/EllipsoidVS.glsl +++ b/Source/Shaders/EllipsoidVS.glsl @@ -4,7 +4,7 @@ uniform vec3 u_radii; varying vec3 v_positionEC; -void main() +void main() { // In the vertex data, the cube goes from (-1.0, -1.0, -1.0) to (1.0, 1.0, 1.0) in model coordinates. // Scale to consider the radii. We could also do this once on the CPU when using the BoxGeometry, @@ -15,8 +15,8 @@ void main() v_positionEC = (czm_modelView * p).xyz; // position in eye coordinates gl_Position = czm_modelViewProjection * p; // position in clip coordinates - // With multi-frustum, when the ellipsoid primitive is positioned on the intersection of two frustums - // and close to terrain, the terrain (writes depth) in the closest frustum can overwrite part of the + // With multi-frustum, when the ellipsoid primitive is positioned on the intersection of two frustums + // and close to terrain, the terrain (writes depth) in the closest frustum can overwrite part of the // ellipsoid (does not write depth) that was rendered in the farther frustum. // // Here, we clamp the depth in the vertex shader to avoid being overwritten; however, this creates diff --git a/Source/Shaders/GlobeFS.glsl b/Source/Shaders/GlobeFS.glsl index 9b3b21a640a..8364c94ad00 100644 --- a/Source/Shaders/GlobeFS.glsl +++ b/Source/Shaders/GlobeFS.glsl @@ -1,5 +1,4 @@ //#define SHOW_TILE_BOUNDARIES - uniform vec4 u_initialColor; #if TEXTURE_UNITS > 0 diff --git a/Source/Shaders/PointPrimitiveCollectionFS.glsl b/Source/Shaders/PointPrimitiveCollectionFS.glsl index 42b5703fe74..92a9ee78861 100644 --- a/Source/Shaders/PointPrimitiveCollectionFS.glsl +++ b/Source/Shaders/PointPrimitiveCollectionFS.glsl @@ -46,4 +46,6 @@ void main() #else gl_FragColor = color; #endif + + czm_writeLogDepth(); } diff --git a/Source/Shaders/PointPrimitiveCollectionVS.glsl b/Source/Shaders/PointPrimitiveCollectionVS.glsl index 05c576606b8..6b23bb15284 100644 --- a/Source/Shaders/PointPrimitiveCollectionVS.glsl +++ b/Source/Shaders/PointPrimitiveCollectionVS.glsl @@ -147,8 +147,11 @@ void main() } #endif - vec4 positionWC = czm_eyeToWindowCoordinates(positionEC); +#ifdef LOG_DEPTH + czm_vertexLogDepth(czm_projection * positionEC); +#endif + vec4 positionWC = czm_eyeToWindowCoordinates(positionEC); gl_Position = czm_viewportOrthographic * vec4(positionWC.xy, -positionWC.z, 1.0); #ifdef DISABLE_DEPTH_DISTANCE @@ -167,6 +170,9 @@ void main() { // Position z on the near plane. gl_Position.z = -gl_Position.w; +#ifdef LOG_DEPTH + czm_vertexLogDepth(vec4(czm_currentFrustum.x)); +#endif } } #endif diff --git a/Source/Shaders/PolylineFS.glsl b/Source/Shaders/PolylineFS.glsl index 409f8ee5b42..604a7b80521 100644 --- a/Source/Shaders/PolylineFS.glsl +++ b/Source/Shaders/PolylineFS.glsl @@ -14,8 +14,9 @@ void main() czm_material material = czm_getMaterial(materialInput); gl_FragColor = vec4(material.diffuse + material.emission, material.alpha); - #ifdef VECTOR_TILE gl_FragColor *= u_highlightColor; #endif + + czm_writeLogDepth(); } diff --git a/Source/Shaders/PolylineVS.glsl b/Source/Shaders/PolylineVS.glsl index ce95b001a47..a43949d08a2 100644 --- a/Source/Shaders/PolylineVS.glsl +++ b/Source/Shaders/PolylineVS.glsl @@ -96,4 +96,8 @@ void main() v_st = vec2(texCoord, clamp(expandDir, 0.0, 1.0)); v_width = width; czm_pickColor = pickColor; + +#ifdef LOG_DEPTH + czm_vertexLogDepth(czm_modelViewProjectionRelativeToEye * p); +#endif } diff --git a/Source/Shaders/PostProcessFilters/PointCloudEyeDomeLighting.glsl b/Source/Shaders/PostProcessFilters/PointCloudEyeDomeLighting.glsl index 9a6f86d448f..3cdfc984b54 100644 --- a/Source/Shaders/PostProcessFilters/PointCloudEyeDomeLighting.glsl +++ b/Source/Shaders/PostProcessFilters/PointCloudEyeDomeLighting.glsl @@ -25,25 +25,28 @@ void main() { discard; } - else - { - vec4 color = texture2D(u_pointCloud_colorTexture, v_textureCoordinates); - // sample from neighbors up, down, left, right - float distX = u_distancesAndEdlStrength.x; - float distY = u_distancesAndEdlStrength.y; + vec4 color = texture2D(u_pointCloud_colorTexture, v_textureCoordinates); - vec2 responseAndCount = vec2(0.0); + // sample from neighbors up, down, left, right + float distX = u_distancesAndEdlStrength.x; + float distY = u_distancesAndEdlStrength.y; - responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(0, distY)); - responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(distX, 0)); - responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(0, -distY)); - responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(-distX, 0)); + vec2 responseAndCount = vec2(0.0); - float response = responseAndCount.x / responseAndCount.y; - float shade = exp(-response * 300.0 * u_distancesAndEdlStrength.z); - color.rgb *= shade; - gl_FragColor = vec4(color); - gl_FragDepthEXT = czm_eyeToWindowCoordinates(vec4(ecAlphaDepth.xyz, 1.0)).z; - } + responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(0, distY)); + responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(distX, 0)); + responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(0, -distY)); + responseAndCount += neighborContribution(ecAlphaDepth.a, vec2(-distX, 0)); + + float response = responseAndCount.x / responseAndCount.y; + float shade = exp(-response * 300.0 * u_distancesAndEdlStrength.z); + color.rgb *= shade; + gl_FragColor = vec4(color); + +#ifdef LOG_DEPTH + czm_writeLogDepth(1.0 + (czm_projection * vec4(ecAlphaDepth.xyz, 1.0)).w); +#else + gl_FragDepthEXT = czm_eyeToWindowCoordinates(vec4(ecAlphaDepth.xyz, 1.0)).z; +#endif } diff --git a/Source/Shaders/ShadowVolumeVS.glsl b/Source/Shaders/ShadowVolumeVS.glsl index 9d83f1a2958..9d3c550b3cd 100644 --- a/Source/Shaders/ShadowVolumeVS.glsl +++ b/Source/Shaders/ShadowVolumeVS.glsl @@ -36,7 +36,6 @@ void main() //extrudeDirection is zero for the top layer position = position + vec4(extrudeDirection * delta, 0.0); #endif - gl_Position = czm_depthClampFarPlane(czm_modelViewProjectionRelativeToEye * position); #endif } diff --git a/Source/Shaders/Vector3DTilePolylinesVS.glsl b/Source/Shaders/Vector3DTilePolylinesVS.glsl index e30c2559ee6..08690062050 100644 --- a/Source/Shaders/Vector3DTilePolylinesVS.glsl +++ b/Source/Shaders/Vector3DTilePolylinesVS.glsl @@ -19,4 +19,8 @@ void main() float angle; vec4 positionWC = getPolylineWindowCoordinatesEC(p, prev, next, expandDir, width, usePrev, angle); gl_Position = czm_viewportOrthographic * positionWC; + +#ifdef LOG_DEPTH + czm_vertexLogDepth(czm_projection * p); +#endif } diff --git a/Specs/Core/barycentricCoordinatesSpec.js b/Specs/Core/barycentricCoordinatesSpec.js index 8f425e1b87d..2738d287634 100644 --- a/Specs/Core/barycentricCoordinatesSpec.js +++ b/Specs/Core/barycentricCoordinatesSpec.js @@ -48,6 +48,15 @@ defineSuite([ expect(barycentricCoordinates(point, p0, p1, p2)).toEqualEpsilon(new Cartesian3(scalar, scalar, scalar), CesiumMath.EPSILON14); }); + it('evaluates with equal length sides', function() { + var p0 = new Cartesian3(9635312487071484, 13827945400273020, -16479219993905144); + var p1 = new Cartesian3(12832234.180639317, -10455085.701705107, 750010.7274386138); + var p2 = new Cartesian3(-9689011.10628853, -13420063.892507521, 750010.7274386119); + expect(barycentricCoordinates(p0, p0, p1, p2)).toEqual(Cartesian3.UNIT_X); + expect(barycentricCoordinates(p1, p0, p1, p2)).toEqual(Cartesian3.UNIT_Y); + expect(barycentricCoordinates(p2, p0, p1, p2)).toEqual(Cartesian3.UNIT_Z); + }); + it('throws without point', function() { expect(function() { barycentricCoordinates(); diff --git a/Specs/DataSources/EntityClusterSpec.js b/Specs/DataSources/EntityClusterSpec.js index e73142fb4b3..57b5706a9dc 100644 --- a/Specs/DataSources/EntityClusterSpec.js +++ b/Specs/DataSources/EntityClusterSpec.js @@ -30,6 +30,8 @@ defineSuite([ var scene; var cluster; + var depth; + var farDepth; beforeAll(function() { scene = createScene({ @@ -84,6 +86,13 @@ defineSuite([ scene.initializeFrame(); scene.render(); + + if (scene.logarithmicDepthBuffer) { + depth = farDepth = 0.1; + } else { + depth = 0.5; + farDepth = 0.9; + } }); afterAll(function() { @@ -151,13 +160,13 @@ defineSuite([ var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); var frameState = scene.frameState; cluster.update(frameState); @@ -184,13 +193,13 @@ defineSuite([ var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); cluster.enabled = true; cluster.update(scene.frameState); @@ -207,13 +216,13 @@ defineSuite([ var label = cluster.getLabel(entity); label.id = entity; label.text = 'a'; - label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); label = cluster.getLabel(entity); label.id = entity; label.text = 'b'; - label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); var frameState = scene.frameState; cluster.update(frameState); @@ -240,13 +249,13 @@ defineSuite([ var label = cluster.getLabel(entity); label.id = entity; label.text = 'a'; - label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); label = cluster.getLabel(entity); label.id = entity; label.text = 'b'; - label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + label.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); cluster.enabled = true; cluster.update(scene.frameState); @@ -263,13 +272,13 @@ defineSuite([ var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); var frameState = scene.frameState; cluster.update(frameState); @@ -296,13 +305,13 @@ defineSuite([ var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); cluster.enabled = true; cluster.update(scene.frameState); @@ -319,13 +328,13 @@ defineSuite([ var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); var frameState = scene.frameState; cluster.update(frameState); @@ -428,13 +437,13 @@ defineSuite([ var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); var frameState = scene.frameState; cluster.update(frameState); @@ -461,25 +470,25 @@ defineSuite([ var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, 0), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, 0), depth); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0, scene.canvas.clientHeight), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0, scene.canvas.clientHeight), depth); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.5); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth); var frameState = scene.frameState; cluster.update(frameState); @@ -506,13 +515,13 @@ defineSuite([ var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.9); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), farDepth); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); - billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.9); + billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), farDepth); var frameState = scene.frameState; cluster.update(frameState); @@ -550,13 +559,13 @@ defineSuite([ var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.9); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), farDepth); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; - point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.9); + point.position = SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), farDepth); var frameState = scene.frameState; cluster.update(frameState); @@ -587,7 +596,7 @@ defineSuite([ var entityCollection = dataSource.entities; entityCollection.add({ - position : SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), 0.9), + position : SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(0.0, 0.0), depth), billboard : { image : createBillboardImage() }, @@ -597,7 +606,7 @@ defineSuite([ }); entityCollection.add({ - position : SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), 0.9), + position : SceneTransforms.drawingBufferToWgs84Coordinates(scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth), billboard : { image : createBillboardImage() }, diff --git a/Specs/DataSources/KmlDataSourceSpec.js b/Specs/DataSources/KmlDataSourceSpec.js index 4cc46891001..64830addf29 100644 --- a/Specs/DataSources/KmlDataSourceSpec.js +++ b/Specs/DataSources/KmlDataSourceSpec.js @@ -16,6 +16,7 @@ defineSuite([ 'Core/JulianDate', 'Core/Math', 'Core/NearFarScalar', + 'Core/PerspectiveFrustum', 'Core/Rectangle', 'Core/RequestErrorEvent', 'Core/Resource', @@ -54,6 +55,7 @@ defineSuite([ JulianDate, CesiumMath, NearFarScalar, + PerspectiveFrustum, Rectangle, RequestErrorEvent, Resource, @@ -136,10 +138,7 @@ defineSuite([ upWC : new Cartesian3(0.0, 1.0, 0.0), pitch : 0.0, heading : 0.0, - frustum : { - aspectRatio : 1.0, - fov : CesiumMath.PI_OVER_FOUR - }, + frustum : new PerspectiveFrustum(), computeViewRectangle : function() { return Rectangle.MAX_VALUE; }, @@ -152,6 +151,8 @@ defineSuite([ clientHeight : 512 } }; + options.camera.frustum.fov = CesiumMath.PI_OVER_FOUR; + options.camera.frustum.aspectRatio = 1.0; beforeEach(function() { // Reset camera - x value will change during onStop tests diff --git a/Specs/Renderer/AutomaticUniformSpec.js b/Specs/Renderer/AutomaticUniformSpec.js index 72baabf820d..f439c7e1af6 100644 --- a/Specs/Renderer/AutomaticUniformSpec.js +++ b/Specs/Renderer/AutomaticUniformSpec.js @@ -1307,4 +1307,15 @@ defineSuite([ }).contextToRender(); }); + it('has czm_logFarDistance', function() { + var fs = + 'void main() {' + + ' gl_FragColor = vec4(czm_logFarDistance == (2.0 / log2(czm_currentFrustum.y + 1.0)));' + + '}'; + expect({ + context : context, + fragmentShader : fs + }).contextToRender(); + }); + }, 'WebGL'); diff --git a/Specs/Scene/BillboardCollectionSpec.js b/Specs/Scene/BillboardCollectionSpec.js index 2e7404ba411..5f3af46dd85 100644 --- a/Specs/Scene/BillboardCollectionSpec.js +++ b/Specs/Scene/BillboardCollectionSpec.js @@ -1401,7 +1401,7 @@ defineSuite([ var expected = BoundingSphere.fromPoints(projectedPositions); expected.center = new Cartesian3(0.0, expected.center.x, expected.center.y); expect(actual.center).toEqualEpsilon(expected.center, CesiumMath.EPSILON8); - expect(actual.radius).toBeGreaterThan(expected.radius); + expect(actual.radius).toBeGreaterThanOrEqualTo(expected.radius); }); it('computes bounding sphere in 2D', function() { diff --git a/Specs/Scene/ClassificationPrimitiveSpec.js b/Specs/Scene/ClassificationPrimitiveSpec.js index 959a793d14d..20026dfe32a 100644 --- a/Specs/Scene/ClassificationPrimitiveSpec.js +++ b/Specs/Scene/ClassificationPrimitiveSpec.js @@ -91,7 +91,7 @@ defineSuite([ beforeEach(function() { scene.morphTo3D(0); - rectangle = Rectangle.fromDegrees(-80.0, 20.0, -70.0, 30.0); + rectangle = Rectangle.fromDegrees(-75.0, 25.0, -70.0, 30.0); var depthColorAttribute = ColorGeometryInstanceAttribute.fromColor(new Color(0.0, 0.0, 1.0, 1.0)); depthColor = depthColorAttribute.value; @@ -326,7 +326,9 @@ defineSuite([ verifyClassificationPrimitiveRender(primitive, boxColor); }); - it('renders in Columbus view when scene3DOnly is false', function() { + // Rendering in 2D/CV is broken: + // https://github.com/AnalyticalGraphicsInc/cesium/issues/6308 + xit('renders in Columbus view when scene3DOnly is false', function() { if (!ClassificationPrimitive.isSupported(scene)) { return; } @@ -340,7 +342,7 @@ defineSuite([ verifyClassificationPrimitiveRender(primitive, boxColor); }); - it('renders in 2D when scene3DOnly is false', function() { + xit('renders in 2D when scene3DOnly is false', function() { if (!ClassificationPrimitive.isSupported(scene)) { return; } diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 251f2d23fba..47a9876fdda 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -554,15 +554,20 @@ defineSuite([ it('does not render labels that are behind the viewer', function() { var label = labels.add({ - position : new Cartesian3(20.0, 0.0, 0.0), // Behind camera + position : Cartesian3.ZERO, // in front of the camera text : 'x', horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER }); + expect(scene).toRenderAndCall(function(rgba) { + expect(rgba[0]).toBeGreaterThan(10); + }); + + label.position = new Cartesian3(20.0, 0.0, 0.0); // Behind camera expect(scene).toRender([0, 0, 0, 255]); - label.position = Cartesian3.ZERO; // Back in front of camera + label.position = new Cartesian3(1.0, 0.0, 0.0); // Back in front of camera expect(scene).toRenderAndCall(function(rgba) { expect(rgba[0]).toBeGreaterThan(10); }); @@ -2000,7 +2005,6 @@ defineSuite([ }); it('computes bounding sphere in Columbus view', function() { - // Disable collision detection to allow controlled camera position, // hence predictable bounding sphere size var originalEnableCollisionDetection = scene.screenSpaceCameraController.enableCollisionDetection; @@ -2030,7 +2034,7 @@ defineSuite([ var expected = BoundingSphere.fromPoints(projectedPositions); expected.center = new Cartesian3(0.0, expected.center.x, expected.center.y); expect(actual.center).toEqualEpsilon(expected.center, CesiumMath.EPSILON8); - expect(actual.radius).toBeGreaterThan(expected.radius); + expect(actual.radius).toBeGreaterThanOrEqualTo(expected.radius); scene.screenSpaceCameraController.enableCollisionDetection = originalEnableCollisionDetection; }); diff --git a/Specs/Scene/MultifrustumSpec.js b/Specs/Scene/MultifrustumSpec.js index 9bf98da5430..2bb926965d0 100644 --- a/Specs/Scene/MultifrustumSpec.js +++ b/Specs/Scene/MultifrustumSpec.js @@ -63,7 +63,13 @@ defineSuite([ var blueImage; var whiteImage; + var logDepth; + beforeAll(function() { + scene = createScene(); + logDepth = scene.logarithmicDepthBuffer; + scene.destroyForSpecs(); + return when.join( Resource.fetchImage('./Data/Images/Green.png').then(function(image) { greenImage = image; @@ -81,6 +87,8 @@ defineSuite([ context = scene.context; primitives = scene.primitives; + scene.logarithmicDepthBuffer = false; + var camera = scene.camera; camera.position = new Cartesian3(); camera.direction = Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()); @@ -196,7 +204,8 @@ defineSuite([ var calls = DrawCommand.prototype.execute.calls.all(); var billboardCall; - for (var i = 0; i < calls.length; ++i) { + var i; + for (i = 0; i < calls.length; ++i) { if (calls[i].object.owner instanceof BillboardCollection) { billboardCall = calls[i]; break; @@ -205,7 +214,16 @@ defineSuite([ expect(billboardCall).toBeDefined(); expect(billboardCall.args.length).toEqual(2); - expect(billboardCall.object.shaderProgram.fragmentShaderSource.sources[1]).toContain('czm_Debug_main'); + + var found = false; + var sources = billboardCall.object.shaderProgram.fragmentShaderSource.sources; + for (var j = 0; j < sources.length; ++j) { + if (sources[i].indexOf('czm_Debug_main') !== -1) { + found = true; + break; + } + } + expect(found).toBe(true); }); function createPrimitive(bounded, closestFrustum) { @@ -340,4 +358,19 @@ defineSuite([ createBillboards(); scene.renderForSpecs(); }); + + it('log depth uses less frustums', function() { + if (!logDepth) { + return; + } + + createBillboards(); + + scene.render(); + expect(scene._frustumCommandsList.length).toEqual(3); + + scene.logarithmicDepthBuffer = true; + scene.render(); + expect(scene._frustumCommandsList.length).toEqual(1); + }); }, 'WebGL'); diff --git a/Specs/Scene/PointCloud3DTileContentSpec.js b/Specs/Scene/PointCloud3DTileContentSpec.js index bb4064d13c2..7457aabbb13 100644 --- a/Specs/Scene/PointCloud3DTileContentSpec.js +++ b/Specs/Scene/PointCloud3DTileContentSpec.js @@ -441,11 +441,12 @@ defineSuite([ }); }); - var noAttenuationPixelCount = 16; + var noAttenuationPixelCount; function attenuationTest(postLoadCallback) { var scene = createScene({ canvas : createCanvas(10, 10) }); + noAttenuationPixelCount = scene.logarithmicDepthBuffer ? 20 : 16; var center = new Cartesian3.fromRadians(centerLongitude, centerLatitude, 5.0); scene.camera.lookAt(center, new HeadingPitchRange(0.0, -1.57, 5.0)); scene.fxaa = false; @@ -497,19 +498,6 @@ defineSuite([ }); }); - it('modulates attenuation using the baseResolution parameter', function() { - return attenuationTest(function(scene, tileset) { - tileset.pointCloudShading.attenuation = true; - tileset.pointCloudShading.geometricErrorScale = 1.0; - tileset.pointCloudShading.maximumAttenuation = undefined; - tileset.pointCloudShading.baseResolution = CesiumMath.EPSILON20; - tileset.maximumScreenSpaceError = 16; - expect(scene).toRenderPixelCountAndCall(function(pixelCount) { - expect(pixelCount).toEqual(noAttenuationPixelCount); - }); - }); - }); - it('modulates attenuation using the baseResolution parameter', function() { return attenuationTest(function(scene, tileset) { // pointCloudNoColorUrl is a single tile with GeometricError = 0, diff --git a/Specs/Scene/PointPrimitiveCollectionSpec.js b/Specs/Scene/PointPrimitiveCollectionSpec.js index 05d3deaec43..381908d6c52 100644 --- a/Specs/Scene/PointPrimitiveCollectionSpec.js +++ b/Specs/Scene/PointPrimitiveCollectionSpec.js @@ -907,7 +907,7 @@ defineSuite([ var expected = BoundingSphere.fromPoints(projectedPositions); expected.center = new Cartesian3(0.0, expected.center.x, expected.center.y); expect(actual.center).toEqualEpsilon(expected.center, CesiumMath.EPSILON8); - expect(actual.radius).toBeGreaterThan(expected.radius); + expect(actual.radius).toBeGreaterThanOrEqualTo(expected.radius); }); it('computes bounding sphere in 2D', function() { diff --git a/Specs/Scene/SceneSpec.js b/Specs/Scene/SceneSpec.js index 55d5565e247..dc04e260b31 100644 --- a/Specs/Scene/SceneSpec.js +++ b/Specs/Scene/SceneSpec.js @@ -251,6 +251,9 @@ defineSuite([ }); it('debugCommandFilter does not filter commands', function() { + var originalLogDepth = scene.logarithmicDepthBuffer; + scene.logarithmicDepthBuffer = false; + var c = new DrawCommand({ shaderProgram : simpleShaderProgram, renderState : simpleRenderState, @@ -264,9 +267,14 @@ defineSuite([ expect(scene.debugCommandFilter).toBeUndefined(); scene.renderForSpecs(); expect(c.execute).toHaveBeenCalled(); + + scene.logarithmicDepthBuffer = originalLogDepth; }); it('debugShowBoundingVolume draws a bounding sphere', function() { + var originalLogDepth = scene.logarithmicDepthBuffer; + scene.logarithmicDepthBuffer = false; + var radius = 10.0; var center = Cartesian3.add(scene.camera.position, scene.camera.direction, new Cartesian3()); @@ -285,9 +293,14 @@ defineSuite([ expect(scene).toRenderAndCall(function(rgba) { expect(rgba[0]).not.toEqual(0); // Red bounding sphere }); + + scene.logarithmicDepthBuffer = originalLogDepth; }); it('debugShowCommands tints commands', function() { + var originalLogDepth = scene.logarithmicDepthBuffer; + scene.logarithmicDepthBuffer = false; + var c = new DrawCommand({ shaderProgram : simpleShaderProgram, renderState : simpleRenderState, @@ -308,6 +321,8 @@ defineSuite([ scene.renderForSpecs(); expect(c._debugColor).toBeDefined(); scene.debugShowCommands = false; + + scene.logarithmicDepthBuffer = originalLogDepth; }); it('debugShowFramesPerSecond', function() { @@ -713,15 +728,12 @@ defineSuite([ scene.destroyForSpecs(); }); - var pickedPosition3D = new Cartesian3(-455845.46867895435, -5210337.548977215, 3637549.8562320103); - var pickedPosition2D = new Cartesian3(-455861.7055871038, -5210523.137686572, 3637866.6638769475); - it('pickPosition', function() { if (!scene.pickPositionSupported) { return; } - var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); + var rectangle = Rectangle.fromDegrees(-0.0001, -0.0001, 0.0001, 0.0001); scene.camera.setView({ destination : rectangle }); var canvas = scene.canvas; @@ -740,7 +752,9 @@ defineSuite([ expect(scene).toRenderAndCall(function() { var position = scene.pickPosition(windowPosition); - expect(position).toEqualEpsilon(pickedPosition3D, CesiumMath.EPSILON6); + expect(position.x).toBeGreaterThan(Ellipsoid.WGS84.minimumRadius); + expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); + expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); }); }); @@ -751,7 +765,7 @@ defineSuite([ scene.morphToColumbusView(0.0); - var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); + var rectangle = Rectangle.fromDegrees(-0.0001, -0.0001, 0.0001, 0.0001); scene.camera.setView({ destination : rectangle }); var canvas = scene.canvas; @@ -770,7 +784,9 @@ defineSuite([ expect(scene).toRenderAndCall(function() { var position = scene.pickPosition(windowPosition); - expect(position).toEqualEpsilon(pickedPosition2D, CesiumMath.EPSILON6); + expect(position.x).toBeGreaterThan(Ellipsoid.WGS84.minimumRadius); + expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); + expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); }); }); @@ -781,7 +797,7 @@ defineSuite([ scene.morphTo2D(0.0); - var rectangle = Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0); + var rectangle = Rectangle.fromDegrees(-0.0001, -0.0001, 0.0001, 0.0001); scene.camera.setView({ destination : rectangle }); var canvas = scene.canvas; @@ -800,7 +816,9 @@ defineSuite([ expect(scene).toRenderAndCall(function() { var position = scene.pickPosition(windowPosition); - expect(position).toEqualEpsilon(pickedPosition2D, CesiumMath.EPSILON6); + expect(position.x).toBeGreaterThan(Ellipsoid.WGS84.minimumRadius); + expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); + expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); }); }); @@ -867,33 +885,6 @@ defineSuite([ var position = scene.pickPosition(windowPosition); expect(position).toBeDefined(); }); - - var rectanglePrimitive2 = createRectangle(rectangle); - rectanglePrimitive2.appearance.material.uniforms.color = new Color(0.0, 1.0, 0.0, 0.5); - primitives.add(rectanglePrimitive2); - - expect(scene).toRenderAndCall(function() { - var position = scene.pickPosition(windowPosition); - expect(position).toBeDefined(); - - var commandList = scene.frameState.commandList; - expect(commandList.length).toEqual(2); - - var command1 = commandList[0]; - var command2 = commandList[1]; - - expect(command1.derivedCommands).toBeDefined(); - expect(command2.derivedCommands).toBeDefined(); - - expect(command1.derivedCommands.depth).toBeDefined(); - expect(command2.derivedCommands.depth).toBeDefined(); - - expect(command1.derivedCommands.depth.depthOnlyCommand).toBeDefined(); - expect(command2.derivedCommands.depth.depthOnlyCommand).toBeDefined(); - - expect(command1.derivedCommands.depth.depthOnlyCommand.shaderProgram).toEqual(command2.derivedCommands.depth.depthOnlyCommand.shaderProgram); - expect(command1.derivedCommands.depth.depthOnlyCommand.renderState).toEqual(command2.derivedCommands.depth.depthOnlyCommand.renderState); - }); }); it('pickPosition caches results per frame',function(){ diff --git a/Specs/Scene/ShadowMapSpec.js b/Specs/Scene/ShadowMapSpec.js index 7439d663470..3b702985d4e 100644 --- a/Specs/Scene/ShadowMapSpec.js +++ b/Specs/Scene/ShadowMapSpec.js @@ -1116,9 +1116,18 @@ defineSuite([ scene.render(); } - // Expect derived commands to be updated twice for both the floor and box, - // once on the first frame and again when the shadow map is dirty - expect(spy.calls.count()).toEqual(4); + var callCount; + if (!scene.logarithmicDepthBuffer) { + // Expect derived commands to be updated twice for both the floor and box, + // once on the first frame and again when the shadow map is dirty + callCount = 4; + } else { + // Same as without log z, but doubled. The derived cast commands do not write log z, but + // the derived receive commands do. + callCount = 8; + } + + expect(spy.calls.count()).toEqual(callCount); box.show = false; floor.show = false; diff --git a/Specs/Scene/Vector3DTileGeometrySpec.js b/Specs/Scene/Vector3DTileGeometrySpec.js index 0d9b28d4b17..d9ad9629df3 100644 --- a/Specs/Scene/Vector3DTileGeometrySpec.js +++ b/Specs/Scene/Vector3DTileGeometrySpec.js @@ -355,7 +355,7 @@ defineSuite([ }); it('renders a single ellipsoid' + webglMessage, function() { - var radii = new Cartesian3(1000000.0, 1000000.0, 1000000.0); + var radii = new Cartesian3(500000.0, 500000.0, 500000.0); var ellipsoid = packEllipsoids([{ modelMatrix : Matrix4.IDENTITY, radii : radii @@ -390,7 +390,7 @@ defineSuite([ }); it('renders a single sphere' + webglMessage, function() { - var radius = 1000000.0; + var radius = 500000.0; var sphere = packSpheres([{ radius : radius, modelMatrix : Matrix4.IDENTITY @@ -570,7 +570,7 @@ defineSuite([ }); it('renders with inverted classification' + webglMessage, function() { - var radii = new Cartesian3(10.0, 10.0, 1000.0); + var radii = new Cartesian3(100.0, 100.0, 1000.0); var ellipsoids = packEllipsoids([{ modelMatrix : Matrix4.IDENTITY, radii : radii @@ -597,18 +597,16 @@ defineSuite([ batchTable : batchTable })); return loadGeometries(geometry).then(function() { - scene.camera.lookAtTransform(modelMatrix, new Cartesian3(radii.x, 0.0, 1.0)); + scene.camera.lookAtTransform(modelMatrix, new Cartesian3(0.0, 0.0, 1.0)); + expect(scene).toRender([255, 255, 255, 255]); + scene.camera.lookAtTransform(modelMatrix, new Cartesian3(radii.x, 0.0, 1.0)); expect(scene).toRender([255, 0, 0, 255]); scene.invertClassification = true; scene.invertClassificationColor = new Color(0.25, 0.25, 0.25, 1.0); - expect(scene).toRender([64, 0, 0, 255]); - scene.camera.lookAtTransform(modelMatrix, new Cartesian3(0.0, 0.0, 1.0)); - expect(scene).toRender([255, 255, 255, 255]); - scene.invertClassification = false; }); }); @@ -662,13 +660,13 @@ defineSuite([ geometry = scene.primitives.add(new Vector3DTileGeometry({ ellipsoids : packEllipsoids([{ modelMatrix : Matrix4.IDENTITY, - radii : new Cartesian3(1000000.0, 1000000.0, 1000000.0) + radii : new Cartesian3(500000.0, 500000.0, 500000.0) }]), ellipsoidBatchIds : new Uint16Array([0]), center : center, modelMatrix : modelMatrix, batchTable : batchTable, - boundingVolume : new BoundingSphere(center, 1000000.0) + boundingVolume : new BoundingSphere(center, 500000.0) })); return loadGeometries(geometry).then(function() { scene.camera.setView({