66 */
77
88import { createSystem , Entity , Types } from '../ecs/index.js' ;
9- import { ExternalTexture , Mesh , Texture , Vector2 } from '../runtime/three.js' ;
9+ import { Mesh , Vector2 } from '../runtime/three.js' ;
1010import { DepthOccludable } from './depth-occludable.js' ;
1111import { DepthTextures } from './depth-textures.js' ;
1212import type { Shader , ShaderUniforms } from './types.js' ;
@@ -58,7 +58,6 @@ export class DepthSensingSystem extends createSystem(
5858 } ,
5959) {
6060 private depthFeatureEnabled : boolean | undefined ;
61- private isGPUOptimized = false ;
6261
6362 // Depth data storage
6463 cpuDepthData : XRCPUDepthInformation [ ] = [ ] ;
@@ -136,10 +135,7 @@ export class DepthSensingSystem extends createSystem(
136135 }
137136 // Only inject occlusion if not already present
138137 if ( ! shader . uniforms . occlusionEnabled ) {
139- DepthSensingSystem . addOcclusionToShader (
140- shader ,
141- this . isGPUOptimized ,
142- ) ;
138+ DepthSensingSystem . addOcclusionToShader ( shader ) ;
143139 }
144140 material . userData . shader = shader ;
145141 entityUniforms . add ( shader . uniforms ) ;
@@ -163,19 +159,16 @@ export class DepthSensingSystem extends createSystem(
163159 /**
164160 * Modifies a material's shader in-place to incorporate inline depth-based
165161 * occlusion. Compares the virtual fragment's view-space depth against the
166- * real-world depth from the XR depth texture.
162+ * real-world depth from the XR depth texture array.
163+ * Both GPU-optimized (ExternalTexture) and CPU-optimized (DataArrayTexture)
164+ * paths use sampler2DArray with VIEW_ID for correct stereo depth sampling.
167165 * @param shader - The shader object provided by onBeforeCompile.
168- * @param isGPUOptimized - Whether the depth data uses GPU-optimized texture arrays.
169166 */
170- private static addOcclusionToShader (
171- shader : Shader ,
172- isGPUOptimized : boolean ,
173- ) : void {
167+ private static addOcclusionToShader ( shader : Shader ) : void {
174168 shader . uniforms . occlusionEnabled = { value : false } ;
175- shader . uniforms . uXRDepthTexture = { value : null } ;
176169 shader . uniforms . uXRDepthTextureArray = { value : null } ;
177170 shader . uniforms . uRawValueToMeters = { value : 0.001 } ;
178- shader . uniforms . uIsTextureArray = { value : false } ;
171+ shader . uniforms . uIsGPUDepth = { value : false } ;
179172 shader . uniforms . uDepthNear = { value : 0 } ;
180173 shader . uniforms . uViewportSize = { value : new Vector2 ( ) } ;
181174 shader . uniforms . uOcclusionBlurRadius = { value : 20.0 } ;
@@ -184,9 +177,6 @@ export class DepthSensingSystem extends createSystem(
184177 ...( shader . defines ?? { } ) ,
185178 USE_UV : true ,
186179 } ;
187- if ( isGPUOptimized ) {
188- shader . defines . USE_DEPTH_TEXTURE_ARRAY = '' ;
189- }
190180
191181 // Vertex shader: compute view-space depth for occlusion comparison
192182 shader . vertexShader = shader . vertexShader
@@ -203,34 +193,33 @@ export class DepthSensingSystem extends createSystem(
203193 ] . join ( '\n' ) ,
204194 ) ;
205195
206- // Fragment shader: sample XR depth and compare against virtual depth
196+ // Fragment shader: sample XR depth array and compare against virtual depth
207197 shader . fragmentShader = shader . fragmentShader
208198 . replace (
209199 'uniform vec3 diffuse;' ,
210200 [
211201 'uniform vec3 diffuse;' ,
212202 'uniform bool occlusionEnabled;' ,
213- 'uniform sampler2D uXRDepthTexture;' ,
214203 'uniform float uRawValueToMeters;' ,
215- 'uniform bool uIsTextureArray ;' ,
204+ 'uniform bool uIsGPUDepth ;' ,
216205 'uniform float uDepthNear;' ,
217206 'uniform vec2 uViewportSize;' ,
218207 'uniform float uOcclusionBlurRadius;' ,
219208 'varying float vOcclusionViewDepth;' ,
220209 '' ,
221- '#ifdef USE_DEPTH_TEXTURE_ARRAY' ,
222210 'uniform sampler2DArray uXRDepthTextureArray;' ,
211+ '' ,
212+ '// Fallback for non-multiview sessions' ,
213+ '#ifndef VIEW_ID' ,
214+ '#define VIEW_ID 0' ,
223215 '#endif' ,
224216 '' ,
225217 'float OcclusionDepthGetMeters(in vec2 uv) {' ,
226- ' #ifdef USE_DEPTH_TEXTURE_ARRAY' ,
227- ' if (uIsTextureArray) {' ,
228- ' float textureValue = texture(uXRDepthTextureArray, vec3(uv.x, uv.y, float(VIEW_ID))).r;' ,
218+ ' float textureValue = texture(uXRDepthTextureArray, vec3(uv.x, uv.y, float(VIEW_ID))).r;' ,
219+ ' if (uIsGPUDepth) {' ,
229220 ' return uRawValueToMeters * uDepthNear / (1.0 - textureValue);' ,
230221 ' }' ,
231- ' #endif' ,
232- ' vec2 packedDepth = texture2D(uXRDepthTexture, uv).rg;' ,
233- ' return packedDepth.r * uRawValueToMeters;' ,
222+ ' return textureValue * uRawValueToMeters;' ,
234223 '}' ,
235224 '' ,
236225 'float OcclusionGetSample(in vec2 depthUV, in vec2 offset) {' ,
@@ -245,7 +234,7 @@ export class DepthSensingSystem extends createSystem(
245234 'vec4 diffuseColor = vec4( diffuse, opacity );' ,
246235 'if (occlusionEnabled) {' ,
247236 ' vec2 screenUV = gl_FragCoord.xy / uViewportSize;' ,
248- ' vec2 depthUV = uIsTextureArray ? screenUV : vec2(screenUV.x, 1.0 - screenUV.y);' ,
237+ ' vec2 depthUV = uIsGPUDepth ? screenUV : vec2(screenUV.x, 1.0 - screenUV.y);' ,
249238 ' vec2 texelSize = uOcclusionBlurRadius / uViewportSize;' ,
250239 ' // 13-tap two-ring sampling pattern for smooth occlusion edges' ,
251240 ' // Center sample' ,
@@ -273,7 +262,6 @@ export class DepthSensingSystem extends createSystem(
273262
274263 private cleanup ( ) : void {
275264 this . depthFeatureEnabled = undefined ;
276- this . isGPUOptimized = false ;
277265 this . cpuDepthData = [ ] ;
278266 this . gpuDepthData = [ ] ;
279267 }
@@ -285,7 +273,6 @@ export class DepthSensingSystem extends createSystem(
285273
286274 const enabledFeatures = xrSession . enabledFeatures ;
287275 this . depthFeatureEnabled = enabledFeatures ?. includes ( 'depth-sensing' ) ;
288- this . isGPUOptimized = xrSession . depthUsage === 'gpu-optimized' ;
289276
290277 if ( ! this . depthFeatureEnabled ) {
291278 console . log (
@@ -320,16 +307,19 @@ export class DepthSensingSystem extends createSystem(
320307 if ( xrRefSpace ) {
321308 const pose = frame . getViewerPose ( xrRefSpace ) ;
322309 if ( pose ) {
323- for ( let viewId = 0 ; viewId < pose . views . length ; ++ viewId ) {
324- const view = pose . views [ viewId ] ;
325-
326- if ( session . depthUsage === 'gpu-optimized' ) {
327- const depthData = binding . getDepthInformation ( view ) ;
328- if ( ! depthData ) {
329- return ;
330- }
331- this . updateGPUDepthData ( depthData , viewId ) ;
332- } else {
310+ if ( session . depthUsage === 'gpu-optimized' ) {
311+ // GPU path: the native texture is a texture array containing all
312+ // views. We only need to update the ExternalTexture once using the
313+ // first view's depth data.
314+ const view = pose . views [ 0 ] ;
315+ const depthData = binding . getDepthInformation ( view ) ;
316+ if ( depthData ) {
317+ this . updateGPUDepthData ( depthData ) ;
318+ }
319+ } else {
320+ // CPU path: each view has its own DataTexture.
321+ for ( let viewId = 0 ; viewId < pose . views . length ; ++ viewId ) {
322+ const view = pose . views [ viewId ] ;
333323 const depthData = frame . getDepthInformation ( view ) ;
334324 if ( ! depthData ) {
335325 return ;
@@ -358,55 +348,42 @@ export class DepthSensingSystem extends createSystem(
358348 /**
359349 * Update with GPU-optimized depth data.
360350 */
361- private updateGPUDepthData (
362- depthData : XRWebGLDepthInformation ,
363- viewId = 0 ,
364- ) : void {
365- this . gpuDepthData [ viewId ] = depthData ;
351+ private updateGPUDepthData ( depthData : XRWebGLDepthInformation ) : void {
352+ this . gpuDepthData [ 0 ] = depthData ;
366353
367354 if ( this . config . enableDepthTexture . value && this . depthTextures ) {
368- this . depthTextures . updateNativeTexture ( depthData , this . renderer , viewId ) ;
355+ this . depthTextures . updateNativeTexture ( depthData , this . renderer ) ;
369356 }
370357 }
371358
372- /**
373- * Get the depth texture for a specific view.
374- * @param viewId - The view index (0 for left eye, 1 for right eye).
375- */
376- getTexture ( viewId : number ) : Texture | undefined {
377- if ( ! this . config . enableDepthTexture . value ) return undefined ;
378- return this . depthTextures ?. get ( viewId ) ;
379- }
380-
381359 /**
382360 * Updates depth texture uniforms on all occludable materials each frame.
361+ * Both GPU and CPU paths set the texture array uniform; the shader uses
362+ * VIEW_ID to select the correct stereo layer.
383363 */
384364 private updateOcclusionUniforms ( ) : void {
385- const leftDepth = this . getTexture ( 0 ) ;
386- const rightDepth = this . getTexture ( 1 ) ;
387- const isTextureArray =
388- leftDepth instanceof ExternalTexture ||
389- rightDepth instanceof ExternalTexture ;
365+ const nativeTexture = this . depthTextures ?. getNativeTexture ( ) ;
366+ const dataArrayTexture = this . depthTextures ?. getDataArrayTexture ( ) ;
367+ const isGPUDepth = nativeTexture !== undefined ;
390368 const depthNear =
391369 ( this . gpuDepthData [ 0 ] as unknown as { depthNear : number } | undefined )
392370 ?. depthNear ?? 0 ;
393371
372+ // Select the texture array: ExternalTexture for GPU, DataArrayTexture for CPU
373+ const depthTextureArray = isGPUDepth ? nativeTexture : dataArrayTexture ;
374+ if ( ! depthTextureArray ) return ;
375+
394376 const viewportSize = new Vector2 ( ) ;
395377 this . renderer . getDrawingBufferSize ( viewportSize ) ;
396378
397379 for ( const uniforms of this . occludableShaders ) {
398- if ( leftDepth ) {
399- uniforms . uXRDepthTexture . value = leftDepth ;
400- }
401- if ( rightDepth ) {
402- uniforms . uXRDepthTextureArray . value = rightDepth ;
403- }
380+ uniforms . uXRDepthTextureArray . value = depthTextureArray ;
404381 uniforms . uRawValueToMeters . value = this . rawValueToMeters ;
405- uniforms . uIsTextureArray . value = isTextureArray ;
382+ uniforms . uIsGPUDepth . value = isGPUDepth ;
406383 uniforms . uDepthNear . value = depthNear ;
407384 ( uniforms . uViewportSize . value as Vector2 ) . copy ( viewportSize ) ;
408385 uniforms . uOcclusionBlurRadius . value = this . config . blurRadius . value ;
409- uniforms . occlusionEnabled . value = true ;
386+ uniforms . occlusionEnabled . value = this . config . enableOcclusion . value ;
410387 }
411388 }
412389}
0 commit comments