Signatur
Beschreibung
Die Methode stencilOp() bestimmt, was mit dem Wert im Stencil-Buffer passiert, nachdem für ein Fragment der Stencil- und der Tiefentest ausgewertet wurden. Es gibt drei Fälle: Der Stencil-Test schlägt fehl (fail), der Stencil-Test besteht, aber der Tiefentest schlägt fehl (zfail), oder beide Tests bestehen (zpass). Die Einstellung gilt sowohl für vorder- als auch für rückseitige Polygone.
Zusammen mit stencilFunc() (Vergleichsbedingung) und stencilMask() (Schreibmaske) ist stencilOp() die Grundlage für klassische Stencil-Techniken: Masken und Ausschnitte (Portale, Spiegel), Umrisse/Outlines, Schattenvolumen (Stencil Shadows) oder das Zählen von Überlagerungen.
Die Operation wirkt nur, wenn der Stencil-Test mit gl.enable(gl.STENCIL_TEST) aktiviert ist und der Zeichenpuffer überhaupt einen Stencil-Buffer besitzt – beim Standard-Framebuffer muss der Kontext dafür mit { stencil: true } erzeugt werden. Sollen Vorder- und Rückseiten unterschiedlich behandelt werden, verwendet man stattdessen stencilOpSeparate().
Mögliche Werte für alle drei Parameter: gl.KEEP (Standard, Wert bleibt), gl.ZERO (auf 0 setzen), gl.REPLACE (durch Referenzwert aus stencilFunc() ersetzen), gl.INCR / gl.DECR (um 1 erhöhen/verringern, an Grenzen gesättigt), gl.INCR_WRAP / gl.DECR_WRAP (erhöhen/verringern mit Überlauf) und gl.INVERT (bitweise invertieren).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $fail Pflicht | GLenum | gl.KEEP | Aktion, wenn der Stencil-Test fehlschlägt. |
| $zfail Pflicht | GLenum | gl.KEEP | Aktion, wenn der Stencil-Test besteht, der Tiefentest aber fehlschlägt. |
| $zpass Pflicht | GLenum | gl.KEEP | Aktion, wenn Stencil- und Tiefentest bestehen (oder kein Tiefentest aktiv ist). |
Rückgabewert
undefined).Beispiele
Grundeinstellung setzen und abfragen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl', { stencil: true });
gl.enable(gl.STENCIL_TEST);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
// Aktuelle Einstellung auslesen
console.log(gl.getParameter(gl.STENCIL_FAIL) === gl.KEEP);
console.log(gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS) === gl.REPLACE);
console.log(gl.getParameter(gl.STENCIL_BACK_PASS_DEPTH_PASS) === gl.REPLACE);
Maske zeichnen und Inhalt darauf beschränken
const gl = document.querySelector('canvas').getContext('webgl', { stencil: true });
// drawMask() und drawScene() seien eigene Zeichenfunktionen
const drawMask = () => { /* z. B. Kreis zeichnen */ };
const drawScene = () => { /* eigentliche Szene */ };
gl.clearStencil(0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
gl.enable(gl.STENCIL_TEST);
// 1. Durchgang: Maske in den Stencil-Buffer schreiben (Wert 1)
gl.stencilFunc(gl.ALWAYS, 1, 0xff);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
gl.colorMask(false, false, false, false); // keine Farbe schreiben
drawMask();
// 2. Durchgang: nur dort zeichnen, wo Stencil == 1
gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.EQUAL, 1, 0xff);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); // Buffer nicht mehr verändern
drawScene();
Überlagerungen zählen
const gl = document.querySelector('canvas').getContext('webgl', { stencil: true });
const drawOverlappingQuads = () => { /* mehrere halbtransparente Flächen */ };
gl.enable(gl.STENCIL_TEST);
gl.clear(gl.STENCIL_BUFFER_BIT);
// Jedes gezeichnete Fragment erhöht den Zähler (gesättigt bei 255)
gl.stencilFunc(gl.ALWAYS, 0, 0xff);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
drawOverlappingQuads();
// Danach z. B. nur Pixel mit mindestens 2 Überlagerungen hervorheben
gl.stencilFunc(gl.LEQUAL, 2, 0xff);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
// Wichtig · Fallstricke
- Kein Stencil-Buffer: WebGL erzeugt standardmäßig keinen Stencil-Buffer. Ohne
getContext('webgl', { stencil: true })(bzw. ein Renderbuffer mitDEPTH_STENCIL/STENCIL_INDEX8beim eigenen Framebuffer) hatstencilOp()keinen sichtbaren Effekt. Prüfen mitgl.getContextAttributes().stencilodergl.getParameter(gl.STENCIL_BITS). - Stencil-Test aktivieren: Ohne
gl.enable(gl.STENCIL_TEST)wird der Buffer weder getestet noch verändert. - Schreibmaske beachten:
stencilMask()begrenzt, welche Bits tatsächlich geschrieben werden – bei Maske0x00bleibt jede Operation wirkungslos. - Ungültige Werte erzeugen den Fehler
gl.INVALID_ENUM(abfragbar mitgl.getError()); der Zustand bleibt dann unverändert. INCR/DECRsättigen an 0 bzw. dem Maximalwert,INCR_WRAP/DECR_WRAPlaufen über – für Schattenvolumen (Z-Fail/Carmack) sind die Wrap-Varianten meist die richtige Wahl.- Die Einstellung ist globaler Kontextzustand und bleibt bis zur nächsten Änderung bestehen – nach Stencil-Durchgängen ggf. auf
KEEP, KEEP, KEEPzurücksetzen. - Verfügbar in allen Browsern mit WebGL-Unterstützung, ebenso in
WebGL2RenderingContext.