Start · Sprachen · JavaScript · Referenz · VRDisplay.submitFrame()

VRDisplay.submitFrame()

Methode

Übergibt den aktuell gezeichneten Inhalt der präsentierten <code>VRLayerInit</code>-Ebene (Canvas) an das <code>VRDisplay</code> und zeigt ihn im Headset an.

seit JavaScript WebVR 1.1 (veraltet, nicht standardisiert; ersetzt deprecated seit ja Kategorie: webapi

Signatur

VRDisplay.prototype.submitFrame()

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);
WebXR

// 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 mit window.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() und submitFrame() zu lange gerechnet, verpasst man den Anzeigetakt und das Bild ruckelt.
  • Für mehr Leistung das WebGL-Canvas mit preserveDrawingBuffer: false (Standardwert) erzeugen.