在着色器程序之间传递数据 [英] pass data between shader programs

查看:143
本文介绍了在着色器程序之间传递数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,我将保持尽可能简单.我想在着色器程序之间传递数据.我目前正在使用readPixels来执行此操作,但我认为这可能会减慢操作速度,并且正在探索更快的选项.

Ok I'm going to keep this as simple as possible. I want to pass data between shader programs. I'm using readPixels currently to do that but I feel it may be slowing operations down and I'm exploring faster options.

我的程序做什么:

  1. program1将我的渲染渲染到画布上.
  2. program2在要传递给program1的着色器中执行了一些出色的操作.

我的问题:

  1. 是否可以使用program2中的vbo并将其传递给program1进行渲染?从下面我提供的链接中看,您无法跨上下文共享数据,这意味着一个缓冲区中的数据不能用于另一个缓冲区.但是也许我想念一些东西.
  2. 我相信本文提到的方法可以通过渲染到画布上,然后使用texImage2D更新program1(
  1. is it possible to use the vbo from program2 and pass that to program1 for rendering? From what it sounds like in the link I give below, you can't share data across contexts, meaning the data from one buffer can't be used for another. But maybe I'm missing something.
  2. I believe the method mentioned in this article would do what I'm looking for by rendering to a canvas and then using texImage2D to update program1 (Copy framebuffer data from one WebGLRenderingContext to another?). Am I correct? If so, would this be faster than using readPixels? ( i ask because if using texImage2D is about the same I won't bother ).

在此先感谢所有回答的人.

thanks in advance to anyone who answers.

推荐答案

将数据从一个着色器传递到下一个着色器的正常方法是

The normal way to pass data from one shader to the next is to render to a texture (by attaching that texture to a framebuffer). Then pass that texture to the second shader.

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>

您还可以使用变换反馈"将顶点着色器的输出存储到一个或多个缓冲区,当然这些缓冲区也可以用作另一着色器的输入.

You can also use "transform feedback" to store the outputs of a vertex shader to one or more buffers and of course those buffers can be used as input to another shader.

// 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>

此答案可能有用.

这篇关于在着色器程序之间传递数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆