在着色器程序之间传递数据
pass data between shader programs
好的,我会尽可能简单。我想在着色器程序之间传递数据。我目前正在使用 readPixels 来执行此操作,但我觉得它可能会减慢操作速度,我正在探索更快的选项。
我的程序做什么:
- program1 对 canvas 进行渲染。
- program2 在我想传递给 program1 的着色器中做了一些很棒的操作。
我的问题:
- 是否可以使用 program2 中的 vbo 并将其传递给 program1 进行渲染?从我在下面给出的 link 中听起来,您不能跨上下文共享数据,这意味着来自一个缓冲区的数据不能用于另一个缓冲区。但也许我遗漏了什么。
- 我相信本文中提到的方法会通过渲染到 canvas 然后使用 texImage2D 更新 program1 (Copy framebuffer data from one WebGLRenderingContext to another?) 来完成我正在寻找的事情。我对么?如果是这样,这会比使用 readPixels 更快吗?(我问是因为如果使用 texImage2D 大致相同,我不会打扰)。
提前感谢任何回答的人。
将数据从一个着色器传递到下一个着色器的正常方法是 render to a texture(通过将该纹理附加到帧缓冲区)。然后将该纹理传递给第二个着色器。
function main() {
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) {
return alert('need webgl2');
}
const vs1 = `#version 300 es
void main () {
gl_Position = vec4(0, 0, 0, 1);
gl_PointSize = 64.0;
}
`;
const fs1 = `#version 300 es
precision highp float;
out vec4 myOutColor;
void main() {
myOutColor = vec4(fract(gl_PointCoord * 4.), 0, 1);
}
`;
const vs2 = `#version 300 es
in vec4 position;
void main () {
gl_Position = position;
gl_PointSize = 32.0;
}
`;
const fs2 = `#version 300 es
precision highp float;
uniform sampler2D tex;
out vec4 myOutColor;
void main() {
myOutColor = texture(tex, gl_PointCoord);
}
`;
// make 2 programs
const prg1 = twgl.createProgram(gl, [vs1, fs1]);
const prg2 = twgl.createProgram(gl, [vs2, fs2]);
// make a texture
const tex = gl.createTexture();
const texWidth = 64;
const texHeight = 64;
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, texWidth, texHeight, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// attach texture to framebuffer
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, tex, 0);
// render to texture
gl.viewport(0, 0, texWidth, texHeight);
gl.useProgram(prg1);
gl.drawArrays(gl.POINTS, 0, 1);
// render texture (output of prg1) to canvas using prg2
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(prg2);
// note: the texture is already bound to texture unit 0
// and uniforms default to 0 so the texture is already setup
const posLoc = gl.getAttribLocation(prg2, 'position')
const numDraws = 12
for (let i = 0; i < numDraws; ++i) {
const a = i / numDraws * Math.PI * 2;
gl.vertexAttrib2f(posLoc, Math.sin(a) * .7, Math.cos(a) * .7);
gl.drawArrays(gl.POINTS, 0, 1);
}
}
main();
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
您还可以使用 "transform feedback" 将顶点着色器的输出存储到一个或多个缓冲区,当然这些缓冲区可以用作另一个着色器的输入。
// this example from
// https://webgl2fundamentals.org/webgl/lessons/resources/webgl-state-diagram.html?exampleId=transform-feedback
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
const genPointsVSGLSL = `#version 300 es
uniform int numPoints;
out vec2 position;
out vec4 color;
#define PI radians(180.0)
void main() {
float u = float(gl_VertexID) / float(numPoints);
float a = u * PI * 2.0;
position = vec2(cos(a), sin(a)) * 0.8;
color = vec4(u, 0, 1.0 - u, 1);
}
`;
const genPointsFSGLSL = `#version 300 es
void main() {
discard;
}
`;
const drawVSGLSL = `#version 300 es
in vec4 position;
in vec4 color;
out vec4 v_color;
void main() {
gl_PointSize = 20.0;
gl_Position = position;
v_color = color;
}
`;
const drawFSGLSL = `#version 300 es
precision highp float;
in vec4 v_color;
out vec4 outColor;
void main() {
outColor = v_color;
}
`;
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.getProgramParameter(prg))
}
return prg
};
const genProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, ['position', 'color']);
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL);
const numPointsLoc = gl.getUniformLocation(genProg, 'numPoints');
const posLoc = gl.getAttribLocation(drawProg, 'position');
const colorLoc = gl.getAttribLocation(drawProg, 'color');
const numPoints = 24;
// make a vertex array and attach 2 buffers
// one for 2D positions, 1 for colors.
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.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(
posLoc, // 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)
);
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, numPoints * 4 * 4, gl.DYNAMIC_DRAW);
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(
colorLoc, // location
4, // 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)
);
// This is not really needed but if we end up binding anything
// to ELEMENT_ARRAY_BUFFER, say we are generating indexed geometry
// we'll change cubeVertexArray's ELEMENT_ARRAY_BUFFER. By binding
// null here that won't happen.
gl.bindVertexArray(null);
// setup a transform feedback object to write to
// the position and color buffers
const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, positionBuffer);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, colorBuffer);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
// above this line is initialization code
// --------------------------------------
// below is rendering code.
// --------------------------------------
// First compute points into buffers
// no need to call the fragment shader
gl.enable(gl.RASTERIZER_DISCARD);
// unbind the buffers so we don't get errors.
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.useProgram(genProg);
// generate numPoints of positions and colors
// into the buffers
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.beginTransformFeedback(gl.POINTS);
gl.uniform1i(numPointsLoc, numPoints);
gl.drawArrays(gl.POINTS, 0, numPoints);
gl.endTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
// turn on using fragment shaders again
gl.disable(gl.RASTERIZER_DISCARD);
// --------------------------------------
// Now draw using the buffers we just computed
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.bindVertexArray(dotVertexArray);
gl.useProgram(drawProg);
gl.drawArrays(gl.POINTS, 0, numPoints);
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
另外 可能会有用。
好的,所以我尝试做的是类似以下的事情(希望这对将来的其他人有帮助)。基本上我想让一个着色器为另一个将渲染的着色器(程序#1)计算移动(程序#2)。我想避免在 JS 中进行任何矢量计算。这个例子结合了@gman 的转换反馈样本和我上面提供的样本:
const canvas = document.querySelector('canvas');
var gl = canvas.getContext('webgl2', {preserveDrawingBuffer: true});
// ___________shaders
// ___________vs and fs #1
const genPointsVSGLSL = `#version 300 es
in vec4 aPos;
void main(void) {
gl_PointSize = 20.0;
gl_Position = vec4( -0.01 + aPos.x , -0.01+aPos.y , aPos.zw);
}
`;
const genPointsFSGLSL = `#version 300 es
precision highp float;
out vec4 color;
void main() {
discard;
//color = vec4(0.5,0.5,0.0,1.0);
}
`;
// ___________vs and fs #2
const drawVSGLSL = `#version 300 es
in vec4 position;
void main() {
gl_PointSize = 20.0;
gl_Position = position;
}
`;
const drawFSGLSL = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4( 255.0,0.0,0.0,1.0 );
}
`;
// create shaders and programs code
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.getProgramParameter(prg))
}
return prg
};
const genProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, ['gl_Position']);
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL, ['gl_Position']);
// program1 location attribute
const positionLoc = gl.getAttribLocation( drawProg , 'position');
// program2 location attribute
const aPosLoc = gl.getAttribLocation( genProg , 'aPos');
var vertizes = [0.8,0,0,1, 0.8,0.5,0,1];
var indizes = vertizes.length/4;
// create buffers and transform feedback
var bufA = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, bufA)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( vertizes ), gl.DYNAMIC_COPY)
var bufB = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, bufB)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( vertizes ) , gl.DYNAMIC_COPY)
var transformFeedback = gl.createTransformFeedback()
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback)
// draw
function draw(){
gl.useProgram( genProg );
gl.clear(gl.COLOR_BUFFER_BIT);
// bind bufA to output of program#2
gl.bindBuffer(gl.ARRAY_BUFFER, bufA);
gl.enableVertexAttribArray( aPosLoc );
gl.vertexAttribPointer(aPosLoc, 4, gl.FLOAT, gl.FALSE, 0, 0)
// run movement calculation code, aka program#2 (calculate movement location and hide the results using RASTERIZER_DISCARD )
gl.enable(gl.RASTERIZER_DISCARD);
gl.drawArrays(gl.POINTS, 0, indizes);
gl.disable(gl.RASTERIZER_DISCARD);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, bufB);
// move dot using rendering code and the position calculated previously which is still stored in bufA
gl.useProgram( drawProg );
gl.bindBuffer( gl.ARRAY_BUFFER, bufA );
gl.enableVertexAttribArray( positionLoc );
gl.vertexAttribPointer( positionLoc , 4, gl.FLOAT, gl.FALSE, 0, 0);
gl.drawArrays(gl.POINTS, 0, indizes);
gl.useProgram( genProg );
// run transforma feedback
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, indizes);
gl.endTransformFeedback();
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
// switch bufA and bufB in preperation for the next draw call
var t = bufA;
bufA = bufB;
bufB = t;
}
setInterval( draw , 100 );
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
好的,我会尽可能简单。我想在着色器程序之间传递数据。我目前正在使用 readPixels 来执行此操作,但我觉得它可能会减慢操作速度,我正在探索更快的选项。
我的程序做什么:
- program1 对 canvas 进行渲染。
- program2 在我想传递给 program1 的着色器中做了一些很棒的操作。
我的问题:
- 是否可以使用 program2 中的 vbo 并将其传递给 program1 进行渲染?从我在下面给出的 link 中听起来,您不能跨上下文共享数据,这意味着来自一个缓冲区的数据不能用于另一个缓冲区。但也许我遗漏了什么。
- 我相信本文中提到的方法会通过渲染到 canvas 然后使用 texImage2D 更新 program1 (Copy framebuffer data from one WebGLRenderingContext to another?) 来完成我正在寻找的事情。我对么?如果是这样,这会比使用 readPixels 更快吗?(我问是因为如果使用 texImage2D 大致相同,我不会打扰)。
提前感谢任何回答的人。
将数据从一个着色器传递到下一个着色器的正常方法是 render to a texture(通过将该纹理附加到帧缓冲区)。然后将该纹理传递给第二个着色器。
function main() {
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) {
return alert('need webgl2');
}
const vs1 = `#version 300 es
void main () {
gl_Position = vec4(0, 0, 0, 1);
gl_PointSize = 64.0;
}
`;
const fs1 = `#version 300 es
precision highp float;
out vec4 myOutColor;
void main() {
myOutColor = vec4(fract(gl_PointCoord * 4.), 0, 1);
}
`;
const vs2 = `#version 300 es
in vec4 position;
void main () {
gl_Position = position;
gl_PointSize = 32.0;
}
`;
const fs2 = `#version 300 es
precision highp float;
uniform sampler2D tex;
out vec4 myOutColor;
void main() {
myOutColor = texture(tex, gl_PointCoord);
}
`;
// make 2 programs
const prg1 = twgl.createProgram(gl, [vs1, fs1]);
const prg2 = twgl.createProgram(gl, [vs2, fs2]);
// make a texture
const tex = gl.createTexture();
const texWidth = 64;
const texHeight = 64;
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, texWidth, texHeight, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// attach texture to framebuffer
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, tex, 0);
// render to texture
gl.viewport(0, 0, texWidth, texHeight);
gl.useProgram(prg1);
gl.drawArrays(gl.POINTS, 0, 1);
// render texture (output of prg1) to canvas using prg2
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(prg2);
// note: the texture is already bound to texture unit 0
// and uniforms default to 0 so the texture is already setup
const posLoc = gl.getAttribLocation(prg2, 'position')
const numDraws = 12
for (let i = 0; i < numDraws; ++i) {
const a = i / numDraws * Math.PI * 2;
gl.vertexAttrib2f(posLoc, Math.sin(a) * .7, Math.cos(a) * .7);
gl.drawArrays(gl.POINTS, 0, 1);
}
}
main();
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
您还可以使用 "transform feedback" 将顶点着色器的输出存储到一个或多个缓冲区,当然这些缓冲区可以用作另一个着色器的输入。
// this example from
// https://webgl2fundamentals.org/webgl/lessons/resources/webgl-state-diagram.html?exampleId=transform-feedback
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
const genPointsVSGLSL = `#version 300 es
uniform int numPoints;
out vec2 position;
out vec4 color;
#define PI radians(180.0)
void main() {
float u = float(gl_VertexID) / float(numPoints);
float a = u * PI * 2.0;
position = vec2(cos(a), sin(a)) * 0.8;
color = vec4(u, 0, 1.0 - u, 1);
}
`;
const genPointsFSGLSL = `#version 300 es
void main() {
discard;
}
`;
const drawVSGLSL = `#version 300 es
in vec4 position;
in vec4 color;
out vec4 v_color;
void main() {
gl_PointSize = 20.0;
gl_Position = position;
v_color = color;
}
`;
const drawFSGLSL = `#version 300 es
precision highp float;
in vec4 v_color;
out vec4 outColor;
void main() {
outColor = v_color;
}
`;
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.getProgramParameter(prg))
}
return prg
};
const genProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, ['position', 'color']);
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL);
const numPointsLoc = gl.getUniformLocation(genProg, 'numPoints');
const posLoc = gl.getAttribLocation(drawProg, 'position');
const colorLoc = gl.getAttribLocation(drawProg, 'color');
const numPoints = 24;
// make a vertex array and attach 2 buffers
// one for 2D positions, 1 for colors.
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.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(
posLoc, // 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)
);
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, numPoints * 4 * 4, gl.DYNAMIC_DRAW);
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(
colorLoc, // location
4, // 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)
);
// This is not really needed but if we end up binding anything
// to ELEMENT_ARRAY_BUFFER, say we are generating indexed geometry
// we'll change cubeVertexArray's ELEMENT_ARRAY_BUFFER. By binding
// null here that won't happen.
gl.bindVertexArray(null);
// setup a transform feedback object to write to
// the position and color buffers
const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, positionBuffer);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, colorBuffer);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
// above this line is initialization code
// --------------------------------------
// below is rendering code.
// --------------------------------------
// First compute points into buffers
// no need to call the fragment shader
gl.enable(gl.RASTERIZER_DISCARD);
// unbind the buffers so we don't get errors.
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.useProgram(genProg);
// generate numPoints of positions and colors
// into the buffers
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.beginTransformFeedback(gl.POINTS);
gl.uniform1i(numPointsLoc, numPoints);
gl.drawArrays(gl.POINTS, 0, numPoints);
gl.endTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
// turn on using fragment shaders again
gl.disable(gl.RASTERIZER_DISCARD);
// --------------------------------------
// Now draw using the buffers we just computed
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.bindVertexArray(dotVertexArray);
gl.useProgram(drawProg);
gl.drawArrays(gl.POINTS, 0, numPoints);
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
另外
好的,所以我尝试做的是类似以下的事情(希望这对将来的其他人有帮助)。基本上我想让一个着色器为另一个将渲染的着色器(程序#1)计算移动(程序#2)。我想避免在 JS 中进行任何矢量计算。这个例子结合了@gman 的转换反馈样本和我上面提供的样本:
const canvas = document.querySelector('canvas');
var gl = canvas.getContext('webgl2', {preserveDrawingBuffer: true});
// ___________shaders
// ___________vs and fs #1
const genPointsVSGLSL = `#version 300 es
in vec4 aPos;
void main(void) {
gl_PointSize = 20.0;
gl_Position = vec4( -0.01 + aPos.x , -0.01+aPos.y , aPos.zw);
}
`;
const genPointsFSGLSL = `#version 300 es
precision highp float;
out vec4 color;
void main() {
discard;
//color = vec4(0.5,0.5,0.0,1.0);
}
`;
// ___________vs and fs #2
const drawVSGLSL = `#version 300 es
in vec4 position;
void main() {
gl_PointSize = 20.0;
gl_Position = position;
}
`;
const drawFSGLSL = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4( 255.0,0.0,0.0,1.0 );
}
`;
// create shaders and programs code
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.getProgramParameter(prg))
}
return prg
};
const genProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, ['gl_Position']);
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL, ['gl_Position']);
// program1 location attribute
const positionLoc = gl.getAttribLocation( drawProg , 'position');
// program2 location attribute
const aPosLoc = gl.getAttribLocation( genProg , 'aPos');
var vertizes = [0.8,0,0,1, 0.8,0.5,0,1];
var indizes = vertizes.length/4;
// create buffers and transform feedback
var bufA = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, bufA)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( vertizes ), gl.DYNAMIC_COPY)
var bufB = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, bufB)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( vertizes ) , gl.DYNAMIC_COPY)
var transformFeedback = gl.createTransformFeedback()
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback)
// draw
function draw(){
gl.useProgram( genProg );
gl.clear(gl.COLOR_BUFFER_BIT);
// bind bufA to output of program#2
gl.bindBuffer(gl.ARRAY_BUFFER, bufA);
gl.enableVertexAttribArray( aPosLoc );
gl.vertexAttribPointer(aPosLoc, 4, gl.FLOAT, gl.FALSE, 0, 0)
// run movement calculation code, aka program#2 (calculate movement location and hide the results using RASTERIZER_DISCARD )
gl.enable(gl.RASTERIZER_DISCARD);
gl.drawArrays(gl.POINTS, 0, indizes);
gl.disable(gl.RASTERIZER_DISCARD);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, bufB);
// move dot using rendering code and the position calculated previously which is still stored in bufA
gl.useProgram( drawProg );
gl.bindBuffer( gl.ARRAY_BUFFER, bufA );
gl.enableVertexAttribArray( positionLoc );
gl.vertexAttribPointer( positionLoc , 4, gl.FLOAT, gl.FALSE, 0, 0);
gl.drawArrays(gl.POINTS, 0, indizes);
gl.useProgram( genProg );
// run transforma feedback
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, indizes);
gl.endTransformFeedback();
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
// switch bufA and bufB in preperation for the next draw call
var t = bufA;
bufA = bufB;
bufB = t;
}
setInterval( draw , 100 );
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>