From ae9bebf940f271d18a90e9a2824779170f523808 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 7 Feb 2020 10:23:13 +1100 Subject: [PATCH] Account for slightly changing log depth equation when reversing. Cleanup. --- Source/Renderer/AutomaticUniforms.js | 45 +++++++++++++++++++ Source/Renderer/UniformState.js | 43 ++++++++++++++++++ Source/Scene/SceneTransforms.js | 5 ++- .../Builtin/Functions/reverseLogDepth.glsl | 5 ++- .../Functions/windowToEyeCoordinates.glsl | 15 +++++-- .../Builtin/Functions/writeLogDepth.glsl | 6 +-- 6 files changed, 107 insertions(+), 12 deletions(-) diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js index a66f47128f5c..9eac506f5f3a 100644 --- a/Source/Renderer/AutomaticUniforms.js +++ b/Source/Renderer/AutomaticUniforms.js @@ -1131,6 +1131,51 @@ import WebGLConstants from '../Core/WebGLConstants.js'; } }), + /** + * Gets the far plane's distance from the near plane, plus 1.0. + * + * @alias czm_farDepthFromNearPlusOne + * @namespace + * @glslUniform + */ + czm_farDepthFromNearPlusOne : new AutomaticUniform({ + size: 1, + datatype : WebGLConstants.FLOAT, + getValue : function(uniformState) { + return uniformState.farDepthFromNearPlusOne; + } + }), + + /** + * Gets the log2 of {@link AutomaticUniforms#czm_farDepthFromNearPlusOne}. + * + * @alias czm_oneOverLog2FarDepthFromNearPlusOne + * @namespace + * @glslUniform + */ + czm_log2FarDepthFromNearPlusOne : new AutomaticUniform({ + size: 1, + datatype : WebGLConstants.FLOAT, + getValue : function(uniformState) { + return uniformState.log2FarDepthFromNearPlusOne; + } + }), + + /** + * Gets 1.0 divided by {@link AutomaticUniforms#czm_log2FarDepthFromNearPlusOne}. + * + * @alias czm_oneOverLog2FarDepthFromNearPlusOne + * @namespace + * @glslUniform + */ + czm_oneOverLog2FarDepthFromNearPlusOne : new AutomaticUniform({ + size: 1, + datatype : WebGLConstants.FLOAT, + getValue : function(uniformState) { + return uniformState.oneOverLog2FarDepthFromNearPlusOne; + } + }), + /** * The log2 of the current frustums far plane. Used for computing the log depth. * diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js index 98e6f52e47a0..9c344f70141f 100644 --- a/Source/Renderer/UniformState.js +++ b/Source/Renderer/UniformState.js @@ -44,6 +44,9 @@ import SunLight from '../Scene/SunLight.js'; this._entireFrustum = new Cartesian2(); this._currentFrustum = new Cartesian2(); this._frustumPlanes = new Cartesian4(); + this._farDepthFromNearPlusOne = undefined; + this._log2FarDepthFromNearPlusOne = undefined; + this._oneOverLog2FarDepthFromNearPlusOne = undefined; this._log2FarDistance = undefined; this._log2FarPlusOne = undefined; this._log2NearDistance = undefined; @@ -637,6 +640,42 @@ import SunLight from '../Scene/SunLight.js'; } }, + /** + * The far plane's distance from the near plane, plus 1.0. + * + * @memberof UniformState.prototype + * @type {Number} + */ + farDepthFromNearPlusOne : { + get : function() { + return this._farDepthFromNearPlusOne; + } + }, + + /** + * The log2 of {@link UniformState#farDepthFromNearPlusOne}. + * + * @memberof UniformState.prototype + * @type {Number} + */ + log2FarDepthFromNearPlusOne : { + get : function() { + return this._log2FarDepthFromNearPlusOne; + } + }, + + /** + * 1.0 divided by {@link UniformState#log2FarDepthFromNearPlusOne}. + * + * @memberof UniformState.prototype + * @type {Number} + */ + oneOverLog2FarDepthFromNearPlusOne : { + get : function() { + return this._oneOverLog2FarDepthFromNearPlusOne; + } + }, + /** * The log2 of the current frustum's far distance. Used to compute the log depth. * @memberof UniformState.prototype @@ -1110,6 +1149,10 @@ import SunLight from '../Scene/SunLight.js'; this._currentFrustum.x = frustum.near; this._currentFrustum.y = frustum.far; + this._farDepthFromNearPlusOne = (frustum.far - frustum.near) + 1.0; + this._log2FarDepthFromNearPlusOne = CesiumMath.log2(this._farDepthFromNearPlusOne); + this._oneOverLog2FarDepthFromNearPlusOne = 1.0 / this._log2FarDepthFromNearPlusOne; + this._log2FarDistance = 2.0 / CesiumMath.log2(frustum.far + 1.0); this._log2FarPlusOne = CesiumMath.log2(frustum.far + 1.0); this._log2NearDistance = CesiumMath.log2(frustum.near + 1.0); diff --git a/Source/Scene/SceneTransforms.js b/Source/Scene/SceneTransforms.js index b0ee16c4ef3f..62613b375dc1 100644 --- a/Source/Scene/SceneTransforms.js +++ b/Source/Scene/SceneTransforms.js @@ -295,8 +295,9 @@ import SceneMode from './SceneMode.js'; // 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 log2Depth = depth * uniformState.log2FarDepthFromNearPlusOne; + var depthFromNear = Math.pow(2.0, log2Depth) - 1.0; + depth = far * (1.0 - near / (depthFromNear + near)) / (far - near); } var viewport = scene.view.passState.viewport; diff --git a/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl b/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl index c6033d6d8782..d28f45494104 100644 --- a/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl +++ b/Source/Shaders/Builtin/Functions/reverseLogDepth.glsl @@ -3,8 +3,9 @@ float czm_reverseLogDepth(float logZ) #ifdef LOG_DEPTH float near = czm_currentFrustum.x; float far = czm_currentFrustum.y; - logZ = pow(2.0, logZ * czm_log2FarPlusOne) - 1.0; - logZ = far * (1.0 - near / logZ) / (far - near); + float log2Depth = logZ * czm_log2FarDepthFromNearPlusOne; + float depthFromNear = pow(2.0, log2Depth) - 1.0; + return far * (1.0 - near / (depthFromNear + near)) / (far - near); #endif return logZ; } diff --git a/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl b/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl index 941da24d8e12..00bdd3929947 100644 --- a/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl +++ b/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl @@ -3,7 +3,7 @@ * The transform from window to normalized device coordinates is done using components * of (@link czm_viewport} and {@link czm_viewportTransformation} instead of calculating * the inverse of czm_viewportTransformation. The transformation from - * normalized device coordinates to clip coordinates is done using positionWC.w, + * normalized device coordinates to clip coordinates is done using fragmentCoordinate.w, * which is expected to be the scalar used in the perspective divide. The transformation * from clip to eye coordinates is done using {@link czm_inverseProjection}. * @@ -25,12 +25,16 @@ */ vec4 czm_windowToEyeCoordinates(vec4 fragmentCoordinate) { + // Reconstruct NDC coordinates float x = 2.0 * (fragmentCoordinate.x - czm_viewport.x) / czm_viewport.z - 1.0; float y = 2.0 * (fragmentCoordinate.y - czm_viewport.y) / czm_viewport.w - 1.0; float z = (fragmentCoordinate.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2]; vec4 q = vec4(x, y, z, 1.0); + + // Reverse the perspective division to obtain clip coordinates. q /= fragmentCoordinate.w; + // Reverse the projection transformation to obtain eye coordinates. if (!(czm_inverseProjection == mat4(0.0))) // IE and Edge sometimes do something weird with != between mat4s { q = czm_inverseProjection * q; @@ -80,10 +84,13 @@ vec4 czm_windowToEyeCoordinates(vec2 fragmentCoordinateXY, float depthOrLogDepth #ifdef LOG_DEPTH float near = czm_currentFrustum.x; float far = czm_currentFrustum.y; - float unscaledDepth = pow(2.0, depthOrLogDepth * czm_log2FarPlusOne) - 1.0; - vec4 windowCoord = vec4(fragmentCoordinateXY, far * (1.0 - near / unscaledDepth) / (far - near), 1.0); + float log2Depth = depthOrLogDepth * czm_log2FarDepthFromNearPlusOne; + float depthFromNear = pow(2.0, log2Depth) - 1.0; + float depthFromCamera = depthFromNear + near; + vec4 windowCoord = vec4(fragmentCoordinateXY, far * (1.0 - near / depthFromCamera) / (far - near), 1.0); vec4 eyeCoordinate = czm_windowToEyeCoordinates(windowCoord); - eyeCoordinate.w = 1.0 / unscaledDepth; // Better precision + eyeCoordinate.w = 1.0 / depthFromCamera; // Better precision + return eyeCoordinate; #else vec4 windowCoord = vec4(fragmentCoordinateXY, depthOrLogDepth, 1.0); vec4 eyeCoordinate = czm_windowToEyeCoordinates(windowCoord); diff --git a/Source/Shaders/Builtin/Functions/writeLogDepth.glsl b/Source/Shaders/Builtin/Functions/writeLogDepth.glsl index 5ef39b84ead7..d0b1481489e7 100644 --- a/Source/Shaders/Builtin/Functions/writeLogDepth.glsl +++ b/Source/Shaders/Builtin/Functions/writeLogDepth.glsl @@ -25,13 +25,11 @@ uniform vec2 u_polygonOffset; void czm_writeLogDepth(float depth) { #if defined(GL_EXT_frag_depth) && defined(LOG_DEPTH) - float farDepth = 1.0 - czm_currentFrustum.x + czm_currentFrustum.y; - // Discard the vertex if it's not between the near and far planes. // We allow a bit of epsilon on the near plane comparison because a 1.0 // from the vertex shader (indicating the vertex should be _on_ the near // plane) will not necessarily come here as exactly 1.0. - if (depth <= 0.9999999 || depth > farDepth) { + if (depth <= 0.9999999 || depth > czm_farDepthFromNearPlusOne) { discard; } @@ -53,7 +51,7 @@ void czm_writeLogDepth(float depth) #endif - gl_FragDepthEXT = log2(depth) / log2(farDepth); + gl_FragDepthEXT = log2(depth) * czm_oneOverLog2FarDepthFromNearPlusOne; #ifdef POLYGON_OFFSET // Apply the units after the log depth.