Signatur
Beschreibung
Die Erweiterung OES_standard_derivatives gehört zur WebGL API. Ist sie aktiv, stehen in GLSL-ES-1.0-Fragment-Shadern die Funktionen dFdx(p), dFdy(p) und fwidth(p) zur Verfügung. Sie liefern, wie stark sich ein Wert von einem Bildpunkt zum benachbarten in x- bzw. y-Richtung ändert. fwidth(p) entspricht dabei abs(dFdx(p)) + abs(dFdy(p)).
Typische Anwendungen sind kantengeglättete Linien und Gitter (Antialiasing ohne MSAA), SDF-Schriftdarstellung (Signed Distance Fields), Flat Shading ohne eigene Normalen pro Fläche (die Normale wird aus cross(dFdx(pos), dFdy(pos)) berechnet) sowie eigene Berechnungen der Mip-Stufe.
Aktiviert wird die Erweiterung auf JavaScript-Seite mit gl.getExtension("OES_standard_derivatives"). Im Shader-Quelltext ist zusätzlich die Direktive #extension GL_OES_standard_derivatives : enable nötig. Das zurückgegebene Objekt hat keine Methoden, aber eine Konstante: FRAGMENT_SHADER_DERIVATIVE_HINT_OES (0x8B8B). Sie wird mit gl.hint() verwendet und bittet den Treiber um mehr Genauigkeit oder mehr Geschwindigkeit (gl.NICEST, gl.FASTEST, gl.DONT_CARE).
In WebGL 2 gehören diese Funktionen zum Standard von GLSL ES 3.00. Dort ist die Erweiterung weder vorhanden noch nötig, und getExtension gibt null zurück.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $FRAGMENT_SHADER_DERIVATIVE_HINT_OES | GLenum (0x8B8B) | Konstante für gl.hint(target, mode) und gl.getParameter(). Legt fest, wie genau die Ableitungen berechnet werden sollen. Mögliche Werte für mode: gl.FASTEST, gl.NICEST oder gl.DONT_CARE (Standard). |
Rückgabewert
gl.getExtension("OES_standard_derivatives") gibt das Erweiterungsobjekt zurück. Wird die Erweiterung nicht unterstützt oder handelt es sich um einen WebGL-2-Kontext, ist das Ergebnis null.Beispiele
Erweiterung aktivieren und Hint setzen
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('OES_standard_derivatives');
if (!ext) {
console.warn('Ableitungsfunktionen werden nicht unterstützt');
} else {
// Höhere Genauigkeit anfordern
gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.NICEST);
const mode = gl.getParameter(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES);
console.log(mode === gl.NICEST);
}
Kantengeglättetes Gitter im Fragment-Shader
const gl = document.querySelector('canvas').getContext('webgl');
gl.getExtension('OES_standard_derivatives');
const fragSrc = `
#extension GL_OES_standard_derivatives : enable
precision mediump float;
varying vec2 vUv;
void main() {
vec2 coord = vUv * 10.0;
// Breite eines Pixels im Gitterraum
vec2 grid = abs(fract(coord - 0.5) - 0.5) / fwidth(coord);
float line = min(grid.x, grid.y);
float alpha = 1.0 - min(line, 1.0);
gl_FragColor = vec4(vec3(alpha), 1.0);
}`;
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, fragSrc);
gl.compileShader(shader);
console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS));
Flat Shading mit Normalen aus dFdx/dFdy
const gl = document.querySelector('canvas').getContext('webgl');
const hasDerivatives = gl.getExtension('OES_standard_derivatives') !== null;
const fragSrc = `
#extension GL_OES_standard_derivatives : enable
precision mediump float;
varying vec3 vViewPos;
void main() {
// Flächennormale ohne Normalen-Attribut
vec3 n = normalize(cross(dFdx(vViewPos), dFdy(vViewPos)));
float light = max(dot(n, normalize(vec3(0.3, 0.5, 1.0))), 0.0);
gl_FragColor = vec4(vec3(light), 1.0);
}`;
if (hasDerivatives) {
const fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, fragSrc);
gl.compileShader(fs);
console.log(gl.getShaderInfoLog(fs) || 'Shader kompiliert');
}
WebGL 1 und WebGL 2 gemeinsam behandeln
const canvas = document.createElement('canvas');
const gl2 = canvas.getContext('webgl2');
const supportsDerivatives = (gl) => {
if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) {
return true; // Fest im Kern von GLSL ES 3.00
}
return gl.getExtension('OES_standard_derivatives') !== null;
};
console.log(gl2 ? supportsDerivatives(gl2) : 'kein WebGL 2');
// Wichtig · Fallstricke
- Es genügt nicht, nur
getExtension()aufzurufen. Der Shader braucht zusätzlich#extension GL_OES_standard_derivatives : enable, sonst schlägt das Kompilieren mit „dFdx: no matching overloaded function“ oder einer ähnlichen Meldung fehl. Diese Direktive muss vor jedem anderen Code stehen, der kein Präprozessor-Code ist. - Die Funktionen gibt es nur im Fragment-Shader, nicht im Vertex-Shader.
- Die Ableitungen werden für Blöcke von 2×2 Bildpunkten berechnet. Innerhalb von Verzweigungen, die nicht für alle Bildpunkte des Blocks gleich ausgehen (nicht-uniformer Kontrollfluss), sind die Ergebnisse undefiniert. Die Ableitungen sollten daher vor
if-Zweigen berechnet werden. - Die Werte sind treiberabhängig und oft recht grob.
FRAGMENT_SHADER_DERIVATIVE_HINT_OESist nur ein Hinweis an den Treiber und keine Garantie. - In WebGL 2 (
#version 300 es) gibt es die Erweiterung nicht. Dort darf auch keine#extension-Direktive für sie im Shader stehen. - Unterstützung: In WebGL 1 steht die Erweiterung in praktisch allen Desktop- und Mobil-Browsern zur Verfügung. Trotzdem sollte der Rückgabewert immer auf
nullgeprüft werden.