Signatur
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
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));
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));
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);
// Wichtig · Fallstricke
- Ist Transform Feedback gerade aktiv und nicht pausiert, erzeugt ein erneutes Binden den Fehler
gl.INVALID_OPERATION. ErstendTransformFeedback()oderpauseTransformFeedback()aufrufen. - Ein anderes
targetalsgl.TRANSFORM_FEEDBACKführt zugl.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 mitbindBufferBase(..., null)lösen. transformFeedbackVaryings()muss vorlinkProgram()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 mitfenceSync()arbeiten oder die Daten auf der GPU lassen.