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

WebGL2RenderingContext.createTransformFeedback()

Methode

Erstellt ein neues <code>WebGLTransformFeedback</code>-Objekt. Damit lassen sich Ausgaben des Vertex-Shaders direkt in Puffer auf der GPU zurückschreiben.

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

Signatur

WebGL2RenderingContext.prototype.createTransformFeedback()

Beschreibung

createTransformFeedback() erzeugt ein leeres WebGLTransformFeedback-Objekt. Es speichert, welche Puffer als Ziel für die Transform-Feedback-Ausgabe gebunden sind. Mit Transform Feedback schreibt die GPU die out-Variablen (Varyings) des Vertex-Shaders in einen oder mehrere Buffer, statt sie nur an den Rasterizer weiterzugeben. Die Daten bleiben dabei auf der Grafikkarte. Das eignet sich besonders für GPU-Partikelsysteme, Physiksimulationen oder mehrstufige Geometrieberechnungen.

Der typische Ablauf sieht so aus:

  • Vor dem Linken des Programms mit gl.transformFeedbackVaryings() festlegen, welche Varyings aufgezeichnet werden.
  • Das Objekt mit createTransformFeedback() erzeugen und mit gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf) binden.
  • Die Zielpuffer mit gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, index, buffer) anhängen.
  • Die Aufzeichnung mit gl.beginTransformFeedback(primitiveMode) starten, zeichnen und mit gl.endTransformFeedback() beenden.

Das Objekt selbst enthält keine Daten, es merkt sich nur die gebundenen Puffer. Wenn du mehrere Objekte anlegst, z. B. eines pro Ping-Pong-Puffer, kannst du beim Zeichnen schnell zwischen den Zielkonfigurationen wechseln.

Rückgabewert

Typ
WebGLTransformFeedback | null
Beschreibung
Ein neues WebGLTransformFeedback-Objekt. Ist der Kontext verloren gegangen (context lost), wird null zurückgegeben.

Beispiele

Transform-Feedback-Objekt erzeugen und binden

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('WebGL 2 wird nicht unterstützt');

const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);

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

Vertex-Shader-Ausgabe in einen Puffer schreiben und zurücklesen

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

const vsSource = `#version 300 es
in float inValue;
out float outValue;
void main() {
  outValue = inValue * 2.0; // Verdoppeln auf der GPU
}`;
const fsSource = `#version 300 es
precision highp 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));
// WICHTIG: vor linkProgram 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); // keine Pixel zeichnen
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, input.length);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);

gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);

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

Ping-Pong für Partikel mit zwei Feedback-Objekten

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

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

// Zwei Zustände: aus A lesen, in B schreiben, danach tauschen
const states = [makeBuffer(), makeBuffer()].map((buffer) => {
  const tf = gl.createTransformFeedback();
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
  gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer);
  return { buffer, tf };
});
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);

let current = 0;
const step = () => {
  const src = states[current];
  const dst = states[1 - current];
  // ... src.buffer als Attribut binden, dann:
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, dst.tf);
  // gl.beginTransformFeedback(gl.POINTS); gl.drawArrays(...); gl.endTransformFeedback();
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
  current = 1 - current;
};

step();
console.log('Aktiver Zustand:', current);

// Aufräumen
states.forEach(({ buffer, tf }) => {
  gl.deleteTransformFeedback(tf);
  gl.deleteBuffer(buffer);
});
Aktiver Zustand: 1