Develop

Chapter 15: Depth Occlusion

Updated: Sep 4, 2026
Depth occlusion hides virtual fragments that are behind real-world surfaces. IWSDK provides DepthSensingSystem to read WebXR depth information and DepthOccludable to apply that information to an entity’s materials.
Depth sensing requires an immersive AR device and browser that support the WebXR depth-sensing feature.

Request depth sensing

Add the depth-sensing request to iwsdk.config.json. This configuration matches the IWSDK depth-occlusion example:
{
  "version": "iwsdk.project.v1",
  "scene": "./public/scenes/depth-occlusion.iwsdk.scene.json",
  "assets": { "module": "./src/assets" },
  "world": {
    "xr": {
      "mode": "ar",
      "referenceSpace": "unbounded",
      "features": {
        "depthSensing": {
          "required": true,
          "usage": "gpu-optimized",
          "format": "float32"
        },
        "hitTest": { "required": true },
        "anchors": { "required": true },
        "unbounded": { "required": true }
      }
    }
  }
}
The usage value is gpu-optimized or cpu-optimized. The format value is float32 or luminance-alpha. Setting required to true prevents the immersive session from starting when the device cannot provide depth data.

Register the depth system

Register DepthSensingSystem after the world is created:
import { DepthSensingSystem, World } from '@iwsdk/core';
import projectOptions from 'virtual:iwsdk-project';

const world = await World.create(
  document.getElementById('scene-container') as HTMLDivElement,
  projectOptions,
);

world.registerSystem(DepthSensingSystem, {
  configData: {
    enableDepthTexture: true,
    enableOcclusion: true,
    useFloat32: true,
    blurRadius: 20,
  },
});
The configuration defaults are:
  • enableOcclusion: true
  • enableDepthTexture: true
  • useFloat32: true
  • blurRadius: 20

Apply occlusion to scene entities

Add DepthOccludable to a native scene node that references a registered asset:
{
  "id": "occluded-object",
  "content": {
    "type": "asset",
    "asset": "robot"
  },
  "components": {
    "DepthOccludable": {},
    "RayInteractable": {},
    "DistanceGrabbable": {
      "movementMode": "MoveFromTarget"
    }
  }
}
You can also add the component in code after creating a transform entity:
import { DepthOccludable } from '@iwsdk/core';

const entity = world.createTransformEntity(mesh);
entity.addComponent(DepthOccludable);
Add DepthOccludable after the entity’s final mesh hierarchy is available.

Choose an occlusion mode

OcclusionShadersMode has three values:
  • OcclusionShadersMode.SoftOcclusion: Uses soft occlusion and is the component default.
  • OcclusionShadersMode.HardOcclusion: Uses a single depth sample for a hard edge.
  • OcclusionShadersMode.MinMaxSoftOcclusion: Uses preprocessed depth for edge-aware soft occlusion.
Set another mode when you add the component:
import { DepthOccludable, OcclusionShadersMode } from '@iwsdk/core';

entity.addComponent(DepthOccludable, {
  mode: OcclusionShadersMode.HardOcclusion,
});
The depth system changes materials during shader compilation. A custom shader that does not use the expected Three.js shader structure might not support depth occlusion.

Change system configuration at runtime

System configuration values are Signals. Assign to each Signal’s value property:
const depthSystem = world.getSystem(DepthSensingSystem);

if (depthSystem != null) {
  depthSystem.config.enableOcclusion.value = false;
  depthSystem.config.blurRadius.value = 5;
}
Do not replace depthSystem.config.enableOcclusion or depthSystem.config.blurRadius with primitive values.

Read CPU depth data

When the session uses cpu-optimized depth, cpuDepthData contains the available XRCPUDepthInformation values:
const depthSystem = world.getSystem(DepthSensingSystem);

if (depthSystem != null && depthSystem.cpuDepthData.length > 0) {
  const depthInfo = depthSystem.cpuDepthData[0];
  const distanceInMeters = depthInfo.getDepthInMeters(0.5, 0.5);
}
Use gpuDepthData when the session supplies GPU-optimized depth information.

Troubleshooting

Occlusion does not start

Symptom: Virtual objects remain visible through real-world surfaces.
Solution: Confirm that the immersive AR session granted the depth-sensing feature, DepthSensingSystem is registered, enableOcclusion.value is true, and the entity has DepthOccludable.

The component does not affect a loaded model

Symptom: A model remains visible after adding the component.
Solution: Add DepthOccludable after the model’s final mesh hierarchy is available so the system can process its materials.

Occlusion edges are unsuitable

Symptom: Hard edges alias, or soft edges bleed across a depth boundary.
Solution: Compare HardOcclusion, SoftOcclusion, and MinMaxSoftOcclusion on the target device. Adjust blurRadius.value only for the soft mode.

A custom material does not occlude

Symptom: A custom shader renders without real-world occlusion.
Solution: Use a compatible Three.js material or adapt the shader to the depth system’s compilation requirements.