Signatur
Beschreibung
Die Erweiterung EXT_sRGB erweitert einen WebGLRenderingContext (WebGL 1) um drei Formate: SRGB_EXT, SRGB_ALPHA_EXT und SRGB8_ALPHA8_EXT. Dazu kommt die Abfragekonstante FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT. Texturen in diesen Formaten speichern ihre Farbwerte gamma-kodiert (sRGB). Liest ein Shader sie mit texture2D(), wandelt die GPU die Werte vor der Filterung in lineare Werte um. Beim Schreiben in einen Framebuffer mit sRGB-Anhang läuft die Umrechnung umgekehrt (linear → sRGB), und zwar auch beim Blending.
Das ist die Grundlage für physikalisch korrekte Beleuchtung. Lichtberechnung, Farbmischung, Texturfilterung und Blending stimmen mathematisch nur im linearen Farbraum. Bilddateien wie PNG oder JPEG liegen aber fast immer in sRGB vor. Ohne die Erweiterung muss man die Umrechnung im Shader von Hand nachbilden, etwa mit pow(color, vec3(2.2)). Das ist ungenauer und kostet Rechenzeit. Außerdem filtert die GPU dann auf nichtlinearen Werten, und dadurch entstehen z. B. zu dunkle Kanten bei bilinearer Filterung.
Die Erweiterung erhält man über gl.getExtension('EXT_sRGB'). Der Aufruf liefert ein Objekt mit den Konstanten oder null, wenn Gerät oder Browser die Erweiterung nicht unterstützen. Das Objekt hat keine Methoden, nur diese Konstanten:
ext.SRGB_EXT(0x8C40): sRGB-Format ohne Alphakanal, nur für Texturenext.SRGB_ALPHA_EXT(0x8C42): sRGB mit linearem Alphakanal, für Texturenext.SRGB8_ALPHA8_EXT(0x8C43): internes Format fürrenderbufferStorage()ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT(0x8210): Abfrage mitgetFramebufferAttachmentParameter(), liefertgl.LINEARoderext.SRGB_EXT
Rückgabewert
gl.getExtension('EXT_sRGB') liefert das Erweiterungsobjekt mit den vier Konstanten. Wird die Erweiterung nicht unterstützt, ist das Ergebnis null.Beispiele
Erweiterung aktivieren und eine sRGB-Textur laden
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('EXT_sRGB');
const loadSRGBTexture = (url) => new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Mit der Erweiterung: sRGB-Format, sonst Rückfall auf RGBA
const fmt = ext ? ext.SRGB_ALPHA_EXT : gl.RGBA;
// In WebGL 1 müssen internalformat und format gleich sein
gl.texImage2D(gl.TEXTURE_2D, 0, fmt, fmt, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
resolve(tex);
};
img.onerror = reject;
img.src = url;
});
const tex = await loadSRGBTexture('albedo.png');
console.log(ext ? 'sRGB-Textur, Shader liest lineare Werte' : 'Fallback: manuelle Gamma-Korrektur nötig');
Framebuffer mit sRGB-Renderbuffer anlegen und Kodierung prüfen
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('EXT_sRGB');
if (!ext) throw new Error('EXT_sRGB nicht verfügbar');
const fb = gl.createFramebuffer();
const rb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorage(gl.RENDERBUFFER, ext.SRGB8_ALPHA8_EXT, 512, 512);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
console.log('vollständig:', status === gl.FRAMEBUFFER_COMPLETE);
const encoding = gl.getFramebufferAttachmentParameter(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT
);
console.log('sRGB-kodiert:', encoding === ext.SRGB_EXT);
Shader mit und ohne Erweiterung (Fallback)
const gl = document.querySelector('canvas').getContext('webgl');
const hasSRGB = gl.getExtension('EXT_sRGB') !== null;
// Ohne Erweiterung dekodiert der Shader selbst (Näherung mit Gamma 2.2)
const fragmentSource = `
precision mediump float;
uniform sampler2D uAlbedo;
varying vec2 vUv;
void main() {
vec3 albedo = texture2D(uAlbedo, vUv).rgb;
${hasSRGB ? '' : 'albedo = pow(albedo, vec3(2.2));'}
vec3 lit = albedo * 0.8; // Beleuchtung im linearen Raum
// Canvas-Framebuffer ist nicht sRGB: Ausgabe von Hand kodieren
gl_FragColor = vec4(pow(lit, vec3(1.0 / 2.2)), 1.0);
}`;
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, fragmentSource);
gl.compileShader(shader);
console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS));