Signatur
Beschreibung
stencilFuncSeparate() legt fest, wie der Stencil-Test entscheidet, ob ein Fragment gezeichnet wird. Anders als stencilFunc(), das beide Seiten gleich behandelt, kann man hier die Regeln für Vorderseiten (gl.FRONT) und Rückseiten (gl.BACK) getrennt setzen.
Beim Test wird (ref & mask) mit (stencilwert & mask) über die gewählte Vergleichsfunktion verglichen. Bei gl.LESS besteht das Fragment zum Beispiel, wenn (ref & mask) < (stencil & mask) gilt. Was danach mit dem Stencil-Puffer passiert, legt man mit stencilOp() bzw. stencilOpSeparate() fest.
Typische Einsatzfälle sind Techniken, die in einem Durchgang Vorder- und Rückseiten unterschiedlich auswerten: Stencil Shadow Volumes (Z-Pass/Z-Fail), Schnittflächen (Capping) bei Clipping-Ebenen oder das Markieren von Bereichen innerhalb geschlossener Körper. Der Stencil-Test wirkt nur, wenn er mit gl.enable(gl.STENCIL_TEST) eingeschaltet ist und der Zeichenpuffer einen Stencil-Puffer hat.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $face Pflicht | GLenum | Für welche Seite die Einstellung gilt: gl.FRONT, gl.BACK oder gl.FRONT_AND_BACK. |
|
| $func Pflicht | GLenum | gl.ALWAYS | Vergleichsfunktion: gl.NEVER, gl.LESS, gl.EQUAL, gl.LEQUAL, gl.GREATER, gl.NOTEQUAL, gl.GEQUAL oder gl.ALWAYS. |
| $ref Pflicht | GLint | 0 | Referenzwert für den Vergleich. Er wird auf den Bereich 0 bis 2^n - 1 begrenzt, wobei n die Zahl der Stencil-Bits ist (meist 8). |
| $mask Pflicht | GLuint | 0xFFFFFFFF (alle Bits gesetzt) | Bitmaske, die vor dem Vergleich per UND auf ref und den gespeicherten Stencil-Wert angewendet wird. |
Rückgabewert
undefined). Bei ungültigen Werten für face oder func wird der Fehler gl.INVALID_ENUM gesetzt, den man mit gl.getError() abfragen kann.Beispiele
Grundlegende Einrichtung für Vorder- und Rückseite
const canvas = document.querySelector('canvas');
// Stencil-Puffer muss explizit angefordert werden
const gl = canvas.getContext('webgl', { stencil: true });
gl.enable(gl.STENCIL_TEST);
// Vorderseiten: nur zeichnen, wo der Stencil-Wert 1 ist
gl.stencilFuncSeparate(gl.FRONT, gl.EQUAL, 1, 0xFF);
// Rückseiten: immer bestehen
gl.stencilFuncSeparate(gl.BACK, gl.ALWAYS, 0, 0xFF);
console.log(gl.getParameter(gl.STENCIL_FUNC) === gl.EQUAL);
console.log(gl.getParameter(gl.STENCIL_BACK_FUNC) === gl.ALWAYS);
Stencil Shadow Volume (Z-Fail) in einem Durchgang
const gl = document.querySelector('canvas').getContext('webgl', { stencil: true, depth: true });
const renderShadowVolume = (drawVolume) => {
gl.enable(gl.STENCIL_TEST);
gl.enable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE); // beide Seiten rastern
gl.colorMask(false, false, false, false);
gl.depthMask(false);
// Test besteht für beide Seiten immer
gl.stencilFuncSeparate(gl.FRONT_AND_BACK, gl.ALWAYS, 0, 0xFF);
// Rückseite: bei Tiefentest-Fehler erhöhen, Vorderseite: verringern
gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.INCR_WRAP, gl.KEEP);
gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.DECR_WRAP, gl.KEEP);
drawVolume();
// Beleuchtung nur dort, wo der Stencil-Wert 0 ist (nicht im Schatten)
gl.colorMask(true, true, true, true);
gl.stencilFuncSeparate(gl.FRONT_AND_BACK, gl.EQUAL, 0, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
};
renderShadowVolume(() => { /* gl.drawArrays(...) für das Volumen */ });
Einzelne Bits mit einer Maske prüfen
const gl = document.querySelector('canvas').getContext('webgl', { stencil: true });
gl.enable(gl.STENCIL_TEST);
// Bit 2 (Wert 4) kennzeichnet z. B. "Spiegelfläche" – nur Vorderseiten dort zeichnen
gl.stencilFuncSeparate(gl.FRONT, gl.EQUAL, 0b100, 0b100);
// Rückseiten nie zeichnen
gl.stencilFuncSeparate(gl.BACK, gl.NEVER, 0, 0xFF);
console.log(gl.getParameter(gl.STENCIL_VALUE_MASK));
console.log(gl.getParameter(gl.STENCIL_REF));
// Wichtig · Fallstricke
- Stencil-Puffer anfordern: Der Standard-Zeichenpuffer hat keinen Stencil-Puffer. Ohne
getContext('webgl', { stencil: true })(oder einen Framebuffer mitSTENCIL- bzw.DEPTH_STENCIL-Anhang) hat der Aufruf keine sichtbare Wirkung. Prüfen lässt sich das mitgl.getParameter(gl.STENCIL_BITS). - WebGL-Einschränkung: Anders als in OpenGL ES verlangt WebGL, dass
refundmaskfür Vorder- und Rückseite übereinstimmen (bezogen auf die tatsächlichen Stencil-Bits). Wenn nicht, erzeugen Zeichenaufrufe (drawArrays/drawElements) den Fehlergl.INVALID_OPERATION. Nurfuncdarf sich zwischen den Seiten unterscheiden. - Welche Seite vorne ist, hängt von
gl.frontFace()ab (Standard:gl.CCW). Wenngl.CULL_FACEaktiv ist, werden verworfene Seiten gar nicht erst getestet. - Der Stencil-Test braucht
gl.enable(gl.STENCIL_TEST). Den Puffer leert man mitgl.clearStencil()undgl.clear(gl.STENCIL_BUFFER_BIT). - Kompatibilität: Die Methode gibt es in allen Browsern mit WebGL 1 und genauso in
WebGL2RenderingContext.