Start · Sprachen · JavaScript · Referenz · WebGL2RenderingContext.getUniformIndices()

WebGL2RenderingContext.getUniformIndices()

Methode

Liefert zu einem Array von Uniform-Namen die zugehörigen Uniform-Indizes innerhalb eines gelinkten <code>WebGLProgram</code>.

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

Signatur

WebGL2RenderingContext.prototype.getUniformIndices(program, uniformNames)

Beschreibung

getUniformIndices() ordnet Uniform-Namen ihren aktiven Uniform-Indizes in einem gelinkten Shader-Programm zu. Diese Indizes sind nicht dasselbe wie die Locations von getUniformLocation(). Man braucht sie für die Abfrage-Methoden, die mit Indizes arbeiten, vor allem getActiveUniforms() und getActiveUniform().

Am häufigsten kommt die Methode bei Uniform Buffer Objects (UBOs) zum Einsatz. Wenn ein Uniform-Block das Layout shared oder packed nutzt, kennt man die Byte-Offsets der einzelnen Mitglieder nicht im Voraus. Dann holt man zuerst mit getUniformIndices() die Indizes und fragt danach mit getActiveUniforms(program, indices, gl.UNIFORM_OFFSET) die Offsets ab. So lässt sich der Puffer passend befüllen.

Namen, die im Programm nicht als aktive Uniforms vorkommen, bekommen den Wert gl.INVALID_INDEX (0xFFFFFFFF). Das passiert auch, wenn der Shader-Compiler eine unbenutzte Uniform wegoptimiert hat. Bei Arrays und Struct-Mitgliedern muss man den vollständigen Namen angeben, z. B. "lights[0].color" oder "weights[0]".

Parameter

Name Typ Default Beschreibung
$program Pflicht WebGLProgram Ein erfolgreich gelinktes Shader-Programm.
$uniformNames Pflicht Array<string> Array mit den Namen der Uniforms, deren Indizes abgefragt werden sollen. Bei Block-Mitgliedern ohne Instanznamen reicht der Mitgliedsname, mit Instanznamen gilt Blockname.mitglied.

Rückgabewert

Typ
Array<number> | null
Beschreibung
Ein Array mit den Uniform-Indizes (GLuint) in derselben Reihenfolge wie uniformNames. Unbekannte oder inaktive Namen ergeben gl.INVALID_INDEX. Bei einem Fehler, z. B. einem ungültigen Programm, wird null zurückgegeben.

Beispiele

Indizes einfacher Uniforms abfragen

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

const vsSource = `#version 300 es
uniform mat4 uModel;
uniform float uScale;
in vec4 aPos;
void main() { gl_Position = uModel * aPos * uScale; }`;
const fsSource = `#version 300 es
precision highp float;
uniform vec4 uColor;
out vec4 outColor;
void main() { outColor = uColor; }`;

const compile = (type, src) => {
  const s = gl.createShader(type);
  gl.shaderSource(s, src);
  gl.compileShader(s);
  return s;
};

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

const indices = gl.getUniformIndices(program, ['uModel', 'uColor', 'uGibtEsNicht']);
console.log(indices.map(i => i === gl.INVALID_INDEX ? 'INVALID' : i));
z. B. [1, 0, "INVALID"] (Reihenfolge der Indizes treiberabhängig)

Offsets in einem Uniform Buffer Object ermitteln

// Annahme: gl und ein gelinktes program mit folgendem Block:
// layout(std140) uniform Material { vec4 diffuse; vec4 specular; float shininess; };
const names = ['diffuse', 'specular', 'shininess'];
const indices = gl.getUniformIndices(program, names);

if (indices.includes(gl.INVALID_INDEX)) {
  throw new Error('Mindestens eine Uniform ist nicht aktiv');
}

const offsets = gl.getActiveUniforms(program, indices, gl.UNIFORM_OFFSET);
const blockIndex = gl.getUniformBlockIndex(program, 'Material');
const blockSize = gl.getActiveUniformBlockParameter(
  program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE
);

const data = new Float32Array(blockSize / 4);
data.set([1, 0, 0, 1], offsets[0] / 4);   // diffuse
data.set([1, 1, 1, 1], offsets[1] / 4);   // specular
data[offsets[2] / 4] = 32;                // shininess

const ubo = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
gl.bufferData(gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW);
gl.uniformBlockBinding(program, blockIndex, 0);
gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, ubo);

console.log(names.map((n, i) => `${n}: ${offsets[i]}`));
["diffuse: 0", "specular: 16", "shininess: 32"]

Typ und Größe über die Indizes auslesen

const indices = gl.getUniformIndices(program, ['uModel', 'uColor']);
const types = gl.getActiveUniforms(program, indices, gl.UNIFORM_TYPE);
const sizes = gl.getActiveUniforms(program, indices, gl.UNIFORM_SIZE);

console.log(types[0] === gl.FLOAT_MAT4, types[1] === gl.FLOAT_VEC4, sizes);
true true [1, 1]

// Wichtig · Fallstricke

  • Index ist nicht gleich Location: Die zurückgegebenen Indizes kann man nicht an gl.uniform*() übergeben. Für diese Aufrufe braucht man getUniformLocation().
  • Prüfung auf INVALID_INDEX: Wegoptimierte oder falsch geschriebene Uniforms liefern gl.INVALID_INDEX (4294967295). Das wirft keinen Fehler. Prüft man den Wert nicht, führt er später zu INVALID_VALUE in getActiveUniforms().
  • Programm muss gelinkt sein: Vor dem Aufruf mit gl.getProgramParameter(program, gl.LINK_STATUS) prüfen.
  • Performance: Die Abfrage erfordert unter Umständen eine Synchronisation mit der GPU bzw. dem Treiber. Deshalb die Indizes einmal nach dem Linken ermitteln und zwischenspeichern, nicht in jedem Frame neu abfragen.
  • Layout std140: Hier sind die Offsets per Spezifikation festgelegt. Die Abfrage ist trotzdem ein sinnvoller Plausibilitätstest.
  • Kompatibilität: Nur im webgl2-Kontext verfügbar, nicht in WebGL 1. Wird von allen aktuellen Browsern unterstützt (Chrome, Edge, Firefox, Safari ab 15).