user3591153
user3591153

Reputation: 419

webgl readpixels textures shader issue

I've been trying to learn some webgl, and basically have two programs where the rendered pixels get stored in a texture and then used in the other program's shader.

webgl.modTexture(gl, webgl.texture[1], webgl.frameBuffer[0]);

This will call and attempt to readpixels of a screen and put the modified data back into the texture to be used in the next program render routine.

All I get is a black screen becoming more transparent gradually, but there should be the u_image being seen also (since this is being rendered in the first program where the pixels are read with readpixels).

If I comment out the second program's rendering:

    gl.useProgram(webgl.program);
    gl.clearColor(0, 0.5, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLES, 0, 6);

Then it will just render u_image without any fading. So I'm not sure what is the problem. Seems like maybe it isn't reading the pixels correctly? But then, why would the fading be working if that's the case? Is there something wrong with trying to use the two textures?

Hopefully someone can look at the code and see what might be the issue.

//webgl stuff

var webgl = new webglData();

function webglData(){
    this.then = 0;      //used for deltatime in rendering
    this.request;       //requestanimationframe, used when stopping/starting

    this.canvas;
    this.context;
    this.div;

    this.program;
    this.cellProgram;

    this.texture = [];
    this.frameBuffer = [];

    this.cellVShader = `
        attribute vec2 aVertexPosition;

        attribute vec2 a_position;
        attribute vec2 a_texCoord;

        uniform vec2 u_resolution;

        varying vec2 v_texCoord;

        varying vec2 v_NW;
        varying vec2 v_N;
        varying vec2 v_NE;
        varying vec2 v_W;
        varying vec2 v_E;
        varying vec2 v_SW;
        varying vec2 v_S;
        varying vec2 v_SE;

        vec2 getOffset( int x, int y){
            vec2 v = floor(a_texCoord * u_resolution);
            v.x += float(x), v.y += float(y);
            v /= u_resolution;
            return v;
        }

        void main() {
            //v_texCoord = a_texCoord;
            //gl_Position = vec4(aVertexPosition, 0.0, 1.0);

            // convert the rectangle from pixels to 0.0 to 1.0
            vec2 zeroToOne = a_position / u_resolution;

            // convert from 0->1 to 0->2
            vec2 zeroToTwo = zeroToOne * 2.0;

            // convert from 0->2 to -1->+1 (clipspace)
            vec2 clipSpace = zeroToTwo - 1.0;

            gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

            // pass the texCoord to the fragment shader
            // The GPU will interpolate this value between points.
            v_texCoord = a_texCoord;

            v_NW = getOffset( -1, 1);
            v_N = getOffset( 0, 1);
            v_NE = getOffset( 1, 1);
            v_W = getOffset( -1, 0);
            v_E = getOffset( 1, 0);
            v_SW = getOffset( -1, -1);
            v_S = getOffset( 0, -1);
            v_SE = getOffset( 1, -1);
        }
    `;
    this.cellFShader = `
        #ifdef GL_ES
        precision highp float;
        #endif

        uniform sampler2D u_canvas1; 
        uniform vec4 uColor;
        // our texture
        uniform sampler2D u_before;
        uniform sampler2D u_after;
        uniform sampler2D u_image;

        // the texCoords passed in from the vertex shader.
        varying vec2 v_texCoord;
        varying vec2 v_NW;
        varying vec2 v_N;
        varying vec2 v_NE;
        varying vec2 v_W;
        varying vec2 v_E;
        varying vec2 v_SW;
        varying vec2 v_S;
        varying vec2 v_SE;

        void main() {
            // Look up a color from the texture.
            gl_FragColor = texture2D(u_image, v_W);
            //gl_FragColor = uColor;
        }
    `;

    this.vertexShader = `
        attribute vec2 aVertexPosition;

        attribute vec2 a_position;
        attribute vec2 a_texCoord;

        uniform vec2 u_resolution;

        varying vec2 v_texCoord;

        void main() {
            //v_texCoord = a_texCoord;
            //gl_Position = vec4(aVertexPosition, 0.0, 1.0);

            // convert the rectangle from pixels to 0.0 to 1.0
            vec2 zeroToOne = a_position / u_resolution;

            // convert from 0->1 to 0->2
            vec2 zeroToTwo = zeroToOne * 2.0;

            // convert from 0->2 to -1->+1 (clipspace)
            vec2 clipSpace = zeroToTwo - 1.0;

            gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

            // pass the texCoord to the fragment shader
            // The GPU will interpolate this value between points.
            v_texCoord = a_texCoord;
        }
    `;
    this.fragmentShader = `
        #ifdef GL_ES
        precision highp float;
        #endif

        uniform sampler2D u_canvas1; 
        uniform vec4 uColor;
        // our texture
        uniform sampler2D u_before;
        uniform sampler2D u_after;
        uniform sampler2D u_image;

        // the texCoords passed in from the vertex shader.
        varying vec2 v_texCoord;

        void main() {
            // Look up a color from the texture.
            gl_FragColor = texture2D(u_before, v_texCoord);
            //gl_FragColor = uColor;
        }
    `;

    this.init = function(){
        this.div = innerDoc.getElementById('webglDiv');
        gui.Window.get().showDevTools();

        this.canvas = document.createElement('canvas');
        this.context = this.canvas.getContext("experimental-webgl");
        this.canvas.width = 512;
        this.canvas.height = 512;
        this.canvas.style.position = 'absolute';
        this.canvas.style.zIndex = -1;
        this.canvas.style.pointerEvents = 'none';

        this.div.appendChild(this.canvas);

        if(!this.context)return;
        var gl = this.context;
        gl.viewport(0, 0, this.canvas.width, this.canvas.height);
        gl.clearColor(0, 0.5, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);

        //compile the shaders and create the program for webgl
        var vs = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vs, this.vertexShader);
        gl.compileShader(vs);        
        var fs = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fs, this.fragmentShader);
        gl.compileShader(fs);
        var cvs = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(cvs, this.cellVShader);
        gl.compileShader(cvs);       
        var cfs = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(cfs, this.cellFShader);
        gl.compileShader(cfs);

        this.program = gl.createProgram();
        gl.attachShader(this.program, vs);
        gl.attachShader(this.program, fs);
        gl.linkProgram(this.program);
        //gl.useProgram(this.program);
        this.cellProgram = gl.createProgram();
        gl.attachShader(this.cellProgram, cvs);
        gl.attachShader(this.cellProgram, cfs);
        gl.linkProgram(this.cellProgram);
        //gl.useProgram(this.cellProgram);

        //output any errors
        if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(vs));
        if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(fs));
        if (!gl.getProgramParameter(this.program, gl.LINK_STATUS))console.log(gl.getProgramInfoLog(this.program));
        if (!gl.getShaderParameter(cvs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(cvs));
        if (!gl.getShaderParameter(cfs, gl.COMPILE_STATUS))console.log(gl.getShaderInfoLog(cfs));
        if (!gl.getProgramParameter(this.cellProgram, gl.LINK_STATUS))console.log(gl.getProgramInfoLog(this.cellProgram));

        this.setupStuff(gl, this.program);
        this.setupStuff(gl, this.cellProgram);

        this.texture.push( this.setupTexture(tool.canvas, 0, "u_image") );
        this.texture.push( this.createBlankTexture(gl, tool.canvas.width*tool.canvas.height*4, gl.RGBA, tool.canvas.width, tool.canvas.height, "u_before") );
        this.texture.push( this.createBlankTexture(gl, tool.canvas.width*tool.canvas.height*4, gl.RGBA, tool.canvas.width, tool.canvas.height, "u_after") );
        this.frameBuffer.push( gl.createFramebuffer() );

        this.request = requestAnimationFrame(this.render);
    }

    this.render = function(now){
        if(!webgl.context || config.tab!='scene'){cancelAnimationFrame(webgl.request); return;}
        var gl = webgl.context;

        // Convert the time to seconds
        now *= 0.001;
        // Subtract the previous time from the current time
        var deltaTime = now - webgl.then;
        // Remember the current time for the next frame.
        webgl.then = now;

        gl.useProgram(webgl.cellProgram);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
        webgl.modTexture(gl, webgl.texture[1], webgl.frameBuffer[0]);

        gl.useProgram(webgl.program);
        gl.clearColor(0, 0.5, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLES, 0, 6);

        this.request = requestAnimationFrame(webgl.render);
    }

    this.setupStuff = function(gl, program){
        gl.useProgram(program);

        // look up where the vertex data needs to go.
        var positionLocation = gl.getAttribLocation(program, "a_position");
        var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");

        // provide texture coordinates for the rectangle.
        //this will be what the texture gets displayed on?
        var texCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
          0.0,  0.0,
          1.0,  0.0,
          0.0,  1.0,
          0.0,  1.0,
          1.0,  0.0,
          1.0,  1.0]), gl.STATIC_DRAW);
        gl.enableVertexAttribArray(texCoordLocation);
        gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);

        // lookup uniforms
        var resolutionLocation = gl.getUniformLocation(program, "u_resolution");

        // set the resolution
        gl.uniform2f(resolutionLocation, this.canvas.width, this.canvas.height);

        // Create a buffer for the position of the rectangle corners.
        // store the data for the texture coordinates that were defined above, into the a_position?
        var buffer = gl.createBuffer();
        //this.frameBuffer.push(buffer);
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

        // Set a rectangle the same size as the image.
        //I guess this adjusts the buffer data that was just created?
        this.setRectangle(gl, 0, 0, this.canvas.width, this.canvas.height);

        //var tex2 = setupTexture(canvas2, 1, program, "u_canvas2");

        // Draw the rectangle.
        //gl.drawArrays(gl.TRIANGLES, 0, 6);
        //gl.drawArrays(gl.TRIANGLES, 0, numItems);
    }

    this.refreshTexture = function(){
        if(!this.context)return;
        var gl = this.context;

        gl.activeTexture(gl.TEXTURE0 + 0);
        gl.bindTexture(gl.TEXTURE_2D, this.texture[0]);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tool.canvas);

        //gl.drawArrays(gl.TRIANGLES, 0, 6);

    }

    this.modTexture = function(gl, sTexture, framebuffer){
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, sTexture, 0);

        if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE)
        {
            var sTextureSize = sTexture.width * sTexture.height * 4;    // r, g, b, a
            var pixels = new Uint8Array( sTextureSize );
            gl.readPixels( 0, 0, sTexture.width, sTexture.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels );

            for( var i=0 ; i<sTextureSize ; i+=4 )
            {
                if( pixels[i+3] > 0 )
                {
                    pixels[i+3] = Math.min( 255, pixels[i+3]*0.995 );     // set half alpha
                }
            }

            // upload changes
            gl.activeTexture(gl.TEXTURE0 + 1);
            gl.bindTexture(gl.TEXTURE_2D, sTexture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 
                        sTexture.width, sTexture.height, 0,
                        gl.RGBA, gl.UNSIGNED_BYTE, pixels);
        }

        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    }

    this.setupFrameBuffer = function(canvas, textureUnit, program, uniformName) {
        if(!this.context)return;
        var gl = this.context;

        var rttFramebuffer = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
        rttFramebuffer.width = this.canvas.width;
        rttFramebuffer.height = this.canvas.height;

        var rttTexture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, rttTexture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
        gl.generateMipmap(gl.TEXTURE_2D);

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

        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, rttTexture, 0);

        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);    

        return rttFrameBuffer;
    }

    this.setupTexture = function(canvas, textureUnit, uniformName) {
        var gl = this.context;
        var tex = gl.createTexture();

        this.updateTextureFromCanvas(tex, canvas, textureUnit);
        tex.width = canvas.width;
        tex.height= canvas.height;

        // Set the parameters so we can render any size image.
        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);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

        gl.useProgram(this.program);
        var location = gl.getUniformLocation(this.program, uniformName);
        gl.uniform1i(location, textureUnit);
        gl.useProgram(this.cellProgram);
        location = gl.getUniformLocation(this.cellProgram, uniformName);
        gl.uniform1i(location, textureUnit);

        return tex;
    }

    this.updateTextureFromCanvas = function(tex, canvas, textureUnit) {
        if(!this.context)return;
        var gl = this.context;

        gl.activeTexture(gl.TEXTURE0 + textureUnit);
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
    }

    this.createBlankTexture = function(gl, dataArray, type, width, height, uniformName) {
        var dataTypedArray = new Uint8Array(dataArray); // Don't need to do this if the data is already in a typed array
        for( var i=0 ; i<dataArray ; i+=4 )
        {
            dataTypedArray[i+3] = 255;
        }
        var texture = gl.createTexture();
        texture.width = width;
        texture.height= height;
        gl.activeTexture(gl.TEXTURE0 + this.texture.length);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, type, width, height, 0, type, gl.UNSIGNED_BYTE, dataTypedArray);
        // Other texture setup here, like filter modes and mipmap generation
        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);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

        gl.useProgram(this.program);
        var location = gl.getUniformLocation(this.program, uniformName);
        gl.uniform1i(location, this.texture.length);
        gl.useProgram(this.cellProgram);
        location = gl.getUniformLocation(this.cellProgram, uniformName);
        gl.uniform1i(location, this.texture.length);

        return texture;
    }


    this.setRectangle = function(gl, x, y, width, height) {
        var x1 = x;
        var x2 = x + width;
        var y1 = y;
        var y2 = y + height;
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        x1, y1,
        x2, y1,
        x1, y2,
        x1, y2,
        x2, y1,
        x2, y2]), gl.STATIC_DRAW);
    }
}

Upvotes: 1

Views: 429

Answers (1)

user3591153
user3591153

Reputation: 419

Okay I managed to get things working. I had to learn what exactly needs to be called at different stages: Do I have to create separate buffers per webgl program?

And I had to change the part with the framebuffer to use a different texture than the one it is putting the data into.

gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,  gl.TEXTURE_2D, sTexture, 0);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, sTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 
             sTexture.width, sTexture.height, 0,
             gl.RGBA, gl.UNSIGNED_BYTE, pixels);

so the sTexture needs to change like so:

gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,  gl.TEXTURE_2D, someOtherTexture, 0);

Upvotes: 1

Related Questions