Signatur
Beschreibung
WebGLProgram ist ein undurchsichtiges Objekt (Handle) der WebGL API, das ein gelinktes Shader-Programm auf der GPU darstellt. Es besteht aus genau einem Vertex-Shader und einem Fragment-Shader, die beide als WebGLShader in GLSL geschrieben und vorher kompiliert werden. Erst ein gelinktes Programm kann mit gl.useProgram() aktiviert und für gl.drawArrays() oder gl.drawElements() verwendet werden.
Das Objekt hat keine eigenen Eigenschaften oder Methoden. Es wird ausschließlich über Methoden des Rendering-Kontexts (WebGLRenderingContext bzw. WebGL2RenderingContext) erzeugt und bearbeitet:
gl.createProgram()– erzeugt ein leeres Programmgl.attachShader(program, shader)– hängt Vertex- und Fragment-Shader angl.linkProgram(program)– linkt die Shader zu einem ausführbaren Programmgl.getProgramParameter(program, gl.LINK_STATUS)undgl.getProgramInfoLog(program)– prüfen den Link-Status bzw. liefern Fehlermeldungengl.getAttribLocation()/gl.getUniformLocation()– ermitteln die Positionen von Attributen und Uniformsgl.useProgram(program)undgl.deleteProgram(program)– aktivieren bzw. freigeben
Typischerweise erstellt man Programme einmalig beim Start (Kompilieren und Linken ist teuer) und wechselt pro Frame nur mit useProgram() zwischen ihnen. Attribut- und Uniform-Positionen sollten nach dem Linken einmal abgefragt und zwischengespeichert werden.
Rückgabewert
gl.createProgram() zurückgegeben (oder null, wenn der Kontext verloren ist).Beispiele
Programm aus Vertex- und Fragment-Shader erstellen und linken
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const vsSource = `
attribute vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
}
`;
const fsSource = `
precision mediump float;
uniform vec4 uColor;
void main() {
gl_FragColor = uColor;
}
`;
const compileShader = (gl, type, source) => {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
const log = gl.getShaderInfoLog(shader);
gl.deleteShader(shader);
throw new Error(`Shader-Fehler: ${log}`);
}
return shader;
};
const createProgram = (gl, vsSrc, fsSrc) => {
const vs = compileShader(gl, gl.VERTEX_SHADER, vsSrc);
const fs = compileShader(gl, gl.FRAGMENT_SHADER, fsSrc);
const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
// Shader werden nach dem Linken nicht mehr benötigt
gl.detachShader(program, vs);
gl.detachShader(program, fs);
gl.deleteShader(vs);
gl.deleteShader(fs);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const log = gl.getProgramInfoLog(program);
gl.deleteProgram(program);
throw new Error(`Link-Fehler: ${log}`);
}
return program;
};
const program = createProgram(gl, vsSource, fsSource);
console.log(program instanceof WebGLProgram);
Programm verwenden: Attribute, Uniforms und Zeichnen
// Fortsetzung: program aus dem vorigen Beispiel
const aPosition = gl.getAttribLocation(program, 'aPosition');
const uColor = gl.getUniformLocation(program, 'uColor');
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([0, 0.8, -0.8, -0.8, 0.8, -0.8]),
gl.STATIC_DRAW
);
gl.useProgram(program);
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.uniform4f(uColor, 1.0, 0.4, 0.0, 1.0);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
Programm-Infos abfragen und aufräumen
const attribCount = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
const uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
console.log(`Attribute: ${attribCount}, Uniforms: ${uniformCount}`);
for (let i = 0; i < uniformCount; i++) {
const info = gl.getActiveUniform(program, i);
console.log(info.name, info.type === gl.FLOAT_VEC4 ? 'vec4' : info.type);
}
// Beim Beenden GPU-Ressourcen freigeben
gl.useProgram(null);
gl.deleteProgram(program);
console.log(gl.isProgram(program));
// Wichtig · Fallstricke
- Link-Status immer prüfen:
gl.linkProgram()wirft keine Exception. Fehler (z. B. nicht passendevarying-Variablen zwischen Vertex- und Fragment-Shader) erkennt man nur übergl.getProgramParameter(program, gl.LINK_STATUS)undgl.getProgramInfoLog(). - Performance:
gl.getProgramParameter()undgetError()können die GPU-Pipeline synchronisieren. Nicht pro Frame aufrufen. In Browsern mit der ErweiterungKHR_parallel_shader_compilekann man mitCOMPLETION_STATUS_KHRohne Blockieren abfragen, ob das Linken fertig ist. - Optimierte Variablen: Attribute oder Uniforms, die im Shader nicht verwendet werden, entfernt der Compiler.
getAttribLocation()gibt dann-1zurück undgetUniformLocation()null. - Kontextverlust: Bei
webglcontextlostwerden alle Programme ungültig. Nachwebglcontextrestoredmuss man sie neu erstellen. - Kontextbindung: Ein
WebGLProgramgehört zu genau einem Rendering-Kontext und kann nicht mit einem anderen Kontext verwendet werden. - GLSL-Versionen: WebGL 1 verwendet GLSL ES 1.00, WebGL 2 zusätzlich GLSL ES 3.00 (
#version 300 esmuss dann in der ersten Zeile stehen). Beide Shader eines Programms müssen dieselbe Version nutzen. - Kompatibilität: WebGL 1 wird von allen modernen Browsern unterstützt, WebGL 2 inzwischen ebenfalls (Safari ab Version 15). Auch in Web Workern mit
OffscreenCanvasverfügbar.