4

I am trying to create two FBOs and implement a ping-pong render. But, I only get the first frame to work properly. I am trying to simulate a game-of-life and, after the first frame, I only get a black screen. Could you help me check it? I have spent hours on this issue.

Edit

Maybe I didn't describe clearly. Actually, I want to use the textureB as the texture and render it to textureA, then use the textureA to render to screen, then vice versa.

Edit I can see the first frame, which is the textureB. After it go through the fragment shader, it become black. At first, I suspect the fragment shader, I change it to only revert the black to white and white to black. It still becomes all black.

Set up the fbo and texture

glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &textureA);
    glBindTexture(GL_TEXTURE_2D, textureA);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, NULL);

    glGenTextures(1, &textureB);
    glBindTexture(GL_TEXTURE_2D, textureB);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    data=(GLubyte*)malloc(256*256*4*sizeof(GLubyte));
    GLubyte val;
    for (int i = 0; i < 256 * 256 * 4; i+=4) {   
        if (rand()%10 ==1) 
            { val = 0; } 
        else 
            { val = 255; }
        data[i] = data[i+1] = data[i+2] = val;
        data[i+3] = 255;
    }
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

    glGenFramebuffers(1, &fboA);
    glBindFramebuffer(GL_FRAMEBUFFER, fboA);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureA, 0);

    glGenFramebuffers(1, &fboB);
    glBindFramebuffer(GL_FRAMEBUFFER, fboB);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureB, 0);

Render Loop

if ([context API] == kEAGLRenderingAPIOpenGLES2) {


        if(counter%2==0)
        {
            glUseProgram(automateProg);
            glBindFramebuffer(GL_FRAMEBUFFER, fboA);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureB);
            glUniform1i(AUTOMATE_TEXT, 0);
            glUniform1f(DU, 1.0/256);
            glUniform1f(DV, 1.0/256);
            // Update attribute values.
            glVertexAttribPointer(ATTRIB_VERTEX_2, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX_2);

            glVertexAttribPointer(ATTRIB_TEXCOORD_2, 2, GL_FLOAT, GL_FALSE, 0, texCoord);    
            //glEnableVertexAttribArray(ATTRIB_TEXCOORD_2);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:automateProg]) {
                NSLog(@"Failed to validate program: %d", automateProg);
                return;
            }

            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glUseProgram(0);
        }
        else
        {
            glUseProgram(automateProg);            
            glBindFramebuffer(GL_FRAMEBUFFER, fboB);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureA);
            glUniform1i(AUTOMATE_TEXT, 0);
            glUniform1f(DU, 1.0/256);
            glUniform1f(DV, 1.0/256);
            // Update attribute values.
            glVertexAttribPointer(ATTRIB_VERTEX_2, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX_2);
            glVertexAttribPointer(ATTRIB_TEXCOORD_2, 2, GL_FLOAT, GL_FALSE, 0, texCoord); 
            //glEnableVertexAttribArray(ATTRIB_TEXCOORD_2);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:automateProg]) {
                NSLog(@"Failed to validate program: %d", automateProg);
                return;
            }

            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glUseProgram(0);
        }

        [(EAGLView *)self.view setFramebuffer];
        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        if (counter % 2 == 0) {
            glUseProgram(normalProg);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureB);
            glUniform1i(NORMAL_TEXT, 0);
            glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX);
            glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoord); 
            glEnableVertexAttribArray(ATTRIB_TEXCOORD);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:normalProg]) {
                NSLog(@"Failed to validate program: %d", normalProg);
                return;
            }
            glUseProgram(0);

        } else {
            glUseProgram(normalProg);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, textureA);
            glUniform1i(NORMAL_TEXT, 0);
            glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
            glEnableVertexAttribArray(ATTRIB_VERTEX);
            glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoord); 
            glEnableVertexAttribArray(ATTRIB_TEXCOORD);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            if (![self validateProgram:normalProg]) {
                NSLog(@"Failed to validate program: %d", normalProg);
                return;
            }
            glUseProgram(0);
        }
        counter++;

[(EAGLView *)self.view presentFramebuffer];

Fragment Shader

precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D tex; //the input texture
uniform float du; //the width of the cells
uniform float dv; //the height of the cells
    void main() {
        int count = 0;

        vec4 C = texture2D( tex, v_texCoord );
        vec4 E = texture2D( tex, vec2(v_texCoord.x + du, v_texCoord.y) );
        vec4 N = texture2D( tex, vec2(v_texCoord.x, v_texCoord.y + dv) );
        vec4 W = texture2D( tex, vec2(v_texCoord.x - du, v_texCoord.y) );
        vec4 S = texture2D( tex, vec2(v_texCoord.x, v_texCoord.y - dv) );
        vec4 NE = texture2D( tex, vec2(v_texCoord.x + du, v_texCoord.y + dv) );
        vec4 NW = texture2D( tex, vec2(v_texCoord.x - du, v_texCoord.y + dv) );
        vec4 SE = texture2D( tex, vec2(v_texCoord.x + du, v_texCoord.y - dv) );
        vec4 SW = texture2D( tex, vec2(v_texCoord.x - du, v_texCoord.y - dv) );

        if (E.r == 1.0) { count++; }
        if (N.r == 1.0) { count++; }
        if (W.r == 1.0) { count++; }
        if (S.r == 1.0) { count++; }
        if (NE.r == 1.0) { count++; }
        if (NW.r == 1.0) { count++; }
        if (SE.r == 1.0) { count++; }
        if (SW.r == 1.0) { count++; }

        if ( (count == 2 || count == 3)) {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); //cell lives...
        } else {
            gl_FragColor = vec4(0.0,0.0,0.0, 1.0); //cell dies...
        }
    }
Yongwei Xing
  • 12,983
  • 24
  • 70
  • 90

3 Answers3

1

Do I understand your code right, that you want to render a result to a texture in the first if-else-block and render that result to screen in the second if-else-block? If so, then it looks like you have a mistake in how you organize your input and output to begin with. This is what happens in your first pass (I reduced your code):

if(counter%2==0)
{
    glBindFramebuffer(GL_FRAMEBUFFER, fboA); // will render to textureA
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureB); // textureB is our input
} else {
    ...
}

if (counter % 2 == 0) {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureB); // textureB still as input? not textureA?
} else {
    ...
}

...and this is what happens in the second pass:

if(counter%2==0)
{
    ...
} else {
    glBindFramebuffer(GL_FRAMEBUFFER, fboB); // will render to textureB
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureA); // textureA as input
}

if (counter % 2 == 0) {
    ...
} else {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureA); // textureA as input again?
}

The reason why you see something in the first frame is, because you actually render your input data, but not the result of your first pass. And the reason why you have black screen in your second pass may be that your fragment shader does not work correctly. Judging from your shader code, a mistake in accessing the neighbor texels seems to be the most plausible cause for that. Can you provide the values of duand dv?

Also I don't think that using only one texture unit should make any trouble, as Brad pointed out earlier. I'm not sure about that though.

On a side note: for ping-ponging you should consider creating your FBOs as an array to make your code a lot more readable.

EDIT:

I you have problems setting your uniforms du and dv with glUniform1f(), try glUniform1i() (you need to cast with float() in your shader then) or glUniform1fv() instead. I once had the same problem with the PowerVR GLES2 drivers, where this function didn't do anything and caused the uniform to be 0.0.

Good Night Nerd Pride
  • 8,245
  • 4
  • 49
  • 65
  • This might cause the first or second frames to display black, but the worst I could see happening for the frames after that would be that it would lag one frame behind what's being rendered (by drawing the input instead of the output). It's something to fix, but I'm not sure that it's what's halting his rendering here. Why I suspect the use of one texture unit is that I tried to do this myself in OpenGL ES and needed to bind the input and output textures on separate texture units for the rendering to proceed properly. – Brad Larson Nov 28 '11 at 22:17
  • Ok, this might depend on the device/driver, as I haven't experienced this so far. I still suspect a faulty fragment shader to be the real cause, though. However, you are of course correct that the mistake in ordering input and output only affects off-screen rendering. And not very much anyway. – Good Night Nerd Pride Nov 28 '11 at 22:26
  • @user1025555 du and dv is just a step for texture. The texture size is 256*256, so their value is 1/256, which is used to read the neighbour position. – Yongwei Xing Nov 29 '11 at 01:57
  • @user1025555 I suspect the fragment shader firstly. So I change it to only simply revert the black to white and white to black. It still becomes all black. – Yongwei Xing Nov 29 '11 at 02:04
  • Regarding my other comment earlier: I meant **on**-screen rendering, of course. @Yongwei Ying: Do you check `glError()` and `glCheckFramebufferStatus()` (the latter after attaching)? What happens if you just set every texel to e.g. green in your fragment shader. Do you get a green window or does it stay black? – Good Night Nerd Pride Nov 29 '11 at 02:20
  • @user1025555 I try to only set them all orange, yes, the screen becomes orange. – Yongwei Xing Nov 29 '11 at 02:38
  • @Yongwei Ying: If the screen is orange all the time and not only for the 1st frame, then I would say it's an error in the fragment shader. Can you add its complete source to your question? I just noticed that you input `du` and `dv` as uniforms. I once had problems when using `glUniform*f` on a device with a PowerVR SGX GPU (which is also used in iPhones (which I presume you are working with)). It's unlikely, but can you verify that `du` and `dv` are non-zero? E.g. the line `gl_FragColor = vec4(du*255.0, dv*255.0, 0.0, 1.0);` should make the screen yellow if `glUniform*f` is working correctly. – Good Night Nerd Pride Nov 29 '11 at 02:49
  • @user1025555 I use the line, gl_FragColor = vec4(du*255.0, dv*255.0, 0.0, 1.0); it becomes black, seems dv and du is zero. – Yongwei Xing Nov 29 '11 at 11:31
  • @UselessIdiot Thanks for your help. Finally, I make my project work now. However, I still have a very strange issue about glUniform1f. By the way, I didn't use your method. I have two uniform, I use glUniform1f to set the value. But, only one can be set, the second is not. I use the glGetUniformfv and set a breakpoint. Only one is set correctly. – Yongwei Xing Nov 29 '11 at 14:26
  • @Yongwei Xing: Did you try making your uniforms `du`and `dv` to `int`'s, using `glUniform1i()` to set them and then casting them to `float`'s with `float()` before adding them to `v_texCoord` in your fragment shader? If `glUniform*f[v]()` really is the problem, this should definetly work. Also try to check for OpenGL errors with `glGetError()`. – Good Night Nerd Pride Nov 29 '11 at 14:50
  • @UselessIdiot I think I know the reason. I should use glGetUniformLocation after glUseProgram – Yongwei Xing Dec 02 '11 at 14:36
0

If a color plane of a frame has to be accessed, the a texture has to be attached to the frame buffer, which is written to. If a texture, which is attached to a frame buffer, has to be read in a shader, then the textue has to be bound to a textur unit and the index of the textue unit has to be set toa texture sampler uniform of the shader.

Since you can't read from a frame buffer and write to the same frame buffer at once (this would cause undefined behavior) you have to draw to read from one frame buffer and to write to a second frame buffer.

After a each frame the frame buffers have to change their place. The buffer which was read from will become the buffer which will be written to and the buffer which was written to will become the buffer which will be read from.

Create the textures for the frame buffer attachments:

GLuint colorTexture[2];
glGenTextures( 2, &colorTexture[0] );
glBindTexture( GL_TEXTURE_2D, colorTexture[0] );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glBindTexture( GL_TEXTURE_2D, colorTexture[1] );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );

Create the frame buffers:

GLuint frameBuffer[2];
glGenFramebuffers( 2, &frameBuffer[0] );
glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer[0] );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture[0], 0 );
glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer[1] );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture[1], 0 );

Note, if a depth buffer or even a stencil buffer is required, then a GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT or GL_DEPTH_STENCIL_ATTACHMENT has to be attached to the frame buffer.

Since every frame is drawn to a frame buffer you have to implement a post process, which brings the color plane from the frame buffer to the drawing buffer.
This can be done by glBlitFramebuffer, which transfer a rectangle of pixel values from one region of a read framebuffer to another region of a draw framebuffer.

 glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBuffer[ ... ]  );
 glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
 glBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST ); 

Your render main loop should look somehow like this:

int drawFB = 0;
while ( /* ... */ )
{
    int readFB = 1 - drawFB;

    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer[drawFB]);
    glActiveTexture(GL_TEXTURE0 + 1);
    glBindTexture(GL_TEXTURE_2D, colorTexture[readFB]);

    glProgramUse( /* shader program object */ );
    glUniform1i( /* texture sampler 2D location */, 1 ); 

    // do the drawing
    // ...

    // post processing
    glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBuffer[drawFB]  );
    glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
    glBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST );

    drawFB = 1 - drawFB;
}

As an alternative, you can also use 1 frame buffer with 2 color planes and 2 textures attached. Activate alternately the first or the second color plane:

GLuint frameBuffer;
glGenFramebuffers( 1, &frameBuffer );
glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture[0], 0 );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, colorTexture[1], 0 );

int drawFB = 0;
while ( /* ... */ )
{
    int readFB = 1 - drawFB;

    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glDrawBuffer( drawFB == 0 ? GL_COLOR_ATTACHMENT0 : GL_COLOR_ATTACHMENT1 );
    glActiveTexture(GL_TEXTURE0 + 1);
    glBindTexture(GL_TEXTURE_2D, colorTexture[readFB]);

    glProgramUse( /* shader program object */ );
    glUniform1i( /* texture sampler 2D location */, 1 ); 

    // do the drawing
    // ...

    // post processing
    glBindFramebuffer( GL_READ_FRAMEBUFFER, frameBuffer[drawFB]  );
    glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
    glBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST );

    drawFB = 1 - drawFB;
}

See following simple WebGL example for demonstration of the process:

var ShaderProgram = {};
ShaderProgram.Create = function( shaderList, uniformNames ) {
    var shaderObjs = [];
    for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
        var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
        if ( shderObj == 0 )
            return 0;
        shaderObjs.push( shderObj );
    }
    var progObj = this.LinkProgram( shaderObjs )
    if ( progObj != 0 ) {
        progObj.unifomLocation = {};
        for ( var i_n = 0; i_n < uniformNames.length; ++ i_n ) {
            var name = uniformNames[i_n];
            progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
        }
    }
    return progObj;
}
ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); } 
ShaderProgram.SetUniformInt = function( progObj, name, val ) { gl.uniform1i( progObj.unifomLocation[name], val ); }
ShaderProgram.SetUniform2i = function( progObj, name, arr ) { gl.uniform2iv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniformFloat = function( progObj, name, val ) { gl.uniform1f( progObj.unifomLocation[name], val ); }
ShaderProgram.SetUniform2f = function( progObj, name, arr ) { gl.uniform2fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniform3f = function( progObj, name, arr ) { gl.uniform3fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniformMat44 = function( progObj, name, mat ) { gl.uniformMatrix4fv( progObj.unifomLocation[name], false, mat ); }
ShaderProgram.CompileShader = function( source, shaderStage ) {
    var shaderScript = document.getElementById(source);
    if (shaderScript) {
      source = "";
      var node = shaderScript.firstChild;
      while (node) {
        if (node.nodeType == 3) source += node.textContent;
        node = node.nextSibling;
      }
    }
    var shaderObj = gl.createShader( shaderStage );
    gl.shaderSource( shaderObj, source );
    gl.compileShader( shaderObj );
    var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
    if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
    return status ? shaderObj : 0;
} 
ShaderProgram.LinkProgram = function( shaderObjs ) {
    var prog = gl.createProgram();
    for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
        gl.attachShader( prog, shaderObjs[i_sh] );
    gl.linkProgram( prog );
    status = gl.getProgramParameter( prog, gl.LINK_STATUS );
    if ( !status ) alert("Could not initialise shaders");
    gl.useProgram( null );
    return status ? prog : 0;
}

var FrameBuffer = {};
FrameBuffer.Create = function( vp, texturePlan ) {
    var texPlan = texturePlan ? new Uint8Array( texturePlan ) : null;
    var fb = gl.createFramebuffer();
    fb.width = vp[0];
    fb.height = vp[1];
    gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
    fb.color0_texture = gl.createTexture();
    gl.bindTexture( gl.TEXTURE_2D, fb.color0_texture );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
    gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, fb.width, fb.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, texPlan );
    fb.renderbuffer = gl.createRenderbuffer();
    gl.bindRenderbuffer( gl.RENDERBUFFER, fb.renderbuffer );
    gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, fb.width, fb.height );
    gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fb.color0_texture, 0 );
    gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fb.renderbuffer );
    gl.bindTexture( gl.TEXTURE_2D, null );
    gl.bindRenderbuffer( gl.RENDERBUFFER, null );
    gl.bindFramebuffer( gl.FRAMEBUFFER, null );

    fb.Bind = function( clear ) {
        gl.bindFramebuffer( gl.FRAMEBUFFER, this );
        if ( clear ) {
            gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
            //gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
            gl.clear( gl.DEPTH_BUFFER_BIT );
        }
    };

    fb.Release = function( clear ) {
        gl.bindFramebuffer( gl.FRAMEBUFFER, null );
        if ( clear ) {
            gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
            //gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
            gl.clear( gl.DEPTH_BUFFER_BIT );
        }
    };

    fb.BindTexture = function( textureUnit ) {
        gl.activeTexture( gl.TEXTURE0 + textureUnit );
        gl.bindTexture( gl.TEXTURE_2D, this.color0_texture );
    };

    return fb;
}

var curBufInx = 0;  
var tick = 0;     
var signal = 0; 
function drawScene(){

    var canvas = document.getElementById( "glow-canvas" );
    var vp = [canvas.width, canvas.height];

    var currentTime = Date.now();   
    var deltaMS = currentTime - startTime
    testTick = Tick( currentTime, 0.05 )
    signal = testTick > tick ? 1 : 0;
    tick = testTick
    
    var srcBufInx = curBufInx == 0 ? 1 : 0;
        
    gl.viewport( 0, 0, drawFB[curBufInx].width, drawFB[curBufInx].height );
    gl.enable( gl.DEPTH_TEST );
    drawFB[curBufInx].Bind( true );
    
    // set up draw shader
    ShaderProgram.Use( progDraw );
    var texUnitSource = 2;
    drawFB[srcBufInx].BindTexture( texUnitSource );
    ShaderProgram.SetUniformInt( progDraw, "u_colorAttachment0", texUnitSource );
    ShaderProgram.SetUniform2i( progDraw, "u_textureSize", [drawFB[curBufInx].width, drawFB[curBufInx].height] );
    ShaderProgram.SetUniformInt( progDraw, "u_signal", signal );
    
    gl.enableVertexAttribArray( progDraw.inPos );
    gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos );
    gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 );
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx );
    gl.drawElements( gl.TRIANGLES, bufQuad.inxLen, gl.UNSIGNED_SHORT, 0 );
    gl.disableVertexAttribArray( progDraw.inPos );

    drawFB[curBufInx].Release( true );
    gl.viewport( 0, 0, canvas.width, canvas.height );
    var texUnitDraw = 2;
    drawFB[curBufInx].BindTexture( texUnitDraw );
    ShaderProgram.Use( progScreenSpace );
    ShaderProgram.SetUniformInt( progScreenSpace, "u_colorAttachment0", texUnitDraw );

    gl.enableVertexAttribArray( progScreenSpace.inPos );
    gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos );
    gl.vertexAttribPointer( progScreenSpace.inPos, 2, gl.FLOAT, false, 0, 0 );
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx );
    gl.drawElements( gl.TRIANGLES, bufQuad.inxLen, gl.UNSIGNED_SHORT, 0 );
    gl.disableVertexAttribArray( progScreenSpace.inPos );

    curBufInx = curBufInx == 0 ? 1 : 0;
}

function Tick( currentTime, intervall ) {
    return Math.trunc( (currentTime - startTime) / intervall );
}

var plot_download_request = false;
var drawFB;
var sliderScale = 100.0
var gl;
var progDraw;
var progScreenSpace;
var bufCube = {};
var bufQuad = {};
function sceneStart() {

    var canvas = document.getElementById( "glow-canvas");
    var vp = [canvas.width, canvas.height];
    gl = canvas.getContext( "experimental-webgl" );
    if ( !gl )
      return;

    progDraw = ShaderProgram.Create( 
      [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
        { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
      ],
      [ "u_colorAttachment0", "u_textureSize", "u_signal" ] );
    progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
    if ( progDraw == 0 )
        return;

    progScreenSpace = ShaderProgram.Create( 
      [ { source : "screen-shader-vs", stage : gl.VERTEX_SHADER },
        { source : "screen-shader-fs", stage : gl.FRAGMENT_SHADER }
      ],
      [ "u_colorAttachment0" ] );
    progScreenSpace.inPos = gl.getAttribLocation( progDraw, "inPos" );
    if ( progDraw == 0 )
        return;

    // create frame buffers
    var texCX = Math.floor(vp[0] / 4);
    var texCY = Math.floor(vp[1] / 4);
    var texPlan = [];
    for (ix = 0; ix < texCX; ++ix) {
        for (iy = 0; iy < texCY; ++iy) {
            texPlan.push( 0, 0, 0, 0 );
        }
    }
    for (ip = 0; ip < texCX * texCY / 20; ++ip) {
        var inx_tex = Math.floor( Math.random() * texCY ) * texCX + Math.floor( Math.random() * texCX );
        texPlan[inx_tex * 4 + 0] = 255 * Math.random();
        texPlan[inx_tex * 4 + 1] = 255 * Math.random();
        texPlan[inx_tex * 4 + 2] = 127;
        texPlan[inx_tex * 4 + 3] = 255;
    }
    drawFB = [ FrameBuffer.Create( [texCX, texCY], texPlan ), FrameBuffer.Create( [texCX, texCY], texPlan ) ];

    bufQuad.pos = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos );
    gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( [ -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0 ] ), gl.STATIC_DRAW );
    bufQuad.inx = gl.createBuffer();
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx );
    gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ), gl.STATIC_DRAW );
    bufQuad.inxLen = 6;

    startTime = Date.now();
    setInterval(drawScene, 50);
}
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
    vertPos.xy  = inPos.xy;
    gl_Position = vec4( inPos, 0.0, 1.0 );
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vertPos;
uniform sampler2D u_colorAttachment0;
uniform ivec2 u_textureSize;
uniform int u_signal;

vec3 Merge( in vec2 texC, in vec2 dir )
{
  vec2 testC = texC + dir;
  vec2 rangeTest = step( vec2(0.0), testC ) * step( testC, vec2(1.0) );  
  vec3 texCol = texture2D( u_colorAttachment0, testC ).rgb;
  vec2 tempDir = texCol.xy * 2.0 - 1.0;
  vec2 pDir = tempDir;
  pDir.x *= step( abs(tempDir.y * 0.7), abs( tempDir.x ) );
  pDir.y *= step( abs(tempDir.x * 0.7), abs( tempDir.y ) );
  pDir = sign( pDir );
  vec2 tDir = sign( dir );
  //vec2 dirTestTemp = step( vec2(0.5), -tDir * pDir );
  //float dirTest = dirTestTemp.x * dirTestTemp.y;
  vec2 dirTestTemp = tDir + pDir;
  float dirTest = 1.0 - step( 0.5, abs( dirTestTemp.x ) + abs( dirTestTemp.y ) );
  return rangeTest.x * rangeTest.y * dirTest * texCol;
}

void main()
{
    ivec2 texSize = u_textureSize;
    vec2  texStep = vec2( 1.0 / float( texSize.x ), 1.0 / float( texSize.y ) );
    vec2  texC    = vertPos.st * 0.5 + 0.5;
    
    vec3 texCol = vec3(0.0);
    if ( u_signal == 0 )
    {
        texCol = texture2D( u_colorAttachment0, texC ).rgb;
    }
    else
    {
        texCol += Merge( texC, -texStep );
        texCol += Merge( texC, vec2( -texStep.x, 0.0 ) );
        texCol += Merge( texC, vec2( -texStep.x, texStep.y ) );
        texCol += Merge( texC, vec2( 0.0, -texStep.y ) );
        texCol += Merge( texC, vec2( 0.0, texStep.y ) );
        texCol += Merge( texC, vec2( texStep.x, -texStep.y ) );
        texCol += Merge( texC, vec2( texStep.x, 0.0 ) );
        texCol += Merge( texC, texStep );
    }

    if ( texCol.b > 0.0 )
    {
        vec2 colDir = texCol.rg * 2.0 - 1.0;
        vec2 pDir = sign( colDir );
        vec2 nextTexC = texC + pDir * texStep;
        if ( nextTexC.x <= texStep.x/2.0 || nextTexC.x >= 1.0-texStep.x/2.0 )
            colDir.x = -colDir.x;
        if ( nextTexC.y <= texStep.y/2.0 || nextTexC.y >= 1.0-texStep.y/2.0 )
            colDir.y *= -1.0;
        texCol.rg = colDir * 0.5 + 0.5;
    }

    vec3 col = texCol.rgb;
    gl_FragColor = vec4( col, 1.0 );
}
</script>

<script id="screen-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
    vertPos.xy  = inPos.xy;
    gl_Position = vec4( inPos, 0.0, 1.0 );
}
</script>

<script id="screen-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vertPos;
uniform sampler2D u_colorAttachment0;
void main()
{
    vec4 texCol = texture2D( u_colorAttachment0, vertPos.st * 0.5 + 0.5 );
    gl_FragColor = vec4( texCol.rgb, 1.0 );
}
</script>

<body onload="sceneStart();">
    <canvas id="glow-canvas" style="border: none;" width="256" height="256"></canvas>
</body>
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
0

You have two textures that you'd like to deal with, yet I see only one texture unit being used here. Perhaps if you bound your FBO texture to texture unit one using code like the following:

glBindFramebuffer(GL_FRAMEBUFFER, fboA);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureA);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureB);

or

glBindFramebuffer(GL_FRAMEBUFFER, fboB);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureB);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureA);

before you render each frame, it would properly read from the one texture bound to unit 0 and output via the FBO to the other texture on unit 1.

As an alternative, you could permanently bind one texture to one unit and the other to the other unit, and alternate values for your AUTOMATE_TEXT uniform to indicate which unit to pull from. This would be a little more efficient, because it would avoid the overhead of binding the textures on every render.

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
  • I don't think he wants to use both textures as input. He wants to use one as input and render into the other using an FBO, so I cannot see how binding both textures might be a solution to his problem. Even more so, binding a texture as input that is currently rendered into is most probably a bad idea. – Christian Rau Nov 28 '11 at 21:49
  • @ChristianRau - As I read it, he wants to ping-pong between textures (take texture 1 as input and render to texture 2, then take texture 2 as input after that and render to texture 1). Only one texture is read as input at a time. I did this once on an OpenGL ES device and had to have the input texture and output FBO-bound texture on separate texture units in order for it to work. Yes, you don't want to be reading from a texture that you're rendering to, but if you split this into separate draw events it should work. – Brad Larson Nov 28 '11 at 22:07
  • @BradLarson -- Maybe I didn't describe clearly. Actually, I want to use the textureB as the texture and render it to textureA, then use the textureA to render to screen, then vice versa. – Yongwei Xing Nov 29 '11 at 01:51
  • @BradLarson Do you mean I need bind both textures in the render loop? But how can I know which one is the input texture and which one is output texture. – Yongwei Xing Nov 29 '11 at 02:07
  • @YongweiXing - Yes, you can bind both textures, by binding one texture to a particular texture unit and the other texture to another. You specify which texture unit to pull from for your shader in the `glUniform1i(AUTOMATE_TEXT, 0);` line, which currently tells your shader to use the texture on unit 0 as input for that uniform. – Brad Larson Nov 30 '11 at 23:00