royalBlue
royalBlue

Reputation: 95

WebGL drawBuffer outputs black textures

I am trying to write simple webgl multipass program with the help of drawBuffers. I create 2 drawBuffer textures and assign color to them in fragmentShaderPass1. Texture1 should be green and Texture2 should be brown. Then I pass these textures as a uniform to fragmentShaderPass2 which uses color of the Texture1 to render rectangle. Problem is that rectangle is always black instead of green! What am I doing wrong here?

    // STAGE 1
    let fragmentShaderPass1 = `
        #extension GL_EXT_draw_buffers : require
        precision highp float;
        precision highp int;
        precision highp sampler2D;

        void main( void )
        {
            gl_FragData[0] = vec4(.2, .8, .0, 1);  // green
            gl_FragData[1] = vec4(.6, .5, .4, .3);  // brown
        }
    `;
    let pass1_prog = createProgram(gl, globals.vertexShader, fragmentShaderPass1);

    let firstStageFrameBuffer = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, firstStageFrameBuffer);

    gl.useProgram(pass1_prog);

    for (let i = 0; i < 2; i++) 
    {
        let tex = gl.createTexture();
        textures.push(tex);

        gl.bindTexture(gl.TEXTURE_2D, tex);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

        // attach texture to framebuffer
        gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL + i, gl.TEXTURE_2D, tex, 0);
    }

    gl.disable(gl.DEPTH_TEST); // framebuffer doesn't even have a depth buffer!
    gl.viewport(0, 0, _this.canvas.width, _this.canvas.height); 

    ext.drawBuffersWEBGL([
        ext.COLOR_ATTACHMENT0_WEBGL,
        ext.COLOR_ATTACHMENT1_WEBGL
    ]);

    gl.drawArrays(gl.POINTS, 0, 1); 



    // STAGE 2
    let fragmentShaderPass2 = `
        precision highp float;
        precision highp int;
        precision highp sampler2D;

        uniform sampler2D inColor0;
        uniform sampler2D inColor1;

        void main( void )
        {
            gl_FragColor = texture2D(inColor0, vec2(0.5));
        }
    `;
    let pass2_prog = createProgram(gl, globals.vertexShader, fragmentShaderPass2);

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    gl.useProgram(pass2_prog);
    gl.viewport(0, 0, canvas.width, canvas.height); 
    gl.clearColor(1.0, 1.0, 1.0, 1.0);

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, textures[0]);
    gl.uniform1i(gl.getUniformLocation(pass2_prog, "inColor0"), 0);

    gl.activeTexture(gl.TEXTURE1);
    gl.bindTexture(gl.TEXTURE_2D, textures[1]); 
    gl.uniform1i(gl.getUniformLocation(pass2_prog, "inColor1"), 1);

    gl.drawArrays(gl.POINTS, 0, 1);

Upvotes: 1

Views: 929

Answers (1)

user128511
user128511

Reputation:

The code you posted above does not make your textures renderable and gets this error in the JavaScript console

RENDER WARNING: texture bound to texture unit 0 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?

They are not renderable because they have no mips but are set to use mips (the default). They are also probably not power of 2 dimensions though you didn't show the size of the canvas but just guessing it's not a power of 2.

Making them renderable

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

makes them render

const canvas = document.querySelector('canvas');
const _this = {canvas};
const textures = [];
const gl = _this.canvas.getContext('webgl');
const ext = gl.getExtension('WEBGL_draw_buffers');
if (!ext) { alert('need WEBGL_draw_buffers'); }
const globals = {
  vertexShader: `
    void main() {
      gl_Position = vec4(0, 0, 0, 1);
      gl_PointSize = 100.0;
    }
  `,
}
// STAGE 1
let fragmentShaderPass1 = `
  #extension GL_EXT_draw_buffers : require
  precision highp float;
  precision highp int;
  precision highp sampler2D;

  void main( void )
  {
      gl_FragData[0] = vec4(.2, .8, .0, 1);  // green
      gl_FragData[1] = vec4(.6, .5, .4, 1);  // brown
  }
`;
let pass1_prog = createProgram(gl, globals.vertexShader, fragmentShaderPass1);

let firstStageFrameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, firstStageFrameBuffer);

gl.useProgram(pass1_prog);

for (let i = 0; i < 2; i++) {
  let tex = gl.createTexture();
  textures.push(tex);

  gl.bindTexture(gl.TEXTURE_2D, tex);

  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

  // attach texture to framebuffer
  gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL + i, gl.TEXTURE_2D, tex, 0);
}

gl.disable(gl.DEPTH_TEST); // framebuffer doesn't even have a depth buffer!
gl.viewport(0, 0, _this.canvas.width, _this.canvas.height);

ext.drawBuffersWEBGL([
  ext.COLOR_ATTACHMENT0_WEBGL,
  ext.COLOR_ATTACHMENT1_WEBGL
]);

gl.drawArrays(gl.POINTS, 0, 1);



// STAGE 2
let fragmentShaderPass2 = `
        precision highp float;
        precision highp int;
        precision highp sampler2D;

        uniform sampler2D inColor0;
        uniform sampler2D inColor1;

        void main( void )
        {
            gl_FragColor = texture2D(inColor0, vec2(0.5));
        }
    `;
let pass2_prog = createProgram(gl, globals.vertexShader, fragmentShaderPass2);

gl.bindFramebuffer(gl.FRAMEBUFFER, null);

gl.useProgram(pass2_prog);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textures[0]);
gl.uniform1i(gl.getUniformLocation(pass2_prog, "inColor0"), 0);

gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, textures[1]);
gl.uniform1i(gl.getUniformLocation(pass2_prog, "inColor1"), 1);

gl.drawArrays(gl.POINTS, 0, 1);


// ---
function createProgram(gl, vSrc, fSrc) {
  return twgl.createProgram(gl, [vSrc, fSrc]);
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

Note I changed the "brown" color from vec4(.6, .5, .4, .3) to vec4(.6, .5, .4, 1). vec4(.6, .5, .4, .3) is an invalid color for the canvas unless you do more math on output or set the canvas to not except premultiplied alpha color. Your output shader didn't do any extra math and you didn't show your code for setting up the canvas. The default is the browser expects you to put premultiplied color values in the canvas. vec4(.6, .5, .4, .3) is not premultiplied alpha because if it was r, g, and b could not be > a.

Upvotes: 3

Related Questions