diff --git a/examples/jsm/tsl/display/GTAONode.js b/examples/jsm/tsl/display/GTAONode.js index 3a7730fb65b7d2..6f79faeb2b1b79 100644 --- a/examples/jsm/tsl/display/GTAONode.js +++ b/examples/jsm/tsl/display/GTAONode.js @@ -1,5 +1,5 @@ import { DataTexture, RenderTarget, RepeatWrapping, Vector2, Vector3 } from 'three'; -import { getNormalFromDepth, getScreenPosition, getViewPosition, QuadMesh, TempNode, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec2, vec3, vec4, int, dot, max, pow, abs, If, textureSize, sin, cos, PI, texture, passTexture, mat3, add, normalize, mul, cross, div, mix, sqrt, sub, acos, clamp, NodeMaterial, PostProcessingUtils } from 'three/tsl'; +import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalFromDepth, getScreenPosition, getViewPosition, QuadMesh, TempNode, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec2, vec3, vec4, int, dot, max, pow, abs, If, textureSize, sin, cos, PI, texture, passTexture, mat3, add, normalize, mul, cross, div, mix, sqrt, sub, acos, clamp, NodeMaterial, PostProcessingUtils } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -27,6 +27,9 @@ class GTAONode extends TempNode { this.resolutionScale = 1; + this.cameraNear = reference( 'near', 'float', camera ); + this.cameraFar = reference( 'far', 'float', camera ); + this.radius = uniform( 0.25 ); this.resolution = uniform( new Vector2() ); this.thickness = uniform( 1 ); @@ -107,7 +110,22 @@ class GTAONode extends TempNode { const uvNode = uv(); - const sampleDepth = ( uv ) => this.depthNode.uv( uv ).x; + const sampleDepth = ( uv ) => { + + const depth = this.depthNode.uv( uv ).x; + + if ( builder.renderer.logarithmicDepthBuffer === true ) { + + const viewZ = logarithmicDepthToViewZ( depth, this.cameraNear, this.cameraFar ); + + return viewZToPerspectiveDepth( viewZ, this.cameraNear, this.cameraFar ); + + } + + return depth; + + }; + const sampleNoise = ( uv ) => this._noiseNode.uv( uv ); const sampleNormal = ( uv ) => ( this.normalNode !== null ) ? this.normalNode.uv( uv ).rgb.normalize() : getNormalFromDepth( uv, this.depthNode.value, this._cameraProjectionMatrixInverse ); diff --git a/src/materials/nodes/NodeMaterial.js b/src/materials/nodes/NodeMaterial.js index 1ecb1e6064aa6e..f8d4afb4f1d2a9 100644 --- a/src/materials/nodes/NodeMaterial.js +++ b/src/materials/nodes/NodeMaterial.js @@ -18,7 +18,7 @@ import { float, vec3, vec4 } from '../../nodes/tsl/TSLBase.js'; import AONode from '../../nodes/lighting/AONode.js'; import { lightingContext } from '../../nodes/lighting/LightingContextNode.js'; import IrradianceNode from '../../nodes/lighting/IrradianceNode.js'; -import { depth, perspectiveDepthToLogarithmicDepth, viewZToOrthographicDepth } from '../../nodes/display/ViewportDepthNode.js'; +import { depth, viewZToLogarithmicDepth, viewZToOrthographicDepth } from '../../nodes/display/ViewportDepthNode.js'; import { cameraFar, cameraNear } from '../../nodes/accessors/Camera.js'; import { clipping, clippingAlpha, hardwareClipping } from '../../nodes/accessors/ClippingNode.js'; import NodeMaterialObserver from './manager/NodeMaterialObserver.js'; @@ -285,7 +285,7 @@ class NodeMaterial extends Material { if ( camera.isPerspectiveCamera ) { - depthNode = perspectiveDepthToLogarithmicDepth( modelViewProjection().w, cameraNear, cameraFar ); + depthNode = viewZToLogarithmicDepth( positionView.z, cameraNear, cameraFar ); } else { diff --git a/src/nodes/display/ViewportDepthNode.js b/src/nodes/display/ViewportDepthNode.js index 201d1c6efdb7d8..a0211891d309db 100644 --- a/src/nodes/display/ViewportDepthNode.js +++ b/src/nodes/display/ViewportDepthNode.js @@ -1,5 +1,5 @@ import Node from '../core/Node.js'; -import { log2, nodeImmutable, nodeProxy } from '../tsl/TSLBase.js'; +import { float, log, log2, nodeImmutable, nodeProxy } from '../tsl/TSLBase.js'; import { cameraNear, cameraFar } from '../accessors/Camera.js'; import { positionView } from '../accessors/Position.js'; import { viewportDepthTexture } from './ViewportDepthTextureNode.js'; @@ -116,8 +116,10 @@ export const viewZToPerspectiveDepth = ( viewZ, near, far ) => near.add( viewZ ) // maps perspective depth in [ 0, 1 ] to viewZ export const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) ); -export const perspectiveDepthToLogarithmicDepth = ( perspectiveW, near, far ) => { +// -near maps to 0; -far maps to 1 +export const viewZToLogarithmicDepth = ( viewZ, near, far ) => { + // NOTE: viewZ must be negative--see explanation at the end of this comment block. // The final logarithmic depth formula used here is adapted from one described in an // article by Thatcher Ulrich (see http://tulrich.com/geekstuff/log_depth_buffer.txt), // which was an improvement upon an earlier formula one described in an @@ -139,15 +141,28 @@ export const perspectiveDepthToLogarithmicDepth = ( perspectiveW, near, far ) => // 1. Clamp the camera near plane so we don't divide by 0. // 2. Use log2 instead of log to avoid an extra multiply (shaders implement log using log2). // 3. Assume K is 1 (K = maximum value in depth buffer; see Ulrich's formula above). - // 4. Add 1 to each division by cameraNear to ensure the depth curve is shifted to the left as cameraNear increases. - // For visual representation of this depth curve, see https://www.desmos.com/calculator/lz5rqfysih + // 4. To maintain consistency with the functions "viewZToOrthographicDepth" and "viewZToPerspectiveDepth", + // we modify the formula here to use 'viewZ' instead of 'w'. The other functions expect a negative viewZ, + // so we do the same here, hence the 'viewZ.negate()' call. + // For visual representation of this depth curve, see https://www.desmos.com/calculator/uyqk0vex1u near = near.max( 1e-6 ).toVar(); - const numerator = log2( perspectiveW.div( near ).add( 1 ) ); - const denominator = log2( far.div( near ).add( 1 ) ); + const numerator = log2( viewZ.negate().div( near ) ); + const denominator = log2( far.div( near ) ); return numerator.div( denominator ); }; +// maps logarithmic depth in [ 0, 1 ] to viewZ +export const logarithmicDepthToViewZ = ( depth, near, far ) => { + + // NOTE: we add a 'negate()' call to the return value here to maintain consistency with + // the functions "orthographicDepthToViewZ" and "perspectiveDepthToViewZ" (they return + // a negative viewZ). + const exponent = depth.mul( log( far.div( near ) ) ); + return float( Math.E ).pow( exponent ).mul( near ).negate(); + +}; + const depthBase = /*@__PURE__*/ nodeProxy( ViewportDepthNode, ViewportDepthNode.DEPTH_BASE ); export const depth = /*@__PURE__*/ nodeImmutable( ViewportDepthNode, ViewportDepthNode.DEPTH ); diff --git a/src/nodes/lighting/ShadowNode.js b/src/nodes/lighting/ShadowNode.js index f8840052418e27..69e3c6195b4654 100644 --- a/src/nodes/lighting/ShadowNode.js +++ b/src/nodes/lighting/ShadowNode.js @@ -15,7 +15,7 @@ import { Loop } from '../utils/LoopNode.js'; import { screenCoordinate } from '../display/ScreenNode.js'; import { HalfFloatType, LessCompare, RGFormat, VSMShadowMap, WebGPUCoordinateSystem } from '../../constants.js'; import { renderGroup } from '../core/UniformGroupNode.js'; -import { perspectiveDepthToLogarithmicDepth } from '../display/ViewportDepthNode.js'; +import { viewZToLogarithmicDepth } from '../display/ViewportDepthNode.js'; const BasicShadowMap = Fn( ( { depthTexture, shadowCoord } ) => { @@ -313,10 +313,10 @@ class ShadowNode extends Node { // The normally available "cameraNear" and "cameraFar" nodes cannot be used here because they do not get // updated to use the shadow camera. So, we have to declare our own "local" ones here. // TODO: How do we get the cameraNear/cameraFar nodes to use the shadow camera so we don't have to declare local ones here? - const cameraNearLocal = uniform( 'float' ).onRenderUpdate( () => shadow.camera.near ); - const cameraFarLocal = uniform( 'float' ).onRenderUpdate( () => shadow.camera.far ); + const cameraNearLocal = reference( 'near', 'float', shadow.camera ).setGroup( renderGroup ); + const cameraFarLocal = reference( 'far', 'float', shadow.camera ).setGroup( renderGroup ); - coordZ = perspectiveDepthToLogarithmicDepth( w, cameraNearLocal, cameraFarLocal ); + coordZ = viewZToLogarithmicDepth( w.negate(), cameraNearLocal, cameraFarLocal ); }