Signatur
Beschreibung
Ein VRDisplay steht für ein einzelnes VR-Gerät, zum Beispiel ein Headset wie Oculus Rift oder HTC Vive, das die WebVR API unterstützt. Einen Konstruktor gibt es nicht. Instanzen bekommst du nur über navigator.getVRDisplays(), das ein Promise mit einem Array aller verfügbaren Geräte liefert. Außerdem stecken sie in der Eigenschaft display von VRDisplayEvent-Objekten.
Das Interface fasst alles zusammen, was man zum Rendern einer VR-Szene braucht:
- Eigenschaften:
capabilities(VRDisplayCapabilities),displayId,displayName,isPresenting,depthNear/depthFar(Clipping-Ebenen in Metern),stageParameters(Raumgröße für Room-Scale) und das veralteteisConnected. - Darstellung:
requestPresent(layers)startet die Ausgabe eines<canvas>auf dem Headset,exitPresent()beendet sie,getLayers()liefert die aktiven Ebenen. - Render-Loop:
requestAnimationFrame(callback)undcancelAnimationFrame(handle)laufen mit der Bildwiederholrate des Headsets (oft 90 Hz), nicht mit der des Monitors.submitFrame()schickt das fertig gerenderte Bild an das Gerät. - Tracking-Daten:
getFrameData(frameData)füllt einVRFrameData-Objekt mit Pose sowie View- und Projektionsmatrizen für beide Augen.getEyeParameters("left"|"right")liefert Auflösung und Versatz pro Auge.getPose()undresetPose()sind veraltet.
Ein typischer Ablauf sieht so aus: Gerät ermitteln, bei einer Nutzeraktion requestPresent() aufrufen, die Canvas-Größe nach getEyeParameters() einstellen und dann in vrDisplay.requestAnimationFrame() pro Bild getFrameData() lesen, beide Augen rendern und submitFrame() aufrufen. Für neue Projekte solltest du VRDisplay nicht mehr verwenden. Der Nachfolger ist die WebXR Device API mit XRSession.
Rückgabewert
navigator.getVRDisplays() (Promise<VRDisplay[]>) oder über event.display eines VRDisplayEvent.Beispiele
Verfügbare VR-Geräte ermitteln
const findVRDisplay = async () => {
if (!('getVRDisplays' in navigator)) {
console.log('WebVR wird nicht unterstützt – bitte WebXR verwenden.');
return null;
}
const displays = await navigator.getVRDisplays();
if (displays.length === 0) {
console.log('Kein VR-Gerät gefunden.');
return null;
}
const vrDisplay = displays[0];
console.log(`Gerät: ${vrDisplay.displayName} (ID ${vrDisplay.displayId})`);
console.log('Kann präsentieren:', vrDisplay.capabilities.canPresent);
console.log('Positions-Tracking:', vrDisplay.capabilities.hasPosition);
return vrDisplay;
};
findVRDisplay();
Präsentation auf Klick starten und Canvas-Größe setzen
const canvas = document.querySelector('#vr-canvas');
const button = document.querySelector('#enter-vr');
const setup = async () => {
const [vrDisplay] = await navigator.getVRDisplays();
if (!vrDisplay) return;
// requestPresent() braucht eine Nutzeraktion
button.addEventListener('click', async () => {
try {
await vrDisplay.requestPresent([{ source: canvas }]);
const left = vrDisplay.getEyeParameters('left');
const right = vrDisplay.getEyeParameters('right');
// Beide Augen liegen nebeneinander auf einem Canvas
canvas.width = Math.max(left.renderWidth, right.renderWidth) * 2;
canvas.height = Math.max(left.renderHeight, right.renderHeight);
console.log('Präsentiert:', vrDisplay.isPresenting);
} catch (err) {
console.error('requestPresent fehlgeschlagen:', err);
}
});
window.addEventListener('vrdisplaypresentchange', () => {
console.log('Präsentationsstatus:', vrDisplay.isPresenting);
});
};
setup();
Render-Loop mit getFrameData() und submitFrame()
const frameData = new VRFrameData();
let frameHandle = null;
// gl ist ein WebGLRenderingContext des präsentierten Canvas
const startLoop = (vrDisplay, gl, drawScene) => {
const onFrame = () => {
frameHandle = vrDisplay.requestAnimationFrame(onFrame);
vrDisplay.getFrameData(frameData);
const { width, height } = gl.canvas;
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Linkes Auge
gl.viewport(0, 0, width / 2, height);
drawScene(frameData.leftProjectionMatrix, frameData.leftViewMatrix);
// Rechtes Auge
gl.viewport(width / 2, 0, width / 2, height);
drawScene(frameData.rightProjectionMatrix, frameData.rightViewMatrix);
vrDisplay.submitFrame();
};
frameHandle = vrDisplay.requestAnimationFrame(onFrame);
};
const stopLoop = async (vrDisplay) => {
vrDisplay.cancelAnimationFrame(frameHandle);
await vrDisplay.exitPresent();
console.log('VR beendet');
};
// Wichtig · Fallstricke
Veraltet und nicht standardisiert: Die WebVR API ist nie über den Entwurfsstatus hinausgekommen und wurde durch die WebXR Device API ersetzt (navigator.xr.requestSession(), XRSession, XRFrame). Chrome hatte WebVR nur hinter einem Flag oder als Origin Trial, und Firefox hat die Unterstützung inzwischen entfernt. Oculus Browser und Samsung Internet haben sie ebenfalls aufgegeben. Für neuen Code solltest du WebXR verwenden oder für Altprojekte das Polyfill webvr-polyfill bzw. webxr-polyfill einsetzen.
requestPresent()funktioniert nur innerhalb einer Nutzeraktion (z. B.click). Sonst wird dasPromiseabgelehnt.- Die API steht nur in einem sicheren Kontext (HTTPS) zur Verfügung. In iframes kann eine Permissions Policy sie zusätzlich sperren.
- Für den VR-Loop musst du
vrDisplay.requestAnimationFrame()nutzen, nichtwindow.requestAnimationFrame(). Das Fenster-Pendant läuft mit der Monitorfrequenz und erzeugt im Headset Ruckeln und Übelkeit. getPose(),resetPose()undisConnectedsind schon innerhalb von WebVR veraltet. Nimm stattdessengetFrameData()und die Eventsvrdisplayconnect/vrdisplaydisconnect.- Lege ein
VRFrameData-Objekt einmal an und verwende es in jedem Frame wieder. Neue Objekte pro Bild belasten den Garbage Collector.