@@ -118,26 +118,33 @@ export const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).d
118
118
119
119
export const perspectiveDepthToLogarithmicDepth = ( perspectiveW , near , far ) => {
120
120
121
- // The final logarithmic depth formula used here is adapted from one described in an article
122
- // by Thatcher Ulrich (see http://tulrich.com/geekstuff/log_depth_buffer.txt), which was an
123
- // improvement upon an earlier formula one described in an
121
+ // The final logarithmic depth formula used here is adapted from one described in an
122
+ // article by Thatcher Ulrich (see http://tulrich.com/geekstuff/log_depth_buffer.txt),
123
+ // which was an improvement upon an earlier formula one described in an
124
124
// Outerra article (https://outerra.blogspot.com/2009/08/logarithmic-z-buffer.html).
125
- // The Outerra article ignored the camera near plane (it always assumed it was 0) and instead
125
+ // Ulrich's formula is the following:
126
+ // z = K * log( w / cameraNear ) / log( cameraFar / cameraNear )
127
+ // where K = 2^k - 1, and k is the number of bits in the depth buffer.
128
+ // The Outerra variant ignored the camera near plane (it assumed it was 0) and instead
126
129
// opted for a "C-constant" for resolution adjustment of objects near the camera.
127
- // Outerra states this about their own formula : "Notice that the 'C' variant doesn’t use a near
128
- // plane distance, it has it set at 0. " (quote from https://outerra.blogspot.com/2012/11/maximizing-depth-buffer-range-and.html)
129
- // It was debated here whether Outerra's "C- constant" version or Ulrich's "near plane" version should
130
- // be used, and ultimately Ulrich's "near plane" version was chosen for simplicity, since no "C-constant"
131
- // needs to be worried about .
132
- // Outerra eventually made another improvement to their original "C-constant" formula, but it still
133
- // does not incorporate the camera near plane (for this version,
130
+ // Outerra states: "Notice that the 'C' variant doesn’t use a near plane distance, it has it
131
+ // set at 0" (quote from https://outerra.blogspot.com/2012/11/maximizing-depth-buffer-range-and.html).
132
+ // Ulrich's variant has the benefit of constant relative precision over the whole near-far range.
133
+ // It was debated here whether Outerra's "C-constant" or Ulrich's "near plane" variant should
134
+ // be used, and ultimately Ulrich's "near plane" version was chosen .
135
+ // Outerra eventually made another improvement to their original "C-constant" variant,
136
+ // but it still does not incorporate the camera near plane (for this version,
134
137
// see https://outerra.blogspot.com/2013/07/logarithmic-depth-buffer-optimizations.html).
135
- near = near . max ( 1e-6 ) ; // <-- clamp so we don't divide by 0
136
- const numerator = log2 ( perspectiveW . div ( near ) ) ;
137
- const denominator = log2 ( far . div ( near ) ) ;
138
- // The only modification we make to Ulrich's formula is
139
- // adding 1 to the final depth value and dividing by 2.
140
- return numerator . div ( denominator ) . add ( 1 ) . div ( 2 ) ;
138
+ // Here we make 4 changes to Ulrich's formula:
139
+ // 1. Clamp the camera near plane so we don't divide by 0.
140
+ // 2. Use log2 instead of log to avoid an extra multiply (shaders implement log using log2).
141
+ // 3. Assume K is 1 (K = maximum value in depth buffer; see Ulrich's formula above).
142
+ // 4. Add 1 to each division by cameraNear to ensure the depth curve is shifted to the left as cameraNear increases.
143
+ // For visual representation of this depth curve, see https://www.desmos.com/calculator/lz5rqfysih
144
+ near = near . max ( 1e-6 ) . toVar ( ) ;
145
+ const numerator = log2 ( perspectiveW . div ( near ) . add ( 1 ) ) ;
146
+ const denominator = log2 ( far . div ( near ) . add ( 1 ) ) ;
147
+ return numerator . div ( denominator ) ;
141
148
142
149
} ;
143
150
0 commit comments