Start · Sprachen · JavaScript · Referenz · GPURenderPassEncoder.beginOcclusionQuery()

GPURenderPassEncoder.beginOcclusionQuery()

Methode

Startet eine Okklusionsabfrage am Index <code>queryIndex</code> des <code>occlusionQuerySet</code>, das beim Beginn des Render-Passes angegeben wurde.

seit JavaScript WebGPU – Chrome/Edge 113+, Firefox 141+ (Windows), Kategorie: webapi

Signatur

GPURenderPassEncoder.prototype.beginOcclusionQuery(queryIndex)

Beschreibung

beginOcclusionQuery() gehört zum GPURenderPassEncoder und startet eine Okklusionsabfrage (Verdeckungsabfrage). Alle Zeichenbefehle zwischen beginOcclusionQuery() und endOcclusionQuery() werden daraufhin geprüft, ob mindestens ein Fragment den Tiefen- und Stencil-Test besteht, also sichtbar wäre. Das Ergebnis wird als 64-Bit-Ganzzahl in das GPUQuerySet geschrieben, das beim Aufruf von GPUCommandEncoder.beginRenderPass() als occlusionQuerySet übergeben wurde.

Typische Anwendungen sind Occlusion Culling: Man zeichnet vereinfachte Hüllkörper (Bounding Boxes) und prüft, ob sie ganz hinter anderer Geometrie liegen. Aufwendige Objekte, die komplett verdeckt sind, lassen sich dann in späteren Frames überspringen. Weitere Anwendungen sind Lens-Flare-Effekte, bei denen man prüft, ob die Sonne sichtbar ist, und Sichtbarkeitsprüfungen für LOD-Systeme.

Die Ergebnisse stehen nicht sofort zur Verfügung. Sie müssen erst mit GPUCommandEncoder.resolveQuerySet() in einen GPUBuffer kopiert und dann über einen Buffer mit MAP_READ asynchron ausgelesen werden. Die Ergebnisse sind in der Regel einen oder mehrere Frames alt. Laut Spezifikation bedeutet ein Wert ungleich 0 „sichtbar“. Eine genaue Anzahl der Samples wird nicht zuverlässig garantiert.

Parameter

Name Typ Default Beschreibung
$queryIndex Pflicht number Index (GPUSize32) innerhalb des occlusionQuerySet, in den das Ergebnis der Abfrage geschrieben wird. Er muss kleiner als querySet.count sein und darf im selben Render-Pass nur einmal verwendet werden.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Das Ergebnis landet nach dem Ausführen und Auflösen im GPUQuerySet.

Beispiele

QuerySet anlegen und eine Abfrage im Render-Pass ausführen

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

// QuerySet für bis zu 8 Okklusionsabfragen
const occlusionQuerySet = device.createQuerySet({ type: 'occlusion', count: 8 });

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
  colorAttachments: [{
    view: context.getCurrentTexture().createView(),
    loadOp: 'clear',
    storeOp: 'store',
    clearValue: { r: 0, g: 0, b: 0, a: 1 }
  }],
  depthStencilAttachment: {
    view: depthTexture.createView(),
    depthLoadOp: 'clear',
    depthStoreOp: 'store',
    depthClearValue: 1.0
  },
  occlusionQuerySet // Pflicht für beginOcclusionQuery()
});

pass.setPipeline(scenePipeline);
pass.draw(sceneVertexCount); // Verdecker zuerst zeichnen

pass.setPipeline(boundingBoxPipeline);
pass.beginOcclusionQuery(0);
pass.draw(36); // Bounding Box des zu prüfenden Objekts
pass.endOcclusionQuery();

pass.end();

Ergebnisse auflösen und auslesen

const count = 8;
const resolveBuffer = device.createBuffer({
  size: count * 8, // 8 Byte pro Ergebnis (uint64)
  usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC
});
const readBuffer = device.createBuffer({
  size: count * 8,
  usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});

// nach pass.end():
encoder.resolveQuerySet(occlusionQuerySet, 0, count, resolveBuffer, 0);
encoder.copyBufferToBuffer(resolveBuffer, 0, readBuffer, 0, count * 8);
device.queue.submit([encoder.finish()]);

await readBuffer.mapAsync(GPUMapMode.READ);
const results = new BigUint64Array(readBuffer.getMappedRange().slice(0));
readBuffer.unmap();

const visible = results[0] > 0n;
console.log(`Objekt 0 sichtbar: ${visible}`);
Objekt 0 sichtbar: true

Mehrere Objekte pro Pass prüfen (Occlusion Culling)

objects.forEach((obj, i) => {
  pass.setBindGroup(0, obj.bindGroup);
  pass.beginOcclusionQuery(i); // jeder Index nur einmal pro Pass
  pass.draw(36);
  pass.endOcclusionQuery();
});

// Im nächsten Frame: nur sichtbare Objekte vollständig zeichnen
const visibleObjects = objects.filter((_, i) => lastResults[i] > 0n);

// Wichtig · Fallstricke

  • Wurde beim Aufruf von beginRenderPass() kein occlusionQuerySet übergeben, führt der Aufruf zu einem Validierungsfehler. Der Encoder wird dann ungültig und finish() liefert einen ungültigen Command Buffer.
  • Abfragen dürfen nicht verschachtelt werden: Vor einem erneuten beginOcclusionQuery() muss endOcclusionQuery() aufgerufen werden. Beim Aufruf von pass.end() darf keine Abfrage mehr offen sein.
  • Jeder queryIndex darf pro Render-Pass nur einmal genutzt werden und muss kleiner als querySet.count sein.
  • Ohne Tiefen- oder Stencil-Anhang besteht jedes Fragment den Test. Die Abfrage ist dann meist sinnlos. Schalte für Bounding-Box-Tests das Schreiben in den Tiefenpuffer aus (depthWriteEnabled: false) und schreibe keine Farbe (writeMask: 0).
  • Die Ergebnisse sind nur zuverlässig als „0 = verdeckt / >0 = sichtbar“ nutzbar. Werte mit BigUint64Array lesen und mit 0n vergleichen.
  • Nicht synchron auf Ergebnisse warten, weil das die Pipeline blockiert. Besser Ergebnisse aus dem vorherigen Frame verwenden.
  • WebGPU ist nur in sicheren Kontexten (HTTPS) verfügbar. Die Browser-Unterstützung ist noch uneinheitlich, deshalb vorher navigator.gpu prüfen.