WebGLSamples / WebGL2Samples

Short and easy to understand samples demonstrating WebGL 2 features
Other
1.01k stars 143 forks source link

Transform feedback without rendering sample request #164

Open DanielMazurkiewicz opened 4 years ago

DanielMazurkiewicz commented 4 years ago

Hi! Would be nice to have some super simple example of transform feedback with vertex shader like this:

#version 300 es
        precision highp float;
        precision highp int;

        flat out int vertexId;

        void main() {
          vertexId = gl_VertexID + 1;
        }

My (finally) working example:

interface GLShaderInfo {
  name?: string,
  src: string,
  type: number 
}

const createGLShader = (gl: WebGL2RenderingContext, shaderInfo: GLShaderInfo) => {
  const shader = gl.createShader(shaderInfo.type);
  if (shader) {
    const shaderSource = shaderInfo.src.trim();
    gl.shaderSource(shader, shaderSource);
    gl.compileShader(shader);
    const compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compileStatus) {
      const error_message = gl.getShaderInfoLog(shader);
      throw "Could not compile shader \"" +
          shaderInfo.name +
          "\" \n" +
          error_message;
    }
  }
  return shader;
}

const createGLProgram = (
  gl: WebGL2RenderingContext, shaderInfos: GLShaderInfo[], transformFeedbackVaryings?: string[]
) => {
  const program = gl.createProgram();
  if (program) {
    for (let i = 0; i < shaderInfos.length; i++) {
      const shaderInfo = shaderInfos[i];
      const shader = createGLShader(gl, shaderInfo);
      if (shader) {
        gl.attachShader(program, shader);
      }
    }

    if (transformFeedbackVaryings != null) {
      gl.transformFeedbackVaryings(program,
        transformFeedbackVaryings,
        gl.INTERLEAVED_ATTRIBS);
    }
    gl.linkProgram(program);
    var link_status = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!link_status) {
      var error_message = gl.getProgramInfoLog(program);
      throw "Could not link program.\n" + error_message;
    }
  }
  return program;
}

const createGL = (width?: number, height?: number, canvas = document.createElement("canvas")) => {
  if (width) canvas.width = width;
  if (height) canvas.height = height;
  return canvas.getContext("webgl2");
}

function main() {
  const gl = createGL()
  if (gl != null) {
    document.body.appendChild(<HTMLCanvasElement>gl.canvas);

    const vs = {
      type: gl.VERTEX_SHADER,
      src: `#version 300 es
        precision highp float;
        precision highp int;

        flat out int vertexId;

        void main() {
          vertexId = gl_VertexID + 1;
        }
        `
      }

    const fs = {
      type: gl.FRAGMENT_SHADER,
      src: `#version 300 es
      precision mediump float;

      out vec4 outColor;

      void main() {
        outColor = vec4(1, 0, 0, 1);
      }
      `
    }

    // setup GLSL program
    const program = createGLProgram(gl, [vs, fs], ['vertexId']);
    if (program) {
      const totalLoops = 20;      
      gl.useProgram(program);

      // https://open.gl/feedback

      const vertexArray = gl.createVertexArray();
      gl.bindVertexArray(vertexArray);

      const glTransformOutputBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, glTransformOutputBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, totalLoops * 16, gl.STATIC_READ);

      gl.enable(gl.RASTERIZER_DISCARD); // don't use fragment shader

      gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, glTransformOutputBuffer);

      gl.bindBuffer(gl.ARRAY_BUFFER, null);

      gl.beginTransformFeedback(gl.POINTS);

      const offset = 0;
      gl.drawArrays(gl.POINTS, offset, totalLoops);

      gl.endTransformFeedback();
      gl.flush();

      const transformOutputBuffer = new Uint32Array(totalLoops);

      gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, transformOutputBuffer);

      console.log(transformOutputBuffer);
    }
  } else {
    document.write("WebGL2 is not supported by your browser");
  }
}