diff --git a/examples/files.json b/examples/files.json index 1230263cb7dad9..32f9332d959fc0 100644 --- a/examples/files.json +++ b/examples/files.json @@ -464,6 +464,7 @@ "webgpu_water", "webgpu_xr_rollercoaster", "webgpu_xr_cubes", + "webgpu_xr_occluded_cubes", "webgpu_xr_native_layers" ], "webaudio": [ diff --git a/examples/screenshots/webgpu_xr_occluded_cubes.jpg b/examples/screenshots/webgpu_xr_occluded_cubes.jpg new file mode 100644 index 00000000000000..214028953b5d6d Binary files /dev/null and b/examples/screenshots/webgpu_xr_occluded_cubes.jpg differ diff --git a/examples/webgpu_xr_occluded_cubes.html b/examples/webgpu_xr_occluded_cubes.html new file mode 100644 index 00000000000000..a0587bfc285729 --- /dev/null +++ b/examples/webgpu_xr_occluded_cubes.html @@ -0,0 +1,279 @@ + + + + three.js xr - cubes + + + + + + +
+ three.js xr - interactive cubes +
+ + + + + + diff --git a/src/Three.TSL.js b/src/Three.TSL.js index e930ff2e500b4e..d8ed56504b6b13 100644 --- a/src/Three.TSL.js +++ b/src/Three.TSL.js @@ -88,6 +88,7 @@ export const bool = TSL.bool; export const buffer = TSL.buffer; export const bufferAttribute = TSL.bufferAttribute; export const bumpMap = TSL.bumpMap; +export const builtin = TSL.builtin; export const burn = TSL.burn; export const bvec2 = TSL.bvec2; export const bvec3 = TSL.bvec3; @@ -423,6 +424,7 @@ export const samplerComparison = TSL.samplerComparison; export const saturate = TSL.saturate; export const saturation = TSL.saturation; export const screen = TSL.screen; +export const screenRaw = TSL.screenRaw; export const screenCoordinate = TSL.screenCoordinate; export const screenSize = TSL.screenSize; export const screenUV = TSL.screenUV; diff --git a/src/nodes/TSL.js b/src/nodes/TSL.js index 70f798b9b6caf4..c764e68494b4ef 100644 --- a/src/nodes/TSL.js +++ b/src/nodes/TSL.js @@ -50,6 +50,7 @@ export * from './tsl/TSLBase.js'; export * from './accessors/AccessorsUtils.js'; export * from './accessors/Arrays.js'; export * from './accessors/UniformArrayNode.js'; +export * from './accessors/BuiltinNode.js'; export * from './accessors/Bitangent.js'; export * from './accessors/BufferAttributeNode.js'; export * from './accessors/BufferNode.js'; diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js index 4eaeb829037778..f7767350728118 100644 --- a/src/nodes/core/NodeBuilder.js +++ b/src/nodes/core/NodeBuilder.js @@ -979,6 +979,12 @@ class NodeBuilder { } + getFrag() { + + console.warn( 'Abstract function.' ); + + } + /** * Whether to flip texture data along its vertical axis or not. WebGL needs * this method evaluate to `true`, WebGPU to `false`. diff --git a/src/nodes/display/ScreenNode.js b/src/nodes/display/ScreenNode.js index b5b33a1c2da58a..5e980725e249aa 100644 --- a/src/nodes/display/ScreenNode.js +++ b/src/nodes/display/ScreenNode.js @@ -1,7 +1,7 @@ import Node from '../core/Node.js'; import { NodeUpdateType } from '../core/constants.js'; import { uniform } from '../core/UniformNode.js'; -import { Fn, nodeImmutable, vec2 } from '../tsl/TSLBase.js'; +import { Fn, nodeImmutable, vec2, vec4 } from '../tsl/TSLBase.js'; import { Vector2 } from '../../math/Vector2.js'; import { Vector4 } from '../../math/Vector4.js'; @@ -26,7 +26,7 @@ class ScreenNode extends Node { /** * Constructs a new screen node. * - * @param {('coordinate'|'viewport'|'size'|'uv')} scope - The node's scope. + * @param {('coordinate'|'viewport'|'size'|'uv'|'raw')} scope - The node's scope. */ constructor( scope ) { @@ -62,7 +62,7 @@ class ScreenNode extends Node { */ getNodeType() { - if ( this.scope === ScreenNode.VIEWPORT ) return 'vec4'; + if ( this.scope === ScreenNode.VIEWPORT || this.scope === ScreenNode.RAW ) return 'vec4'; else return 'vec2'; } @@ -143,6 +143,10 @@ class ScreenNode extends Node { output = uniform( viewportVec || ( viewportVec = new Vector4() ) ); + } else if ( scope === ScreenNode.RAW ) { + + output = vec4( screenRaw.div( screenSize ) ); + } else { output = vec2( screenCoordinate.div( screenSize ) ); @@ -155,7 +159,11 @@ class ScreenNode extends Node { generate( builder ) { - if ( this.scope === ScreenNode.COORDINATE ) { + if ( this.scope === ScreenNode.RAW ) { + + return builder.getFrag(); + + } else if ( this.scope === ScreenNode.COORDINATE ) { let coord = builder.getFragCoord(); @@ -183,6 +191,7 @@ ScreenNode.COORDINATE = 'coordinate'; ScreenNode.VIEWPORT = 'viewport'; ScreenNode.SIZE = 'size'; ScreenNode.UV = 'uv'; +ScreenNode.RAW = 'raw'; export default ScreenNode; @@ -230,6 +239,8 @@ export const viewport = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.VIEW */ export const viewportSize = viewport.zw; +export const screenRaw = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.RAW );//.div( vec4( viewport.z, viewport.w, 1, 1 ) ); + /** * TSL object that represents the current `x`/`y` pixel position on the viewport in physical pixel units. * diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index e2ee9095e9ba63..ef28bd8a6c4282 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -2494,8 +2494,9 @@ class Renderer { * This method can only be used if the renderer has been initialized. * * @param {Texture} texture - The texture. + * @param {Object} [options={}] - Optional configuration parameter. */ - initTexture( texture ) { + initTexture( texture, options = {} ) { if ( this._initialized === false ) { @@ -2503,7 +2504,7 @@ class Renderer { } - this._textures.updateTexture( texture ); + this._textures.updateTexture( texture, options ); } diff --git a/src/renderers/common/Textures.js b/src/renderers/common/Textures.js index c966ac75678ebe..d4eac48b092ddb 100644 --- a/src/renderers/common/Textures.js +++ b/src/renderers/common/Textures.js @@ -242,7 +242,7 @@ class Textures extends DataMap { // - if ( isRenderTarget || texture.isStorageTexture === true ) { + if ( isRenderTarget || texture.isStorageTexture === true || texture.isExternalTexture === true ) { backend.createSampler( texture ); backend.createTexture( texture, options ); @@ -306,7 +306,11 @@ class Textures extends DataMap { // async update - backend.createDefaultTexture( texture ); + if ( options.source === undefined ) { + + backend.createDefaultTexture( texture ); + + } textureData.isDefaultTexture = true; textureData.generation = texture.version; @@ -332,7 +336,11 @@ class Textures extends DataMap { texture.removeEventListener( 'dispose', onDispose ); - this._destroyTexture( texture ); + if ( options.source === undefined ) { + + this._destroyTexture( texture ); + + } }; diff --git a/src/renderers/common/XRManager.js b/src/renderers/common/XRManager.js index 217851c5936b7b..04fb341a0f865a 100644 --- a/src/renderers/common/XRManager.js +++ b/src/renderers/common/XRManager.js @@ -16,6 +16,11 @@ import NodeMaterial from '../../materials/nodes/NodeMaterial.js'; import { PlaneGeometry } from '../../geometries/PlaneGeometry.js'; import { MeshBasicMaterial } from '../../materials/MeshBasicMaterial.js'; import { Mesh } from '../../objects/Mesh.js'; +import { Fn, vec2, vec3 } from '../../nodes/tsl/TSLBase.js'; +import { texture3D } from '../../nodes/accessors/Texture3DNode.js'; +import { screenRaw, viewport } from '../../nodes/display/ScreenNode.js'; +import { builtin } from '../../nodes/accessors/BuiltinNode.js'; +import { Texture } from '../../textures/Texture.js'; const _cameraLPos = /*@__PURE__*/ new Vector3(); const _cameraRPos = /*@__PURE__*/ new Vector3(); @@ -859,6 +864,67 @@ class XRManager extends EventDispatcher { } + applyOcclusion( scene ) { + + if ( this.depthData ) { + + const data = this.depthData[ 0 ]; + + if ( this._occlusionTexture == undefined ) { + + this._occlusionTexture = new Texture( { width: data.width, height: data.height, depth: 2 } ); + this._occlusionTexture.isTextureArray = true; + this._occlusionTexture.isExternalTexture = true; + + this._masknode = Fn( () => { + + let pixel = ( screenRaw.xyzw ).toVar( 'pixel' ); + pixel = pixel.xy.div( vec2( viewport.zw ) ); + + const real_world_depth = texture3D( this._occlusionTexture, vec3( pixel, builtin( 'gl_ViewID_OVR' ) ) ).r; + const virtual_world_depth = screenRaw.z; + + return virtual_world_depth.lessThan( real_world_depth ); + + } )(); + + this._renderer.initTexture( this._occlusionTexture, { width: data.width, height: data.height, depth: 2, source: data.texture } ); + + scene.traverse( ( object ) => { + + if ( object.material ) { + + object.material.maskNode = this._masknode; + + } + + } ); + + } + + } else { + + if ( this._masknode ) { + + scene.traverse( ( object ) => { + + if ( object.material ) { + + delete object.material.maskNode; + + } + + } ); + + } + + delete this._masknode; + delete this._occlusionTexture; + + } + + } + /** * Returns the current XR session. @@ -1069,8 +1135,15 @@ class XRManager extends EventDispatcher { if ( session === null ) return; - const depthNear = camera.near; - const depthFar = camera.far; + let depthNear = camera.near; + let depthFar = camera.far; + + if ( this.depthData ) { + + depthNear = this.depthData[ 0 ].depthNear; + depthFar = this.depthData[ 0 ].depthFar; + + } const cameraXR = this._cameraXR; const cameraL = this._cameraL; @@ -1565,6 +1638,36 @@ function onAnimationFrame( time, frame ) { } + const enabledFeatures = this._session.enabledFeatures; + const gpuDepthSensingEnabled = enabledFeatures && + enabledFeatures.includes( 'depth-sensing' ) && + this._session.depthUsage == 'gpu-optimized'; + let depth_data = undefined; + + if ( gpuDepthSensingEnabled ) { + + depth_data = this._glBinding.getDepthInformation( view ); + + } + + if ( depth_data ) { + + if ( this.depthData === undefined ) { + + this.depthData = [ depth_data ]; + + } else { + + this.depthData.push( depth_data ); + + } + + } else { + + delete this.depthData; + + } + } else { viewport = glBaseLayer.getViewport( view ); @@ -1624,6 +1727,8 @@ function onAnimationFrame( time, frame ) { if ( this._currentAnimationLoop ) this._currentAnimationLoop( time, frame ); + delete this.depthData; + if ( frame.detectedPlanes ) { this.dispatchEvent( { type: 'planesdetected', data: frame } ); diff --git a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js index da2f96f8911a88..22eb54af991735 100644 --- a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +++ b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js @@ -935,6 +935,12 @@ ${ flowData.code } } + getFrag() { + + return 'gl_FragCoord'; + + } + /** * Returns the frag depth builtin. * diff --git a/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js b/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js index 4e0c0fca20f1d2..3da5b3b7632b81 100644 --- a/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +++ b/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js @@ -404,30 +404,34 @@ class WebGLTextureUtils { createTexture( texture, options ) { const { gl, backend } = this; - const { levels, width, height, depth } = options; + const { levels, width, height, depth, source } = options; const glFormat = backend.utils.convert( texture.format, texture.colorSpace ); const glType = backend.utils.convert( texture.type ); const glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture ); - const textureGPU = gl.createTexture(); + const textureGPU = source ? source : gl.createTexture(); const glTextureType = this.getGLTextureType( texture ); backend.state.bindTexture( glTextureType, textureGPU ); - this.setTextureParameters( glTextureType, texture ); + if ( ! source ) { - if ( texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + this.setTextureParameters( glTextureType, texture ); - gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth ); + if ( texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { - } else if ( texture.isData3DTexture ) { + gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth ); + + } else if ( texture.isData3DTexture ) { - gl.texStorage3D( gl.TEXTURE_3D, levels, glInternalFormat, width, height, depth ); + gl.texStorage3D( gl.TEXTURE_3D, levels, glInternalFormat, width, height, depth ); - } else if ( ! texture.isVideoTexture ) { + } else if ( ! texture.isVideoTexture ) { - gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height ); + gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height ); + + } } diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index 55488ec8aa7aa2..e22c736e75f125 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -1174,6 +1174,12 @@ ${ flowData.code } } + getFrag() { + + return this.getBuiltin( 'position', 'fragCoord', 'vec4' ); + + } + /** * Returns the frag depth builtin. *