2

I'm working on MRT in my graphics engine.

An interesting point i'm at (and aim to fix) has my generated fragment shader spitting out:

layout(location = 0) out vec4 thing1;
layout(location = 2) out vec4 thing2;

The drawBuffers call on the application side calls something like this:

gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE, gl.COLOR_ATTACHMENT1]);

However, I'm getting an error:

WebGL: INVALID_OPERATION: drawBuffers: COLOR_ATTACHMENTi_EXT or NONE

So obviously, this would appear to not be allowed. From the documentation I've read from a wikipedia article discussing it:

https://www.khronos.org/opengl/wiki/Fragment_Shader

It states along the lines that the layout location specified refers to the array index specified from the drawBuffers call. So, in theory I would have thought this shader to configuration would be valid.

What am I missing from my understanding that makes this not work?

I ask for understanding mostly and not to fix my program, my generator will correct the indices when I'm done to be 'correct' with no location index skipping.

Update: As noted below, you CAN skip layout locations in the shader. My issue was the improper formatting of the drawBuffers call where I had COLOR_ATTACHMENT1 in the index where ONLY COLOR_ATTACHMENT2 is valid.

Diniden
  • 1,005
  • 6
  • 14

1 Answers1

3

This is wrong

gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE, gl.COLOR_ATTACHMENT1]);

the i-th attachment must be gl.NONE or gl.COLOR_ATTACHMENTi

so it has to be this

gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE, gl.COLOR_ATTACHMENT2]);

function main() {
  const gl = document.querySelector('canvas').getContext('webgl2');

  const vs = `#version 300 es
  void main() {
    gl_Position = vec4(0, 0, 0, 1);
    gl_PointSize = 100.0;
  }
  `;

  const fs = `#version 300 es
  precision highp float;
  layout(location = 0) out vec4 thing1;
  layout(location = 2) out vec4 thing2;
  void main () {
    thing1 = vec4(1, 0, 0, 1);
    thing2 = vec4(0, 0, 1, 1);
  }
  `;

  const prg = twgl.createProgram(gl, [vs, fs]);

  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

  createTextureAndAttach(gl, gl.COLOR_ATTACHMENT0);
  createTextureAndAttach(gl, gl.COLOR_ATTACHMENT2);

  gl.drawBuffers([
    gl.COLOR_ATTACHMENT0,
    gl.NONE,
    gl.COLOR_ATTACHMENT2,
  ]);

  const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
  if (status !== gl.FRAMEBUFFER_COMPLETE) {
    console.error("can't render to this framebuffer combo");
    return;
  }

  gl.useProgram(prg);
  gl.viewport(0, 0, 1, 1);
  gl.drawArrays(gl.POINTS, 0, 1);
  
  checkError();
  
  read(gl.COLOR_ATTACHMENT0);
  read(gl.COLOR_ATTACHMENT2);

  checkError();

  function checkError() {
    const err = gl.getError();
    if (err) {
      console.error(twgl.glEnumToString(gl, err));
    }
  }

  function read(attachmentPoint) {
    gl.readBuffer(attachmentPoint);
    const pixel = new Uint8Array(4);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    console.log(Array.from(pixel).join(','));
  }

  function createTextureAndAttach(gl, attachmentPoint) {
    const tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, tex, 0);
  }

}

main();
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

note: Referencing the OpenGL docs for WebGL are often wrong and/or misleading for WebGL. You need to reference the OpenGL 3.0 ES spec for WebGL2

gman
  • 100,619
  • 31
  • 269
  • 393
  • Thanks! Definitely was my issue. I believe I knew this already but was in some pretty thick woods getting it together! Luckily I had everything set up to do just this so my engine is purring again :) – Diniden Jan 07 '21 at 03:07