Start · Sprachen · JavaScript · Referenz · OES_element_index_uint extension

OES_element_index_uint extension

Schnittstelle

WebGL-1-Erweiterung, die <code>gl.UNSIGNED_INT</code> als Indextyp in <code>drawElements()</code> erlaubt und damit Meshes mit mehr als 65.536 Vertices ermöglicht.

seit JavaScript WebGL 1 (Erweiterung); in WebGL 2 standardmäßig en Kategorie: webapi

Signatur

interface OES_element_index_uint

Beschreibung

Die Erweiterung OES_element_index_uint gehört zur WebGL API. Sie erlaubt den Typ gl.UNSIGNED_INT (32-Bit-Indizes) beim indizierten Zeichnen mit WebGLRenderingContext.drawElements(). Ohne diese Erweiterung sind in WebGL 1 nur gl.UNSIGNED_BYTE und gl.UNSIGNED_SHORT zulässig. Ein einzelner Draw-Call kann dann höchstens 65.536 verschiedene Vertices adressieren.

Man braucht sie vor allem für große Geometrien: hochaufgelöste Terrain-Meshes, eingescannte 3D-Modelle, Punktwolken mit Dreiecksnetz oder CAD-Daten. Ohne die Erweiterung müsste man solche Modelle in mehrere Teil-Meshes aufteilen.

Die Schnittstelle hat keine eigenen Methoden, Eigenschaften oder Konstanten. Es reicht, sie mit gl.getExtension("OES_element_index_uint") zu aktivieren. Danach ist die bereits vorhandene Konstante gl.UNSIGNED_INT (0x1405) als type-Argument in drawElements() gültig. Die Indexdaten übergibt man als Uint32Array an einen ELEMENT_ARRAY_BUFFER.

In WebGL 2 (WebGL2RenderingContext) ist diese Funktion fest eingebaut. Dort gibt es die Erweiterung nicht, und getExtension() liefert für diesen Namen null.

Parameter

Name Typ Default Beschreibung
$name Pflicht string Argument von gl.getExtension(): exakt "OES_element_index_uint".

Rückgabewert

Typ
OES_element_index_uint | null
Beschreibung
gl.getExtension("OES_element_index_uint") gibt ein (leeres) Erweiterungsobjekt zurück. Ist die Erweiterung nicht verfügbar, etwa in einem WebGL-2-Kontext, lautet der Rückgabewert null.

Beispiele

Erweiterung aktivieren und Unterstützung prüfen

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

const ext = gl.getExtension("OES_element_index_uint");
if (ext) {
  console.log("32-Bit-Indizes verfügbar");
} else {
  console.log("Nur 16-Bit-Indizes – Mesh muss aufgeteilt werden");
}
32-Bit-Indizes verfügbar

Großes Gitter-Mesh mit Uint32Array-Indizes zeichnen

const gl = document.querySelector("canvas").getContext("webgl");
const hasUint = !!gl.getExtension("OES_element_index_uint");

// Gitter mit 400 × 400 = 160.000 Vertices (> 65.536)
const size = 400;
const positions = new Float32Array(size * size * 2);
for (let y = 0; y < size; y++) {
  for (let x = 0; x < size; x++) {
    const i = (y * size + x) * 2;
    positions[i] = (x / (size - 1)) * 2 - 1;
    positions[i + 1] = (y / (size - 1)) * 2 - 1;
  }
}

const indices = [];
for (let y = 0; y < size - 1; y++) {
  for (let x = 0; x < size - 1; x++) {
    const a = y * size + x;
    const b = a + 1;
    const c = a + size;
    const d = c + 1;
    indices.push(a, b, c, b, d, c);
  }
}

const vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, "attribute vec2 p; void main(){ gl_Position = vec4(p, 0.0, 1.0); }");
gl.compileShader(vs);
const fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, "precision mediump float; void main(){ gl_FragColor = vec4(0.2, 0.6, 1.0, 1.0); }");
gl.compileShader(fs);
const prog = gl.createProgram();
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
gl.linkProgram(prog);
gl.useProgram(prog);

const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(prog, "p");
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);

if (hasUint) {
  const ibo = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), gl.STATIC_DRAW);
  gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0);
  console.log("Gezeichnet:", indices.length / 3, "Dreiecke");
}
Gezeichnet: 318402 Dreiecke

Passenden Indextyp für WebGL 1 und WebGL 2 wählen

const getIndexSetup = (gl, vertexCount) => {
  const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" &&
    gl instanceof WebGL2RenderingContext;
  const uintOk = isWebGL2 || !!gl.getExtension("OES_element_index_uint");

  if (vertexCount <= 65536) {
    return { ArrayType: Uint16Array, glType: gl.UNSIGNED_SHORT };
  }
  if (uintOk) {
    return { ArrayType: Uint32Array, glType: gl.UNSIGNED_INT };
  }
  throw new Error("Mesh zu groß für 16-Bit-Indizes – bitte aufteilen");
};

const gl = document.createElement("canvas").getContext("webgl");
const { ArrayType, glType } = getIndexSetup(gl, 100000);
console.log(ArrayType.name, glType === gl.UNSIGNED_INT);
Uint32Array true

// Wichtig · Fallstricke

  • WebGL 2: Hier ist gl.UNSIGNED_INT immer erlaubt. getExtension("OES_element_index_uint") liefert null. Ein null bedeutet dort also nicht, dass die Funktion fehlt.
  • Aktivierung ist Pflicht: Rufst du in WebGL 1 drawElements(..., gl.UNSIGNED_INT, ...) auf, ohne vorher getExtension() aufzurufen, erzeugt das den Fehler gl.INVALID_ENUM und es wird nichts gezeichnet. Das Vorhandensein der Konstante gl.UNSIGNED_INT sagt nichts über die Unterstützung aus.
  • Offset-Ausrichtung: Der offset-Parameter von drawElements() muss bei UNSIGNED_INT ein Vielfaches von 4 sein, sonst gibt es INVALID_OPERATION.
  • Speicher und Leistung: 32-Bit-Indizes brauchen doppelt so viel Speicher wie 16-Bit-Indizes. Nutze sie deshalb nur, wenn ein Mesh wirklich mehr als 65.536 Vertices hat.
  • Kompatibilität: Die Erweiterung wird von praktisch allen Desktop- und Mobilbrowsern mit WebGL 1 unterstützt. Auf sehr alten mobilen GPUs kann sie fehlen, deshalb immer auf null prüfen.
  • Nach einem Kontextverlust (webglcontextlost) muss die Erweiterung neu angefordert werden.