Skip to content

Examples: Add DepthSavePass and depth texture tools #31360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@
"webgl_postprocessing_afterimage",
"webgl_postprocessing_backgrounds",
"webgl_postprocessing_transition",
"webgl_postprocessing_interactive_displacement",
"webgl_postprocessing_dof",
"webgl_postprocessing_dof2",
"webgl_postprocessing_fxaa",
Expand Down
60 changes: 60 additions & 0 deletions examples/jsm/postprocessing/DepthSavePass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
WebGLRenderTarget,
FloatType
} from 'three';

import { SavePass } from 'three/addons/postprocessing/SavePass.js';


/**
* A pass that saves the depth contents of the current read buffer in a render target.
*
* ```js
* const depthSavePass = new DepthSavePass( customRenderTarget );
* composer.addPass( depthSavePass );
* ```
*
* @augments SavePass
* @three_import import { SavePass } from 'three/addons/postprocessing/SavePass.js';
*/

class DepthSavePass extends SavePass {

constructor( renderTarget ) {

if ( renderTarget === undefined ) {

renderTarget = new WebGLRenderTarget( 1, 1, { type: FloatType } ); // will be resized later
renderTarget.texture.name = 'DepthSavePass.rt';

}

super( renderTarget );

}

/**
* Performs the depth save pass.
*
* @param {WebGLRenderer} renderer - The renderer.
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
* destination for the pass.
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
* previous pass from this buffer.
* @param {number} deltaTime - The delta time in seconds.
* @param {boolean} maskActive - Whether masking is active or not.
*/

render( renderer, writeBuffer, readBuffer, /* deltaTime , maskActive */ ) {

this.uniforms[ 'tDiffuse' ].value = readBuffer.depthTexture;

renderer.setRenderTarget( this.renderTarget );
if ( this.clear ) renderer.clear();
this._fsQuad.render( renderer );

}

}

export { DepthSavePass };
101 changes: 101 additions & 0 deletions examples/jsm/utils/DepthTextureUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Vector2, Vector3 } from 'three';

const NDC = new Vector3();
const cursorNDC = new Vector2();
const z_Axis = new Vector3( 0, 0, - 1 );
/**
* Converts depth to view position.
* @param {Vector2} cursorNDC - Normalized device coordinates of the cursor.
* @param {number} depth - Depth value.
* @param {Camera} camera - Camera object.
* @returns {Vector3} - View position.
*/
function depthToViewPosition( cursorNDC, depth, camera ) {

// depth to Z NDC
const zNDC = 2.0 * depth - 1.0;

NDC.set( cursorNDC.x, cursorNDC.y, zNDC );

const viewPosition = NDC.applyMatrix4( camera.projectionMatrixInverse );

return viewPosition;

}

/**
* Converts logarithmic depth to view position.
* @param {Vector2} cursorNDC - Normalized device coordinates of the cursor.
* @param {number} logDepth - Logarithmic depth value.
* @param {Camera} camera - Camera object.
* @returns {Vector3} - View position.
*/
function logDepthToViewPosition( cursorNDC, logDepth, camera ) {

const w = ( camera.far + 1.0 ) ** logDepth - 1;

NDC.set( cursorNDC.x, cursorNDC.y, - 1 );
const viewPosition = NDC.applyMatrix4( camera.projectionMatrixInverse );

const angle = viewPosition.angleTo( z_Axis );
viewPosition.setLength( w / Math.cos( angle ) );

return viewPosition;

}

const buffer = new Float32Array( 4 );

/**
* Computes the 3D world position corresponding to a given 2D mouse position on the screen,
* using depth information from a render target.
*
* @param {Object} mouse - The mouse position in screen coordinates.
* @param {number} mouse.x - The x-coordinate of the mouse on the screen.
* @param {number} mouse.y - The y-coordinate of the mouse on the screen.
* @param {THREE.WebGLRenderer} renderer - The WebGL renderer used to access the render target and canvas size.
* @param {THREE.WebGLRenderTarget} renderTarget - The render target that contains depth data.
* @param {THREE.Camera} camera - The camera used for the scene. Supports both perspective and orthographic cameras.
* @param {THREE.Vector3} target - A pre-allocated Vector3 to store the resulting world position.
* @returns {THREE.Vector3} The computed world position corresponding to the mouse input.
*
* This function:
* 1. Converts the mouse position from screen space to canvas space.
* 2. Reads the depth value from the render target at that position.
* 3. Converts the position from normalized device coordinates (NDC) and depth into view space.
* 4. Transforms the result into world space using the camera's world matrix.
*
* It supports both regular and logarithmic depth buffers.
*/

const pickWorldPosition = ( mouse, renderer, renderTarget, camera, target ) => {

const canvasRect = renderer.domElement.getBoundingClientRect();

const left = mouse.x - canvasRect.left;
const bottom = canvasRect.bottom - mouse.y;

renderer.readRenderTargetPixels( renderTarget, left, bottom, 1, 1, buffer );

const depth = buffer[ 0 ];

cursorNDC.setX( ( left / canvasRect.width ) * 2 - 1 );
cursorNDC.setY( ( bottom / canvasRect.height ) * 2 - 1 );

if ( renderer.capabilities.logarithmicDepthBuffer && camera.isPerspectiveCamera ) {

target.copy( logDepthToViewPosition( cursorNDC, depth, camera ) );

} else {

target.copy( depthToViewPosition( cursorNDC, depth, camera ) );

}

target.applyMatrix4( camera.matrixWorld );

return target;

};

export { pickWorldPosition };
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading