Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/materials/nodes/NodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,6 @@ class NodeMaterial extends Material {

if ( camera.isPerspectiveCamera ) {

// Note: normally we could use "float( camera.near )" and "float( camera.far )" for the near/far arguments, but
// there is currently a bug with TSL/Three Shading Language whereby a "float()" expression using a huge value
// in scientific notation like "float( 1e27 )" will output "1e+27.0" to the shader code, which is causing problems.
// Since it's possible that camera.near/camera.far values may be using huge values like this (such as the logarithmic
// depth buffer examples on threejs.org), we must use the cameraNear/cameraFar nodes for now.
// TODO: can the float() node be fixed to allow for expressions like "float( 1e27 )"?
depthNode = perspectiveDepthToLogarithmicDepth( modelViewProjection().w, cameraNear, cameraFar );

} else {
Expand Down
41 changes: 24 additions & 17 deletions src/nodes/display/ViewportDepthNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,33 @@ export const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).d

export const perspectiveDepthToLogarithmicDepth = ( perspectiveW, near, far ) => {

// 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
// 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
// Outerra article (https://outerra.blogspot.com/2009/08/logarithmic-z-buffer.html).
// The Outerra article ignored the camera near plane (it always assumed it was 0) and instead
// Ulrich's formula is the following:
// z = K * log( w / cameraNear ) / log( cameraFar / cameraNear )
// where K = 2^k - 1, and k is the number of bits in the depth buffer.
// The Outerra variant ignored the camera near plane (it assumed it was 0) and instead
// opted for a "C-constant" for resolution adjustment of objects near the camera.
// Outerra states this about their own formula: "Notice that the 'C' variant doesn’t use a near
// plane distance, it has it set at 0." (quote from https://outerra.blogspot.com/2012/11/maximizing-depth-buffer-range-and.html)
// It was debated here whether Outerra's "C-constant" version or Ulrich's "near plane" version should
// be used, and ultimately Ulrich's "near plane" version was chosen for simplicity, since no "C-constant"
// needs to be worried about.
// Outerra eventually made another improvement to their original "C-constant" formula, but it still
// does not incorporate the camera near plane (for this version,
// Outerra states: "Notice that the 'C' variant doesn’t use a near plane distance, it has it
// set at 0" (quote from https://outerra.blogspot.com/2012/11/maximizing-depth-buffer-range-and.html).
// Ulrich's variant has the benefit of constant relative precision over the whole near-far range.
// It was debated here whether Outerra's "C-constant" or Ulrich's "near plane" variant should
// be used, and ultimately Ulrich's "near plane" version was chosen.
// Outerra eventually made another improvement to their original "C-constant" variant,
// but it still does not incorporate the camera near plane (for this version,
// see https://outerra.blogspot.com/2013/07/logarithmic-depth-buffer-optimizations.html).
near = near.max( 1e-6 ); // <-- clamp so we don't divide by 0
const numerator = log2( perspectiveW.div( near ) );
const denominator = log2( far.div( near ) );
// The only modification we make to Ulrich's formula is
// adding 1 to the final depth value and dividing by 2.
return numerator.div( denominator ).add( 1 ).div( 2 );
// Here we make 4 changes to Ulrich's formula:
// 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
near = near.max( 1e-6 ).toVar();
const numerator = log2( perspectiveW.div( near ).add( 1 ) );
const denominator = log2( far.div( near ).add( 1 ) );
return numerator.div( denominator );

};

Expand Down
2 changes: 1 addition & 1 deletion src/nodes/lighting/AnalyticLightNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ class AnalyticLightNode extends LightingNode {

// 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: Can we fix cameraNear/cameraFar in src/nodes/accessors/Camera.js so we don't have to declare 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 );

Expand Down