I try to follow example from webgl2fundamentals regarding transform feedback.
My goal is to create animation of vertex positions in a way that there are two programs:
I made this code snippet on codesandbox.
It seems that varying vPosition
from first program is not being feed to the second program. What am I doing wrong?
Is setup enough to handle this use case? I have one TFO and one VAO and one buffer. What would be best approach?
The output should look something like this:
here is the code:
const canvas = document.querySelector("canvas");
canvas.width = 500;
canvas.height = 500;
const gl = canvas.getContext("webgl2");
const genPointsVSGLSL = `#version 300 es
uniform float time;
in vec2 position;
out vec2 vPosition;
void main() {
vPosition = vec2(position * (sin(time) + 1.0));
}
`;
const genPointsFSGLSL = `#version 300 es
void main() {
discard;
}
`;
const drawVSGLSL = `#version 300 es
uniform float time;
in vec2 vPosition;
void main() {
gl_PointSize = 20.0;
gl_Position = vec4(vPosition, 0.0, 1.0);
}
`;
const drawFSGLSL = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(0.0, 0.0, 0.0, 1.0);
}
`;
function generatePoints(num) {
const arr = [];
for (let i = 0; i < num; i++) {
const u = i / num;
const a = u * Math.PI * 2.0;
arr.push(Math.cos(a) * 0.8, Math.sin(a) * 0.8);
}
return new Float32Array(arr);
}
const numPoints = 12;
let time = 0.0;
const createShader = function (gl, type, glsl) {
const shader = gl.createShader(type);
gl.shaderSource(shader, glsl);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
};
const createProgram = function (gl, vsGLSL, fsGLSL, outVaryings) {
const vs = createShader(gl, gl.VERTEX_SHADER, vsGLSL);
const fs = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL);
const prg = gl.createProgram();
gl.attachShader(prg, vs);
gl.attachShader(prg, fs);
if (outVaryings) {
gl.transformFeedbackVaryings(prg, outVaryings, gl.SEPARATE_ATTRIBS);
}
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
throw new Error(gl.getProgramInfoLog(prg));
}
return prg;
};
// gen prog
const genProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, [
"vPosition"
]);
gl.bindAttribLocation(genProg, 0, "position");
const timeLocGen = gl.getUniformLocation(genProg, "time");
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL);
gl.bindAttribLocation(drawProg, 0, "vPosition");
const timeLocDraw = gl.getUniformLocation(drawProg, "time");
const dotVertexArray = gl.createVertexArray();
gl.bindVertexArray(dotVertexArray);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// gl.bufferData(gl.ARRAY_BUFFER, numPoints * 2 * 4, gl.DYNAMIC_DRAW);
gl.bufferData(gl.ARRAY_BUFFER, generatePoints(12), gl.DYNAMIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(
0, // location
2, // size (components per iteration)
gl.FLOAT, // type of to get from buffer
false, // normalize
0, // stride (bytes to advance each iteration)
0 // offset (bytes from start of buffer)
);
gl.bindVertexArray(null);
const tf = gl.createTransformFeedback();
function loop() {
// update positions
gl.useProgram(genProg);
gl.enable(gl.RASTERIZER_DISCARD);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, positionBuffer);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
gl.beginTransformFeedback(gl.POINTS);
gl.uniform1f(timeLocGen, time);
gl.drawArrays(gl.POINTS, 0, numPoints);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);
// draw vertices
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.bindVertexArray(dotVertexArray);
gl.useProgram(drawProg);
gl.uniform1f(timeLocDraw, time);
gl.drawArrays(gl.POINTS, 0, numPoints);
}
function animate() {
time += 0.01;
loop();
window.requestAnimationFrame(animate);
}
animate();
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<canvas></canvas>
<script src="src/index.js"></script>
</body>
</html>
Actually you only have 1 buffer. You cannot read and write the same buffer. This is undefined behavior. Read the points from the 1st buffer and write the transformed points to the 2nd buffer. Draw the points from the 2nd buffer.
const canvas = document.querySelector("canvas");
canvas.width = 500;
canvas.height = 500;
const gl = canvas.getContext("webgl2");
const genPointsVSGLSL = `#version 300 es
uniform float time;
in vec2 position;
out vec2 vPosition;
void main() {
vPosition = vec2(position * (sin(time) + 1.0)*0.5);
}
`;
const genPointsFSGLSL = `#version 300 es
void main() {
discard;
}
`;
const drawVSGLSL = `#version 300 es
uniform float time;
in vec2 vPosition;
void main() {
gl_PointSize = 20.0;
gl_Position = vec4(vPosition, 0.0, 1.0);
}
`;
const drawFSGLSL = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(0.0, 0.0, 0.0, 1.0);
}
`;
function generatePoints(num) {
const arr = [];
for (let i = 0; i < num; i++) {
const u = i / num;
const a = u * Math.PI * 2.0;
arr.push(Math.cos(a) * 0.8, Math.sin(a) * 0.8);
}
return new Float32Array(arr);
}
const numPoints = 12;
let time = 0.0;
const createShader = function (gl, type, glsl) {
const shader = gl.createShader(type);
gl.shaderSource(shader, glsl);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
};
const createProgram = function (gl, vsGLSL, fsGLSL, outVaryings) {
const vs = createShader(gl, gl.VERTEX_SHADER, vsGLSL);
const fs = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL);
const prg = gl.createProgram();
gl.attachShader(prg, vs);
gl.attachShader(prg, fs);
if (outVaryings) {
gl.transformFeedbackVaryings(prg, outVaryings, gl.SEPARATE_ATTRIBS);
}
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
throw new Error(gl.getProgramInfoLog(prg));
}
return prg;
};
// gen prog
const genProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, [
"vPosition"
]);
gl.bindAttribLocation(genProg, 0, "position");
const timeLocGen = gl.getUniformLocation(genProg, "time");
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL);
gl.bindAttribLocation(drawProg, 0, "vPosition");
const timeLocDraw = gl.getUniformLocation(drawProg, "time");
// source buffer
const positionBuffer1 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer1);
gl.bufferData(gl.ARRAY_BUFFER, generatePoints(12), gl.DYNAMIC_DRAW);
// trnsform feedback VAO
const dotVertexArray1 = gl.createVertexArray();
gl.bindVertexArray(dotVertexArray1);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
// traget buffer
const positionBuffer2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer2);
gl.bufferData(gl.ARRAY_BUFFER, 12*2*4, gl.DYNAMIC_DRAW);
// draw points VAO
const dotVertexArray2 = gl.createVertexArray();
gl.bindVertexArray(dotVertexArray2);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const tf = gl.createTransformFeedback();
function loop() {
// update positions
gl.useProgram(genProg);
gl.enable(gl.RASTERIZER_DISCARD);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, positionBuffer2);
gl.beginTransformFeedback(gl.POINTS);
gl.uniform1f(timeLocGen, time);
gl.bindVertexArray(dotVertexArray1);
gl.drawArrays(gl.POINTS, 0, numPoints);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
// draw vertices
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.bindVertexArray(dotVertexArray2);
gl.useProgram(drawProg);
gl.uniform1f(timeLocDraw, time);
gl.drawArrays(gl.POINTS, 0, numPoints);
}
function animate() {
time += 0.01;
loop();
window.requestAnimationFrame(animate);
}
animate();
<canvas></canvas>