diff --git a/examples/files.json b/examples/files.json index 3a51bc11df9da8..7213b4af611c2b 100644 --- a/examples/files.json +++ b/examples/files.json @@ -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", diff --git a/examples/jsm/postprocessing/DepthSavePass.js b/examples/jsm/postprocessing/DepthSavePass.js new file mode 100644 index 00000000000000..0fc8a71d94bf43 --- /dev/null +++ b/examples/jsm/postprocessing/DepthSavePass.js @@ -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 }; diff --git a/examples/jsm/utils/DepthTextureUtils.js b/examples/jsm/utils/DepthTextureUtils.js new file mode 100644 index 00000000000000..2fbcc2663ab4f4 --- /dev/null +++ b/examples/jsm/utils/DepthTextureUtils.js @@ -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 }; diff --git a/examples/screenshots/webgl_postprocessing_interactive_displacement.jpg b/examples/screenshots/webgl_postprocessing_interactive_displacement.jpg new file mode 100644 index 00000000000000..382959656f0b4a Binary files /dev/null and b/examples/screenshots/webgl_postprocessing_interactive_displacement.jpg differ diff --git a/examples/textures/noises/voronoi/normal-256x256.png b/examples/textures/noises/voronoi/normal-256x256.png new file mode 100644 index 00000000000000..5c86e467105280 Binary files /dev/null and b/examples/textures/noises/voronoi/normal-256x256.png differ diff --git a/examples/webgl_postprocessing_interactive_displacement.html b/examples/webgl_postprocessing_interactive_displacement.html new file mode 100644 index 00000000000000..e54f1eb336c42b --- /dev/null +++ b/examples/webgl_postprocessing_interactive_displacement.html @@ -0,0 +1,284 @@ + + + + three.js webgl - postprocessing - interactive displacement map + + + + + + + +
+ three.js - interactive displacement map demo.
+
+ + + + + + + + diff --git a/package-lock.json b/package-lock.json index 7543abf870c97f..4807fcfcdb42df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "three", - "version": "0.177.0", + "version": "0.178.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "three", - "version": "0.177.0", + "version": "0.178.0", "license": "MIT", "devDependencies": { "@rollup/plugin-node-resolve": "^16.0.0",