Signatur
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
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}`);
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()keinocclusionQuerySetübergeben, führt der Aufruf zu einem Validierungsfehler. Der Encoder wird dann ungültig undfinish()liefert einen ungültigen Command Buffer. - Abfragen dürfen nicht verschachtelt werden: Vor einem erneuten
beginOcclusionQuery()mussendOcclusionQuery()aufgerufen werden. Beim Aufruf vonpass.end()darf keine Abfrage mehr offen sein. - Jeder
queryIndexdarf pro Render-Pass nur einmal genutzt werden und muss kleiner alsquerySet.countsein. - 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
BigUint64Arraylesen und mit0nvergleichen. - 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.gpuprüfen.