Signatur
Beschreibung
submitFrame() gehört zur veralteten WebVR-API. Die Methode nimmt den aktuellen Zustand des Canvas, das über VRDisplay.requestPresent() als VRLayerInit.source angemeldet wurde, und schickt ihn an das VR-Headset. Erst durch diesen Aufruf wird ein gerendertes Bild im Headset sichtbar.
Ein typischer Render-Durchlauf sieht so aus:
- mit
VRDisplay.requestAnimationFrame()den nächsten Frame anfordern (im Takt des Headsets, nicht des Monitors), - mit
VRDisplay.getFrameData()die aktuelle Pose und die Projektions- bzw. View-Matrizen beider Augen abholen, - die Szene für das linke und das rechte Auge jeweils in eine Hälfte des Canvas zeichnen (
leftBounds/rightBounds), - zum Schluss
submitFrame()aufrufen.
Die Pose, mit der gerendert wurde, sollte aus demselben getFrameData()-Aufruf stammen, der unmittelbar vor dem Zeichnen erfolgte. Nur dann kann die Laufzeitumgebung die Bewegungskompensation (Reprojektion) korrekt durchführen, was Ruckeln und Übelkeit verringert.
Beispiele
Vollständiger WebVR-Render-Durchlauf mit WebGL
const canvas = document.querySelector('#vr-canvas');
const gl = canvas.getContext('webgl');
const frameData = new VRFrameData();
let vrDisplay = null;
const drawScene = (projectionMatrix, viewMatrix) => {
// Hier die eigene Szene mit den Matrizen zeichnen
gl.clearColor(0.1, 0.2, 0.3, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
};
const onVRFrame = () => {
vrDisplay.requestAnimationFrame(onVRFrame);
vrDisplay.getFrameData(frameData);
const halfWidth = canvas.width / 2;
// Linkes Auge
gl.viewport(0, 0, halfWidth, canvas.height);
drawScene(frameData.leftProjectionMatrix, frameData.leftViewMatrix);
// Rechtes Auge
gl.viewport(halfWidth, 0, halfWidth, canvas.height);
drawScene(frameData.rightProjectionMatrix, frameData.rightViewMatrix);
// Fertigen Frame an das Headset übergeben
vrDisplay.submitFrame();
};
const startVR = async () => {
const displays = await navigator.getVRDisplays();
if (displays.length === 0) return console.log('Kein VR-Headset gefunden');
vrDisplay = displays[0];
await vrDisplay.requestPresent([{ source: canvas }]);
vrDisplay.requestAnimationFrame(onVRFrame);
};
document.querySelector('#enter-vr').addEventListener('click', startVR);
submitFrame() nur während der Präsentation aufrufen
const renderLoop = (vrDisplay, frameData, render) => {
const loop = () => {
vrDisplay.requestAnimationFrame(loop);
vrDisplay.getFrameData(frameData);
render(frameData);
// Ohne aktive Präsentation hat submitFrame() keine Wirkung
if (vrDisplay.isPresenting) {
vrDisplay.submitFrame();
}
};
vrDisplay.requestAnimationFrame(loop);
};
Feature-Erkennung: WebXR bevorzugen, WebVR als Rückfall
const detectVR = async () => {
if (navigator.xr && await navigator.xr.isSessionSupported('immersive-vr')) {
return 'WebXR';
}
if ('getVRDisplays' in navigator && typeof VRDisplay !== 'undefined' &&
'submitFrame' in VRDisplay.prototype) {
return 'WebVR (veraltet)';
}
return 'keine VR-Unterstützung';
};
detectVR().then(console.log);
// Wichtig · Fallstricke
Veraltet: Die WebVR-API und damit submitFrame() wurde nie standardisiert und ist durch die WebXR Device API ersetzt. In WebXR gibt es kein explizites Übergeben mehr: Der Frame wird automatisch angezeigt, sobald der Callback von XRSession.requestAnimationFrame() fertig ist und in den XRWebGLLayer-Framebuffer gezeichnet wurde.
Browser-Kompatibilität: Unterstützt wurde WebVR nur in älteren Firefox-Versionen (Windows/macOS), im Samsung Internet Browser, in Oculus Browser und in Chrome mit Flag bzw. Origin-Trial. In aktuellen Browsern ist die Schnittstelle entfernt. Für neue Projekte nicht mehr einsetzen und bei Bedarf den WebXR-Polyfill verwenden.
- Ohne vorheriges erfolgreiches
requestPresent()hat der Aufruf keine Wirkung. - Den Render-Durchlauf mit
VRDisplay.requestAnimationFrame()steuern, nicht mitwindow.requestAnimationFrame(): Headsets laufen oft mit 90 Hz und mehr, und der Fenster-Takt wird während der Präsentation gedrosselt oder angehalten. - Nur einmal pro Frame aufrufen. Wird zwischen
getFrameData()undsubmitFrame()zu lange gerechnet, verpasst man den Anzeigetakt und das Bild ruckelt. - Für mehr Leistung das WebGL-Canvas mit
preserveDrawingBuffer: false(Standardwert) erzeugen.