Skip to content

Commit

Permalink
WebGPU: Logarithmic Depth Buffer Rename/Revision + GTAONode Fixes (#2…
Browse files Browse the repository at this point in the history
…9870)

* 1. Renamed the function 'perspectiveDepthToLogarithmicDepth' to 'viewZToLogarithmicDepth' and modified it to expect a negative viewZ value in order maintain consistency with 'viewZToOrthographicDepth' and 'viewZToPerspectiveDepth'
2. Added function 'logarithmicDepthToViewZ'
3. Fixed AO not working when 'logarithmicDepthBuffer' is true

* Update GTAONode.js

Clean up.

* Update ShadowNode.js

Clean up.

* Update ShadowNode.js

Add `setGroup()`.

* Update ShadowNode.js

---------

Co-authored-by: Michael Herzog <[email protected]>
  • Loading branch information
PoseidonEnergy and Mugen87 authored Nov 13, 2024
1 parent bb7f17a commit 485f7f0
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 14 deletions.
22 changes: 20 additions & 2 deletions examples/jsm/tsl/display/GTAONode.js
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );

Expand Down
4 changes: 2 additions & 2 deletions src/materials/nodes/NodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -285,7 +285,7 @@ class NodeMaterial extends Material {

if ( camera.isPerspectiveCamera ) {

depthNode = perspectiveDepthToLogarithmicDepth( modelViewProjection().w, cameraNear, cameraFar );
depthNode = viewZToLogarithmicDepth( positionView.z, cameraNear, cameraFar );

} else {

Expand Down
27 changes: 21 additions & 6 deletions src/nodes/display/ViewportDepthNode.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
Expand All @@ -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 );
Expand Down
8 changes: 4 additions & 4 deletions src/nodes/lighting/ShadowNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 } ) => {

Expand Down Expand Up @@ -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 );

}

Expand Down

0 comments on commit 485f7f0

Please sign in to comment.