Skip to content

Commit 8df424d

Browse files
zjm-metameta-codesync[bot]
authored andcommitted
feat(depth): Add OcclusionShadersMode to DepthOccludable component
Summary: Add per-entity occlusion mode selection (SoftOcclusion / HardOcclusion) to the DepthOccludable component. HardOcclusion skips the 13-tap blur sampling and uses a single depth sample per fragment for sharper edges. updateOcclusionUniforms now iterates entityShaderMap to set the uOcclusionHardMode uniform per entity based on its component mode. Reviewed By: felixtrz Differential Revision: D94115730 fbshipit-source-id: e4d26a19e9cd590c627a719951a2af034db0e26b
1 parent b80ee29 commit 8df424d

File tree

3 files changed

+67
-43
lines changed

3 files changed

+67
-43
lines changed

examples/depth-occlusion/src/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
Color,
1414
createSystem,
1515
DepthOccludable,
16+
OcclusionShadersMode,
1617
DepthSensingSystem,
1718
DistanceGrabbable,
1819
Interactable,
@@ -37,7 +38,7 @@ export class OcclusionDemoSystem extends createSystem({
3738
init() {
3839
// Create several occludable objects at different positions
3940
this.createOccludableSphere(new Vector3(0, 0.8, -0.8), 0xff4444);
40-
this.createOccludableCube(new Vector3(-0.4, 0.8, -0.6), 0x44ff44);
41+
this.createHardModeOccludableCube(new Vector3(-0.4, 0.8, -0.6), 0x44ff44);
4142
this.createNonOccludableCylinder(new Vector3(0.4, 0.8, -0.6), 0x4444ff);
4243
this.createOccludablePlant(new Vector3(-0.6, 0, -1.0));
4344
this.createOccludableRobot(new Vector3(0.6, 0, -1.0));
@@ -72,7 +73,7 @@ export class OcclusionDemoSystem extends createSystem({
7273
/**
7374
* Creates an occludable cube with depth occlusion support.
7475
*/
75-
createOccludableCube(position, color, size = 0.25) {
76+
createHardModeOccludableCube(position, color, size = 0.25) {
7677
const geometry = new BoxGeometry(size, size, size);
7778
const material = new MeshStandardMaterial({
7879
color: new Color(color),
@@ -90,7 +91,7 @@ export class OcclusionDemoSystem extends createSystem({
9091
movementMode: MovementMode.MoveFromTarget,
9192
});
9293
entity.addComponent(XRAnchor);
93-
entity.addComponent(DepthOccludable);
94+
entity.addComponent(DepthOccludable, { mode: OcclusionShadersMode.HardOcclusion });
9495

9596
return entity;
9697
}

packages/core/src/depth/depth-occludable.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import { createComponent } from '../ecs/index.js';
8+
import { Types, createComponent } from '../ecs/index.js';
9+
10+
/** Occlusion shader mode for {@link DepthOccludable}. @category Depth Sensing */
11+
export const OcclusionShadersMode = {
12+
/** Soft occlusion with 13-tap blur sampling for smooth edges. */
13+
SoftOcclusion: 'SoftOcclusion',
14+
/** Hard occlusion with a single depth sample per fragment. */
15+
HardOcclusion: 'HardOcclusion',
16+
};
917

1018
/**
1119
* Component for entities that should be occluded by real-world depth.
@@ -14,15 +22,24 @@ import { createComponent } from '../ecs/index.js';
1422
*
1523
* @example
1624
* ```ts
17-
* // Create an entity with occlusion enabled
25+
* // Create an entity with soft occlusion (default)
1826
* const entity = world.createTransformEntity(mesh);
1927
* entity.addComponent(DepthOccludable);
28+
*
29+
* // Create an entity with hard occlusion (no blur)
30+
* entity.addComponent(DepthOccludable, { mode: OcclusionShadersMode.HardOcclusion });
2031
* ```
2132
*
2233
* @category Depth Sensing
2334
*/
2435
export const DepthOccludable = createComponent(
2536
'DepthOccludable',
26-
{},
37+
{
38+
mode: {
39+
type: Types.Enum,
40+
enum: OcclusionShadersMode,
41+
default: OcclusionShadersMode.SoftOcclusion,
42+
},
43+
},
2744
'Entity that can be occluded by real-world depth',
2845
);

packages/core/src/depth/depth-sensing-system.ts

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import { createSystem, Entity, Types } from '../ecs/index.js';
99
import { Mesh, Vector2 } from '../runtime/three.js';
10-
import { DepthOccludable } from './depth-occludable.js';
10+
import { DepthOccludable, OcclusionShadersMode } from './depth-occludable.js';
1111
import { DepthTextures } from './depth-textures.js';
1212
import type { Shader, ShaderUniforms } from './types.js';
1313

@@ -65,7 +65,6 @@ export class DepthSensingSystem extends createSystem(
6565
private depthTextures?: DepthTextures;
6666

6767
// Occlusion
68-
private occludableShaders = new Set<ShaderUniforms>();
6968
private entityShaderMap = new Map<Entity, Set<ShaderUniforms>>();
7069

7170
/**
@@ -139,21 +138,14 @@ export class DepthSensingSystem extends createSystem(
139138
}
140139
material.userData.shader = shader;
141140
entityUniforms.add(shader.uniforms);
142-
this.occludableShaders.add(shader.uniforms);
143141
};
144142
material.needsUpdate = true;
145143
}
146144
});
147145
}
148146

149147
private detachOcclusionFromEntity(entity: Entity): void {
150-
const entityUniforms = this.entityShaderMap.get(entity);
151-
if (entityUniforms) {
152-
for (const uniforms of entityUniforms) {
153-
this.occludableShaders.delete(uniforms);
154-
}
155-
this.entityShaderMap.delete(entity);
156-
}
148+
this.entityShaderMap.delete(entity);
157149
}
158150

159151
/**
@@ -172,6 +164,7 @@ export class DepthSensingSystem extends createSystem(
172164
shader.uniforms.uDepthNear = { value: 0 };
173165
shader.uniforms.uViewportSize = { value: new Vector2() };
174166
shader.uniforms.uOcclusionBlurRadius = { value: 20.0 };
167+
shader.uniforms.uOcclusionHardMode = { value: false };
175168

176169
shader.defines = {
177170
...(shader.defines ?? {}),
@@ -205,6 +198,7 @@ export class DepthSensingSystem extends createSystem(
205198
'uniform float uDepthNear;',
206199
'uniform vec2 uViewportSize;',
207200
'uniform float uOcclusionBlurRadius;',
201+
'uniform bool uOcclusionHardMode;',
208202
'varying float vOcclusionViewDepth;',
209203
'',
210204
'uniform sampler2DArray uXRDepthTextureArray;',
@@ -235,25 +229,30 @@ export class DepthSensingSystem extends createSystem(
235229
'if (occlusionEnabled) {',
236230
' vec2 screenUV = gl_FragCoord.xy / uViewportSize;',
237231
' vec2 depthUV = uIsGPUDepth ? screenUV : vec2(screenUV.x, 1.0 - screenUV.y);',
238-
' vec2 texelSize = uOcclusionBlurRadius / uViewportSize;',
239-
' // 13-tap two-ring sampling pattern for smooth occlusion edges',
240-
' // Center sample',
241-
' float occlusion_value = OcclusionGetSample(depthUV, vec2(0.0));',
242-
' // Inner ring: 6 samples at 40% radius, 60 degree intervals',
243-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.4, 0.0));',
244-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.2, 0.346));',
245-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.2, 0.346));',
246-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.4, 0.0));',
247-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.2, -0.346));',
248-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.2, -0.346));',
249-
' // Outer ring: 6 samples at full radius, offset 30 degrees',
250-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.866, 0.5));',
251-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.0, 1.0));',
252-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.866, 0.5));',
253-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.866, -0.5));',
254-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.0, -1.0));',
255-
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.866, -0.5));',
256-
' occlusion_value /= 13.0;',
232+
' float occlusion_value;',
233+
' if (uOcclusionHardMode) {',
234+
' occlusion_value = OcclusionGetSample(depthUV, vec2(0.0));',
235+
' } else {',
236+
' vec2 texelSize = uOcclusionBlurRadius / uViewportSize;',
237+
' // 13-tap two-ring sampling pattern for smooth occlusion edges',
238+
' // Center sample',
239+
' occlusion_value = OcclusionGetSample(depthUV, vec2(0.0));',
240+
' // Inner ring: 6 samples at 40% radius, 60 degree intervals',
241+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.4, 0.0));',
242+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.2, 0.346));',
243+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.2, 0.346));',
244+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.4, 0.0));',
245+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.2, -0.346));',
246+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.2, -0.346));',
247+
' // Outer ring: 6 samples at full radius, offset 30 degrees',
248+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.866, 0.5));',
249+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.0, 1.0));',
250+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.866, 0.5));',
251+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2(-0.866, -0.5));',
252+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.0, -1.0));',
253+
' occlusion_value += OcclusionGetSample(depthUV, texelSize * vec2( 0.866, -0.5));',
254+
' occlusion_value /= 13.0;',
255+
' }',
257256
' diffuseColor.a *= occlusion_value;',
258257
'}',
259258
].join('\n'),
@@ -376,14 +375,21 @@ export class DepthSensingSystem extends createSystem(
376375
const viewportSize = new Vector2();
377376
this.renderer.getDrawingBufferSize(viewportSize);
378377

379-
for (const uniforms of this.occludableShaders) {
380-
uniforms.uXRDepthTextureArray.value = depthTextureArray;
381-
uniforms.uRawValueToMeters.value = this.rawValueToMeters;
382-
uniforms.uIsGPUDepth.value = isGPUDepth;
383-
uniforms.uDepthNear.value = depthNear;
384-
(uniforms.uViewportSize.value as Vector2).copy(viewportSize);
385-
uniforms.uOcclusionBlurRadius.value = this.config.blurRadius.value;
386-
uniforms.occlusionEnabled.value = this.config.enableOcclusion.value;
378+
for (const [entity, entityUniforms] of this.entityShaderMap) {
379+
const isHardMode =
380+
DepthOccludable.data.mode[entity.index] ===
381+
OcclusionShadersMode.HardOcclusion;
382+
383+
for (const uniforms of entityUniforms) {
384+
uniforms.uXRDepthTextureArray.value = depthTextureArray;
385+
uniforms.uRawValueToMeters.value = this.rawValueToMeters;
386+
uniforms.uIsGPUDepth.value = isGPUDepth;
387+
uniforms.uDepthNear.value = depthNear;
388+
(uniforms.uViewportSize.value as Vector2).copy(viewportSize);
389+
uniforms.uOcclusionBlurRadius.value = this.config.blurRadius.value;
390+
uniforms.uOcclusionHardMode.value = isHardMode;
391+
uniforms.occlusionEnabled.value = this.config.enableOcclusion.value;
392+
}
387393
}
388394
}
389395
}

0 commit comments

Comments
 (0)