Start · Sprachen · JavaScript · Referenz · WebGLProgram

WebGLProgram

Schnittstelle

Ein <code>WebGLProgram</code> verknüpft einen kompilierten Vertex- und Fragment-Shader (GLSL) zu einem ausführbaren GPU-Programm für WebGL-Zeichenaufrufe.

seit JavaScript WebGL 1.0 – Baseline: alle modernen Browser Kategorie: webapi

Signatur

interface WebGLProgram extends WebGLObject

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 Programm
  • gl.attachShader(program, shader) – hängt Vertex- und Fragment-Shader an
  • gl.linkProgram(program) – linkt die Shader zu einem ausführbaren Programm
  • gl.getProgramParameter(program, gl.LINK_STATUS) und gl.getProgramInfoLog(program) – prüfen den Link-Status bzw. liefern Fehlermeldungen
  • gl.getAttribLocation() / gl.getUniformLocation() – ermitteln die Positionen von Attributen und Uniforms
  • gl.useProgram(program) und gl.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

Typ
WebGLProgram
Beschreibung
Wird von 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);
true

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);
Ein orangefarbenes Dreieck wird auf schwarzem Hintergrund gezeichnet.

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));
Attribute: 1, Uniforms: 1 uColor vec4 false

// Wichtig · Fallstricke

  • Link-Status immer prüfen: gl.linkProgram() wirft keine Exception. Fehler (z. B. nicht passende varying-Variablen zwischen Vertex- und Fragment-Shader) erkennt man nur über gl.getProgramParameter(program, gl.LINK_STATUS) und gl.getProgramInfoLog().
  • Performance: gl.getProgramParameter() und getError() können die GPU-Pipeline synchronisieren. Nicht pro Frame aufrufen. In Browsern mit der Erweiterung KHR_parallel_shader_compile kann man mit COMPLETION_STATUS_KHR ohne Blockieren abfragen, ob das Linken fertig ist.
  • Optimierte Variablen: Attribute oder Uniforms, die im Shader nicht verwendet werden, entfernt der Compiler. getAttribLocation() gibt dann -1 zurück und getUniformLocation() null.
  • Kontextverlust: Bei webglcontextlost werden alle Programme ungültig. Nach webglcontextrestored muss man sie neu erstellen.
  • Kontextbindung: Ein WebGLProgram gehö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 es muss 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 OffscreenCanvas verfügbar.