Start · Sprachen · JavaScript · Referenz · WebGLRenderingContext.getUniformLocation()

WebGLRenderingContext.getUniformLocation()

Methode

Gibt ein <code>WebGLUniformLocation</code>-Objekt für eine benannte uniform-Variable in einem gelinkten <code>WebGLProgram</code> zurück, oder <code>null</code>, wenn es sie nicht gibt.

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

Signatur

WebGLRenderingContext.prototype.getUniformLocation(program, name)

Beschreibung

Mit getUniformLocation() holst du dir einen „Griff“ auf eine uniform-Variable eines Shader-Programms. Uniforms sind Werte, die für alle Vertices bzw. Fragmente eines Zeichenaufrufs gleich bleiben, zum Beispiel Transformationsmatrizen, Farben, die Zeit für Animationen oder Textureinheiten für sampler2D.

Das zurückgegebene WebGLUniformLocation-Objekt übergibst du anschließend an die uniform*()-Methoden wie uniform1f(), uniform4fv() oder uniformMatrix4fv(), um den Wert zu setzen. Diese Methoden wirken immer auf das Programm, das gerade mit useProgram() aktiv ist.

Du solltest die Location einmal nach dem Linken des Programms abfragen und zwischenspeichern, nicht bei jedem Frame. Der Aufruf geht über Zeichenketten und kann den Treiber ansprechen, darum ist er vergleichsweise teuer. Elemente von Arrays und Struct-Felder sprichst du über Namen wie "uLights[2].color" oder "uWeights[0]" an.

Parameter

Name Typ Default Beschreibung
$program Pflicht WebGLProgram Das gelinkte Shader-Programm (nach linkProgram()), in dem die uniform-Variable gesucht wird.
$name Pflicht string Name der uniform-Variable, genau so wie im GLSL-Code (Groß-/Kleinschreibung zählt). Für Arrays und Structs sind Namen wie "uArr[1]" oder "uLight.pos" erlaubt. Namen mit dem Präfix gl_ oder webgl_ sind nicht zulässig.

Rückgabewert

Typ
WebGLUniformLocation | null
Beschreibung
Ein undurchsichtiges WebGLUniformLocation-Objekt, oder null, wenn die Variable nicht existiert, vom Shader-Compiler als unbenutzt entfernt wurde oder der Name ungültig ist.

Beispiele

Farbe und Matrix eines Shaders setzen

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

const vsSource = `
  attribute vec2 aPosition;
  uniform mat4 uMatrix;
  void main() { gl_Position = uMatrix * vec4(aPosition, 0.0, 1.0); }
`;
const fsSource = `
  precision mediump float;
  uniform vec4 uColor;
  void main() { gl_FragColor = uColor; }
`;

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

const program = gl.createProgram();
gl.attachShader(program, compile(gl.VERTEX_SHADER, vsSource));
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, fsSource));
gl.linkProgram(program);

// Locations einmal nach dem Linken abfragen
const uColor = gl.getUniformLocation(program, 'uColor');
const uMatrix = gl.getUniformLocation(program, 'uMatrix');

gl.useProgram(program); // uniform*() wirkt auf das aktive Programm
gl.uniform4f(uColor, 1.0, 0.4, 0.0, 1.0);
gl.uniformMatrix4fv(uMatrix, false, new Float32Array([
  1, 0, 0, 0,
  0, 1, 0, 0,
  0, 0, 1, 0,
  0, 0, 0, 1,
]));
console.log(uColor instanceof WebGLUniformLocation);
true

Alle Uniforms automatisch cachen

// Legt eine Map Name -> Location für alle aktiven Uniforms an
const getUniformLocations = (gl, program) => {
  const locations = {};
  const count = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
  for (let i = 0; i < count; i++) {
    const info = gl.getActiveUniform(program, i);
    // Arrays werden als "name[0]" gemeldet, das Suffix entfernen
    const name = info.name.replace(/\[0\]$/, '');
    locations[name] = gl.getUniformLocation(program, info.name);
  }
  return locations;
};

const uniforms = getUniformLocations(gl, program);
console.log(Object.keys(uniforms));
["uMatrix", "uColor"]

Zeit-Uniform animieren und null abfangen

const uTime = gl.getUniformLocation(program, 'uTime');
if (uTime === null) {
  console.warn('uTime nicht gefunden – falsch geschrieben oder vom Compiler entfernt?');
}

const render = (t) => {
  gl.useProgram(program);
  // uniform1f mit null ist erlaubt und bewirkt einfach nichts
  gl.uniform1f(uTime, t / 1000);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  requestAnimationFrame(render);
};
requestAnimationFrame(render);
uTime nicht gefunden – falsch geschrieben oder vom Compiler entfernt? (wenn der Shader uTime nicht verwendet)

// Wichtig · Fallstricke

  • Unbenutzte Uniforms werden entfernt: Wenn der GLSL-Compiler eine deklarierte Uniform nicht braucht, optimiert er sie weg. Dann liefert der Aufruf null, obwohl die Variable im Quelltext steht. Beim Debuggen ist das eine häufige Ursache für Verwirrung.
  • Wenn du null an uniform*() übergibst, passiert still nichts. Du bekommst also keinen Fehler, falls ein Name falsch geschrieben ist.
  • Die Location gilt nur für genau das Programm, für das sie abgefragt wurde. Linkst du das Programm neu (linkProgram()), werden alle alten Locations ungültig. Benutzt du sie mit einem anderen Programm, meldet WebGL INVALID_OPERATION.
  • Ist das Programm nicht erfolgreich gelinkt, entsteht INVALID_OPERATION. Prüfe das vorher mit getProgramParameter(program, gl.LINK_STATUS).
  • Samplern (sampler2D) weist du mit uniform1i() die Nummer der Textureinheit zu, nicht das Texturobjekt selbst.
  • Nach einem Kontextverlust (webglcontextlost) sind alle Locations ungültig und müssen neu abgefragt werden.
  • Die Methode gibt es auch in WebGL2RenderingContext. In WebGL 2 lassen sich viele Uniforms zusätzlich in Uniform-Blöcken zusammenfassen (getUniformBlockIndex()).