Signatur
Beschreibung
setStencilReference() setzt den Stencil-Referenzwert für alle folgenden Zeichenbefehle im aktuellen GPURenderPassEncoder. Der Wert wird an zwei Stellen verwendet:
- beim Stencil-Vergleich (
stencilFront.compare/stencilBack.compareindepthStencilder Render-Pipeline), zum Beispiel"equal"oder"not-equal"; - als Wert, den die Stencil-Operation
"replace"(inpassOp,failOpoderdepthFailOp) in den Stencil-Puffer schreibt.
Der Referenzwert gehört zum Zustand des Render-Passes, nicht zur Pipeline. Er bleibt also gültig, wenn Sie mit setPipeline() die Pipeline wechseln. Genau deshalb ist er praktisch: Mit einer einzigen Pipeline können Sie verschiedene Objekte mit unterschiedlichen Stencil-IDs markieren, indem Sie vor jedem draw() einen anderen Referenzwert setzen. Zu Beginn jedes Render-Passes ist der Wert 0.
Typische Einsatzfälle sind Masken (zum Beispiel Portale, Spiegel, UI-Ausschnitte), Umriss- bzw. Outline-Effekte, das Markieren von Regionen für einen späteren Pass und Techniken wie Stencil-Schatten. Wirksam ist der Wert nur, wenn der Render-Pass ein Depth-Stencil-Attachment mit Stencil-Anteil hat, zum Beispiel "stencil8" oder "depth24plus-stencil8".
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $reference Pflicht | number (GPUStencilValue, vorzeichenlose 32-Bit-Ganzzahl) | Der neue Stencil-Referenzwert. Es zählen nur die unteren Bits, die das Stencil-Format tatsächlich hat, bei stencil8 also 0–255. Vor Vergleichen wird der Wert zusätzlich mit stencilReadMask verknüpft, beim Schreiben mit stencilWriteMask. |
Rückgabewert
queue.submit() übermittelt wird.Beispiele
Maske schreiben und anschließend nur innerhalb der Maske zeichnen
// Voraussetzung: device, context, maskModule, sceneModule sind bereits eingerichtet
const stencilTexture = device.createTexture({
size: [canvas.width, canvas.height],
format: 'stencil8',
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
// Pipeline 1: schreibt den Referenzwert in den Stencil-Puffer, keine Farbe
const maskPipeline = device.createRenderPipeline({
layout: 'auto',
vertex: { module: maskModule, entryPoint: 'vs' },
fragment: {
module: maskModule,
entryPoint: 'fs',
targets: [{ format: navigator.gpu.getPreferredCanvasFormat(), writeMask: 0 }],
},
depthStencil: {
format: 'stencil8',
stencilFront: { compare: 'always', passOp: 'replace' },
stencilBack: { compare: 'always', passOp: 'replace' },
},
});
// Pipeline 2: zeichnet nur dort, wo Stencil == Referenzwert
const scenePipeline = device.createRenderPipeline({
layout: 'auto',
vertex: { module: sceneModule, entryPoint: 'vs' },
fragment: {
module: sceneModule,
entryPoint: 'fs',
targets: [{ format: navigator.gpu.getPreferredCanvasFormat() }],
},
depthStencil: {
format: 'stencil8',
stencilFront: { compare: 'equal' },
stencilBack: { compare: 'equal' },
},
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearValue: [0, 0, 0, 1],
loadOp: 'clear',
storeOp: 'store',
}],
depthStencilAttachment: {
view: stencilTexture.createView(),
stencilClearValue: 0,
stencilLoadOp: 'clear',
stencilStoreOp: 'store',
},
});
pass.setStencilReference(1); // Maske = 1
pass.setPipeline(maskPipeline);
pass.draw(6); // z. B. ein Rechteck als Maske
pass.setPipeline(scenePipeline); // Referenzwert bleibt 1
pass.draw(3); // nur innerhalb der Maske sichtbar
pass.end();
device.queue.submit([encoder.finish()]);
Mehrere Objekte mit unterschiedlichen Stencil-IDs markieren
// Eine Pipeline mit passOp: 'replace' reicht für alle Objekte
const objects = [
{ id: 1, vertexCount: 36, bindGroup: bgCube },
{ id: 2, vertexCount: 96, bindGroup: bgSphere },
{ id: 3, vertexCount: 6, bindGroup: bgPlane },
];
pass.setPipeline(markPipeline);
for (const obj of objects) {
pass.setStencilReference(obj.id); // ID pro Objekt
pass.setBindGroup(0, obj.bindGroup);
pass.draw(obj.vertexCount);
}
// Später lässt sich z. B. mit compare: 'equal' und Referenz 2
// nur die Kugel hervorheben (Outline-Effekt).
Wertebereich bei stencil8 beachten
// stencil8 hat nur 8 Bit – höhere Bits werden ignoriert
pass.setStencilReference(0xff); // 255: größter sinnvoller Wert
pass.setStencilReference(256); // wirkt wie 0 (nur untere 8 Bit zählen)
// Negative Werte sind unzulässig
try {
pass.setStencilReference(-1);
} catch (err) {
console.log(err.name);
}
// Wichtig · Fallstricke
- Zustand wird zurückgesetzt: Jeder neue Render-Pass beginnt mit dem Referenzwert
0. Der Wert gilt nicht über Passes hinweg und muss in jedem Pass neu gesetzt werden. - Kein Stencil-Attachment, keine Wirkung: Ohne Stencil-Anteil im
depthStencilAttachment(zum Beispiel bei reinem"depth32float") gibt es keinen Fehler, der Wert wirkt aber einfach nicht. - Masken beachten: Beim Vergleich wird der Referenzwert mit
stencilReadMaskverknüpft, beim Schreiben mitstencilWriteMask. Ist die Schreibmaske0, schreibt auch"replace"nichts. - Wertebereich: Der Parameter ist mit
[EnforceRange]als vorzeichenlose 32-Bit-Ganzzahl deklariert. Negative oder zu große Werte lösen deshalb einenTypeErroraus. Von den gültigen Werten zählen nur die Bits, die das Stencil-Format hat. - Vorder- und Rückseite: Es gibt nur einen Referenzwert für
stencilFrontundstencilBack. Getrennte Werte wie in OpenGL (glStencilFuncSeparate) sind nicht möglich. - Render-Bundles:
GPURenderBundleEncoderhat diese Methode nicht. Setzen Sie den Referenzwert vorexecuteBundles()auf dem Pass-Encoder. NachexecuteBundles()ist er allerdings wieder0und muss bei Bedarf erneut gesetzt werden. - Kompatibilität: WebGPU steht nur in sicheren Kontexten zur Verfügung. Prüfen Sie die Unterstützung vorab mit
if (!navigator.gpu) { … }. Firefox und Safari unterstützen WebGPU erst in neueren Versionen und je nach Plattform.