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

WebGL2RenderingContext.bindTransformFeedback()

Methode

Bindet ein <code>WebGLTransformFeedback</code>-Objekt an das Ziel <code>gl.TRANSFORM_FEEDBACK</code>, damit Vertex-Shader-Ausgaben in Puffer geschrieben werden können.

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

Signatur

WebGL2RenderingContext.prototype.bindTransformFeedback(target, transformFeedback)

Beschreibung

Mit bindTransformFeedback() machst du ein WebGLTransformFeedback-Objekt zum aktiven Transform-Feedback-Objekt des WebGL-2-Kontexts. Das Objekt speichert, welche Puffer an die indizierten Bindungspunkte von gl.TRANSFORM_FEEDBACK_BUFFER gebunden sind und ob Transform Feedback gerade läuft oder pausiert ist.

Transform Feedback wird vor allem für GPU-seitige Berechnungen genutzt, etwa Partikelsysteme, Physiksimulationen oder das Vorberechnen von Geometrie. Die Ausgabevariablen (out) des Vertex-Shaders werden dabei direkt in Puffer geschrieben, ohne dass Daten über die CPU laufen müssen. Häufig arbeitet man mit zwei Puffern im „Ping-Pong“-Verfahren und je einem Transform-Feedback-Objekt pro Richtung.

Der typische Ablauf lautet: Objekt mit createTransformFeedback() erzeugen, mit bindTransformFeedback() binden, Puffer per bindBufferBase() anhängen und danach zwischen beginTransformFeedback() und endTransformFeedback() zeichnen. Übergibst du null, wird wieder das Standard-Transform-Feedback-Objekt des Kontexts gebunden.

Parameter

Name Typ Default Beschreibung
$target Pflicht GLenum Bindungsziel. Muss gl.TRANSFORM_FEEDBACK sein.
$transformFeedback Pflicht WebGLTransformFeedback | null Das zu bindende Objekt aus gl.createTransformFeedback(). Bei null wird das Standardobjekt gebunden.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert (undefined). Fehler meldest du über gl.getError().

Beispiele

Transform-Feedback-Objekt erzeugen und Ausgabepuffer anhängen

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

// Ausgabepuffer für 100 vec2-Werte anlegen
const outBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, outBuffer);
gl.bufferData(gl.ARRAY_BUFFER, 100 * 2 * 4, gl.DYNAMIC_COPY);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, outBuffer);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);

console.log(gl.isTransformFeedback(tf));
true

Vertex-Shader-Ausgaben auf der GPU berechnen und auslesen

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

const vsSource = `#version 300 es
in float inValue;
out float outValue;
void main() { outValue = inValue * 2.0; }`;
const fsSource = `#version 300 es
precision mediump float;
out vec4 color;
void main() { color = vec4(0.0); }`;

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));
// Varyings VOR dem Linken festlegen
gl.transformFeedbackVaryings(program, ['outValue'], gl.SEPARATE_ATTRIBS);
gl.linkProgram(program);
gl.useProgram(program);

const input = new Float32Array([1, 2, 3, 4]);
const inBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, inBuf);
gl.bufferData(gl.ARRAY_BUFFER, input, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(program, 'inValue');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 1, gl.FLOAT, false, 0, 0);

const outBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, outBuf);
gl.bufferData(gl.ARRAY_BUFFER, input.byteLength, gl.STATIC_READ);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, outBuf);

gl.enable(gl.RASTERIZER_DISCARD); // nichts rastern
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, input.length);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);

gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);

const result = new Float32Array(input.length);
gl.bindBuffer(gl.ARRAY_BUFFER, outBuf);
gl.getBufferSubData(gl.ARRAY_BUFFER, 0, result);
console.log(Array.from(result));
[2, 4, 6, 8]

Ping-Pong zwischen zwei Transform-Feedback-Objekten

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

const makeBuffer = () => {
  const b = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, b);
  gl.bufferData(gl.ARRAY_BUFFER, 1024 * 4, gl.DYNAMIC_COPY);
  return b;
};
gl.bindBuffer(gl.ARRAY_BUFFER, null);

const buffers = [makeBuffer(), makeBuffer()];
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// Je ein TF-Objekt, das in den jeweils anderen Puffer schreibt
const feedbacks = buffers.map((_, i) => {
  const tf = gl.createTransformFeedback();
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
  gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffers[1 - i]);
  return tf;
});
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);

let current = 0;
const step = () => {
  // Lesen aus buffers[current], schreiben via feedbacks[current]
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, feedbacks[current]);
  // ... Attribute auf buffers[current] setzen, begin/draw/end ...
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
  current = 1 - current;
};
step();
console.log('Nächster Lesepuffer:', current);
Nächster Lesepuffer: 1

// Wichtig · Fallstricke

  • Ist Transform Feedback gerade aktiv und nicht pausiert, erzeugt ein erneutes Binden den Fehler gl.INVALID_OPERATION. Erst endTransformFeedback() oder pauseTransformFeedback() aufrufen.
  • Ein anderes target als gl.TRANSFORM_FEEDBACK führt zu gl.INVALID_ENUM.
  • Ein Puffer darf nicht gleichzeitig als Transform-Feedback-Ziel und als Vertex-Attributquelle gebunden sein, sonst gibt es beim Zeichnen INVALID_OPERATION. Nach der Nutzung den Puffer mit bindBufferBase(..., null) lösen.
  • transformFeedbackVaryings() muss vor linkProgram() aufgerufen werden, sonst landen keine Werte im Puffer.
  • Die Methode gibt es nur im WebGL-2-Kontext (getContext('webgl2')), nicht in WebGL 1. Alle modernen Browser unterstützen WebGL 2, Safari erst ab Version 15.
  • getBufferSubData() blockiert die CPU, bis die GPU fertig ist. Für flüssige Animationen besser mit fenceSync() arbeiten oder die Daten auf der GPU lassen.