Reputation: 1234
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>
Upvotes: 1
Views: 537
Reputation: 1234
After reviewing above accepted answer from Rabbid76 I also created different solution, with only 1 VAO. This requires vertexAttribPointer
changes.
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.1)*0.5);
}
`;
const genPointsFSGLSL = `#version 300 es
void main() {
discard;
}
`;
const drawVSGLSL = `#version 300 es
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;
};
// calc prog
const calcProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, [
"vPosition"
]);
gl.bindAttribLocation(calcProg, 0, "position");
const timeLocGen = gl.getUniformLocation(calcProg, "time");
// draw prog
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL);
gl.bindAttribLocation(drawProg, 0, "vPosition");
// vao init
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(0);
const tf = gl.createTransformFeedback();
// source buffer
const srcBuff = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, srcBuff);
gl.bufferData(gl.ARRAY_BUFFER, generatePoints(numPoints), gl.DYNAMIC_DRAW);
// target buffer
const tgtBuff = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, tgtBuff);
gl.bufferData(gl.ARRAY_BUFFER, numPoints * 2 * 4, gl.DYNAMIC_DRAW);
function loop() {
// update vertex positions
gl.useProgram(calcProg);
gl.uniform1f(timeLocGen, time);
gl.enable(gl.RASTERIZER_DISCARD);
// will be needed if more objects are present
// gl.bindVertexArray(vao);
// binding srcBuff as ARRAY_BUFFER so it can be used as
// attr0 in VAO so tgtBuff is unlocked for TF
gl.bindBuffer(gl.ARRAY_BUFFER, srcBuff);
// vertexAttribPointer implicitly binds currently bound ARRAY_BUFFER
// to currently bound VAO using currently used program
// and configures attr props.
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
// using tgtBuff as write buffer for TF
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tgtBuff);
gl.beginTransformFeedback(gl.POINTS);
// drawingArrays is not actually drawing anything but sends output varyings
// from vertex shader to tfo bound buffer (tgtBuff)
gl.drawArrays(gl.POINTS, 0, numPoints);
gl.endTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
gl.disable(gl.RASTERIZER_DISCARD);
// draw vertices
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(drawProg);
// binding tgtBuff as ARRAY_BUFFER
gl.bindBuffer(gl.ARRAY_BUFFER, tgtBuff);
// pointer binds the ARRAY_BUFFER to VAO attribute
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
// drawing output of tgtBuff
gl.drawArrays(gl.POINTS, 0, numPoints);
}
function animate() {
time += 0.005;
loop();
window.requestAnimationFrame(animate);
}
animate();
<canvas></canvas>
Upvotes: 0
Reputation: 210878
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>
Upvotes: 1