Start · Sprachen · JavaScript · Referenz · WebGLShader

WebGLShader

Schnittstelle

Ein <code>WebGLShader</code> ist ein undurchsichtiges Handle auf ein GPU-Shader-Objekt, entweder ein Vertex- oder ein Fragment-Shader in GLSL.

seit JavaScript Baseline: alle Browser (WebGL 1.0, auch in WebGL 2 Kategorie: webapi

Signatur

interface WebGLShader extends WebGLObject

Beschreibung

WebGLShader ist ein Teil der WebGL-API und steht für ein einzelnes Shader-Objekt auf der GPU. Das kann entweder ein Vertex-Shader (gl.VERTEX_SHADER) sein, der die Position jedes Eckpunkts berechnet, oder ein Fragment-Shader (gl.FRAGMENT_SHADER), der die Farbe jedes Pixels festlegt. Das Objekt selbst hat weder eigene Eigenschaften noch eigene Methoden. Es ist nur ein undurchsichtiges Handle, das du an Methoden von WebGLRenderingContext bzw. WebGL2RenderingContext übergibst.

Einen Konstruktor gibt es nicht. Instanzen erzeugst du mit gl.createShader(type). Danach übergibst du mit gl.shaderSource() den GLSL-Quelltext und übersetzt ihn mit gl.compileShader(). Ob das Kompilieren geklappt hat, prüfst du mit gl.getShaderParameter(shader, gl.COMPILE_STATUS). Fehlermeldungen bekommst du über gl.getShaderInfoLog().

Zum Zeichnen brauchst du immer genau einen Vertex- und einen Fragment-Shader. Beide hängst du mit gl.attachShader() an ein WebGLProgram an und verknüpfst sie mit gl.linkProgram(). Nach dem erfolgreichen Linken kannst du die Shader-Objekte mit gl.detachShader() und gl.deleteShader() freigeben. Das Programm behält den übersetzten Code.

Parameter

Name Typ Default Beschreibung
$type Pflicht GLenum Parameter von gl.createShader(type), mit dem ein WebGLShader erzeugt wird: gl.VERTEX_SHADER oder gl.FRAGMENT_SHADER.

Rückgabewert

Typ
WebGLShader | null
Beschreibung
gl.createShader() liefert ein neues WebGLShader-Objekt. Bei einem ungültigen Typ oder einem verlorenen Kontext kommt null zurück.

Beispiele

Shader erzeugen und kompilieren (mit Fehlerprüfung)

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');

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 vsSource = `
  attribute vec2 aPosition;
  void main() {
    gl_Position = vec4(aPosition, 0.0, 1.0);
  }
`;

const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vsSource);
console.log(vertexShader instanceof WebGLShader);
true

Vertex- und Fragment-Shader zu einem Programm verknüpfen

const gl = document.createElement('canvas').getContext('webgl');

const compile = (type, src) => {
  const s = gl.createShader(type);
  gl.shaderSource(s, src);
  gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(s));
  }
  return s;
};

const vs = compile(gl.VERTEX_SHADER, `
  attribute vec2 aPosition;
  void main() { gl_Position = vec4(aPosition, 0.0, 1.0); }
`);
const fs = compile(gl.FRAGMENT_SHADER, `
  precision mediump float;
  void main() { gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0); }
`);

const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);

if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(program));
}

// Shader werden nach dem Linken nicht mehr gebraucht
gl.detachShader(program, vs);
gl.detachShader(program, fs);
gl.deleteShader(vs);
gl.deleteShader(fs);

gl.useProgram(program);
console.log('Programm bereit');
Programm bereit

Kompilierfehler auslesen

const gl = document.createElement('canvas').getContext('webgl');
const shader = gl.createShader(gl.FRAGMENT_SHADER);
// Absichtlicher Fehler: fehlendes Semikolon und keine Präzision
gl.shaderSource(shader, 'void main() { gl_FragColor = vec4(1.0) }');
gl.compileShader(shader);

console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS));
console.log(gl.getShaderInfoLog(shader));
console.log(gl.getShaderParameter(shader, gl.SHADER_TYPE) === gl.FRAGMENT_SHADER);
gl.deleteShader(shader);
false ERROR: 0:1: '}' : syntax error (Wortlaut hängt vom Browser/Treiber ab) true

// Wichtig · Fallstricke

  • Kein Konstruktor: new WebGLShader() wirft einen TypeError. Verwende immer gl.createShader().
  • Fehler kommen still: gl.compileShader() wirft bei Fehlern keine Ausnahme. Prüfe deshalb immer COMPILE_STATUS. Der Text von getShaderInfoLog() sieht je nach Browser und Treiber anders aus.
  • Präzision: Im Fragment-Shader von GLSL ES 1.00 ist eine Präzisionsangabe wie precision mediump float; Pflicht, sonst schlägt das Kompilieren fehl.
  • GLSL-Version: WebGL 2 unterstützt GLSL ES 3.00. Dann muss #version 300 es in der allerersten Zeile stehen, auch Leerzeilen davor sind nicht erlaubt. Statt attribute/varying/gl_FragColor nutzt du dort in/out.
  • Kontextverlust: Nach dem Ereignis webglcontextlost sind alle Shader-Objekte ungültig und müssen nach webglcontextrestored neu erzeugt werden.
  • Leistung: Kompilieren und Linken sind teuer. Übersetze Shader einmal beim Start und nicht in jedem Frame. Mit der Erweiterung KHR_parallel_shader_compile lässt sich das Kompilieren nicht blockierend abfragen.
  • Sicherheit: Übernimm keinen GLSL-Code ungeprüft aus Benutzereingaben. Fehlerhafte Shader können die GPU stark belasten oder den Kontext zum Absturz bringen.
  • Unterstützt von allen aktuellen Browsern. Auch in Web Workers verfügbar, zusammen mit OffscreenCanvas.