Signatur
Beschreibung
XRSession ist das zentrale Objekt der WebXR Device API. Eine Instanz erhältst du nicht über einen Konstruktor, sondern über navigator.xr.requestSession(mode, options). Der Modus ist "inline" (Darstellung im Seitenelement), "immersive-vr" (vollständige VR) oder "immersive-ar" (Überlagerung der realen Umgebung). Die Sitzung läuft, bis du sie mit end() beendest oder der Benutzer bzw. das System sie beendet.
Wichtige Bestandteile:
- Eigenschaften:
renderState(aktuelle Render-Konfiguration, z. B.baseLayer,depthNear,depthFar),inputSources(verbundene Controller und Hände alsXRInputSourceArray),visibilityState("visible","visible-blurred","hidden"),environmentBlendMode,interactionMode,frameRateundsupportedFrameRates. - Methoden:
requestAnimationFrame(callback)(Render-Schleife mitXRFrame),cancelAnimationFrame(handle),requestReferenceSpace(type),updateRenderState(state),updateTargetFrameRate(rate),requestHitTestSource(options)(AR) undend(). - Events:
end,inputsourceschange,select/selectstart/selectend,squeeze/squeezestart/squeezeend,visibilitychangeundframeratechange.
Der typische Ablauf: Unterstützung mit navigator.xr.isSessionSupported() prüfen, Sitzung per Benutzergeste anfordern, einen WebGL-Kontext mit xrCompatible: true anlegen, per updateRenderState() einen XRWebGLLayer setzen, einen Referenzraum anfordern und dann in session.requestAnimationFrame() für jede Ansicht (XRView) der Betrachterpose rendern.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $mode Pflicht | "inline" | "immersive-vr" | "immersive-ar" | Sitzungsmodus, den du an navigator.xr.requestSession() übergibst, um eine XRSession zu erhalten. |
|
| $options | XRSessionInit | {} | Optionales Objekt mit requiredFeatures und optionalFeatures (z. B. "local-floor", "hand-tracking", "hit-test", "dom-overlay") sowie ggf. domOverlay: { root }. |
Rückgabewert
navigator.xr.requestSession() gibt ein Promise zurück, das mit der neuen XRSession erfüllt wird. Ist der Modus oder eine Pflichtfunktion nicht verfügbar, wird es mit NotSupportedError abgelehnt; ohne Benutzergeste mit SecurityError.Beispiele
Immersive VR-Sitzung starten und Render-Schleife aufsetzen
const button = document.querySelector('#enter-vr');
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2', { xrCompatible: true });
button.addEventListener('click', async () => {
if (!navigator.xr || !(await navigator.xr.isSessionSupported('immersive-vr'))) {
console.log('Immersive VR wird nicht unterstützt');
return;
}
// Muss innerhalb einer Benutzergeste angefordert werden
const session = await navigator.xr.requestSession('immersive-vr', {
optionalFeatures: ['local-floor']
});
await session.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
const refSpace = await session.requestReferenceSpace('local-floor')
.catch(() => session.requestReferenceSpace('local'));
const onFrame = (time, frame) => {
const s = frame.session;
s.requestAnimationFrame(onFrame);
const pose = frame.getViewerPose(refSpace);
if (!pose) return;
const layer = s.renderState.baseLayer;
gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
gl.clearColor(0.1, 0.1, 0.2, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
for (const view of pose.views) {
const vp = layer.getViewport(view);
gl.viewport(vp.x, vp.y, vp.width, vp.height);
// Szene hier mit view.projectionMatrix und view.transform zeichnen
}
};
session.requestAnimationFrame(onFrame);
session.addEventListener('end', () => {
console.log('XR-Sitzung beendet');
});
});
Controller-Eingaben über select- und inputsourceschange-Events
const setupInput = (session) => {
session.addEventListener('inputsourceschange', (event) => {
for (const source of event.added) {
console.log(`Neu: ${source.handedness} (${source.profiles[0] ?? 'unbekannt'})`);
}
for (const source of event.removed) {
console.log(`Entfernt: ${source.handedness}`);
}
});
session.addEventListener('select', (event) => {
// event.frame ist nur während des Handlers gültig
console.log(`Auswahl mit ${event.inputSource.handedness}-Controller`);
});
session.addEventListener('squeeze', () => {
console.log('Greiftaste gedrückt');
});
};
// Nutzung: setupInput(session) direkt nach requestSession() aufrufen
Sitzung sauber beenden und Sichtbarkeit beachten
let paused = false;
const watchSession = (session) => {
session.addEventListener('visibilitychange', () => {
// z. B. Systemmenü des Headsets geöffnet
paused = session.visibilityState !== 'visible';
console.log(`Sichtbarkeit: ${session.visibilityState}`);
});
document.querySelector('#exit-vr').addEventListener('click', async () => {
await session.end();
console.log('Sitzung per Button beendet');
}, { once: true });
};
AR-Sitzung mit Hit-Test (Oberflächen erkennen)
const startAR = async (gl) => {
const session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['hit-test']
});
await session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
const localSpace = await session.requestReferenceSpace('local');
const viewerSpace = await session.requestReferenceSpace('viewer');
const hitTestSource = await session.requestHitTestSource({ space: viewerSpace });
console.log(`Blend-Modus: ${session.environmentBlendMode}`);
session.requestAnimationFrame(function onFrame(t, frame) {
frame.session.requestAnimationFrame(onFrame);
const [hit] = frame.getHitTestResults(hitTestSource);
if (hit) {
const pose = hit.getPose(localSpace);
// Fadenkreuz an pose.transform.position platzieren
}
});
};