Search code examples
androidandroid-ndkframebufferstencil-bufferstencils

Using OpenGL ES 2.0 FrameBuffer (FBO) and Stencil in android Native code (ndk)


I am trying to generate a frambuffer object and use stencil inside a native android application using the NDK (r5b). Target device is running froyo 2.2, supporting OpenGL ES 2.0.

So, I've been coding lots of gl code in my c++ native libs and havent got through any problem except for this. I just can't seems to make it work.

Here's a code snipplet for the framebuffer creation. Completness is all good, but screen remains completly black. It's like the fbo I am creating is not really bound to the gl surface that is created by the Java part of the app. The rest of my app code is all good, if I remove the fbo creation and binding, everything works perfectly fine except that I don't have the stencils working which I need for my app.

    GLint backingWidth = 1024;
      GLint backingHeight = 1024;


    //Create the FrameBuffer and binds it
    glGenFramebuffers(1, &_defaultFramebuffer);
    checkGlError("glGenFramebuffers");
    glBindFramebuffer(GL_FRAMEBUFFER, _defaultFramebuffer);
    checkGlError("glBindFramebuffer");

    //Create the RenderBuffer for offscreen rendering // Color
    glGenRenderbuffers(1, &_colorRenderbuffer);
    checkGlError("glGenRenderbuffers color");
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
    checkGlError("glBindRenderbuffer color");
    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, backingWidth, backingHeight);
    checkGlError("glRenderbufferStorage color");

    //Create the RenderBuffer for offscreen rendering // Depth
    glGenRenderbuffers(1, &_depthRenderbuffer);
    checkGlError("glGenRenderbuffers depth");
    glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer);
    checkGlError("glBindRenderbuffer depth");
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, backingWidth, backingHeight);
    checkGlError("glRenderbufferStorage depth");

    //Create the RenderBuffer for offscreen rendering // Stencil
    glGenRenderbuffers(1, &_stencilRenderbuffer);
    checkGlError("glGenRenderbuffers stencil");
    glBindRenderbuffer(GL_RENDERBUFFER, _stencilRenderbuffer);
    checkGlError("glBindRenderbuffer stencil");
    glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, backingWidth, backingHeight);
    checkGlError("glRenderbufferStorage stencil");

    // bind renderbuffers to framebuffer object
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer);
    checkGlError("glFramebufferRenderbuffer depth");
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
    checkGlError("glFramebufferRenderbuffer color");
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _stencilRenderbuffer);
    checkGlError("glFramebufferRenderbuffer stencil");

//Test for FrameBuffer completeness
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    checkGlError("glCheckFramebufferStatus");
    switch (status)
    {
    case GL_FRAMEBUFFER_COMPLETE: LOGI("\n\n\nFLIPBOOM : FBO complete  GL_FRAMEBUFFER_COMPLETE %x\n\n\n", status);break;

    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: LOGI("\n\n\nFLIPBOOM : FBO GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT  %x\n\n\n", status);break;

    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: LOGI("\n\n\nFLIPBOOM : FBO FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT  %x\n\n\n", status);break;

    case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: LOGI("\n\n\nFLIPBOOM : FBO FRAMEBUFFER_INCOMPLETE_DIMENSIONS  %x\n\n\n", status);break;

    case GL_FRAMEBUFFER_UNSUPPORTED: LOGI("\n\n\nFLIPBOOM : FBO GL_FRAMEBUFFER_UNSUPPORTED  %x\n\n\n", status);break;

    default : LOGI("\n\n\nFLIPBOOM : failed to make complete framebuffer object %x\n\n\n", status);
    }

I've also tried rendering to a 2D texture instead of the renderbuffer...didn't worked either.

So, Is there a way I can fix this ? Am I getting something wrong here ? If anyone has any ideas please lemme know....been spending way too much time looking up this problem...hehe ;)

Thanks in advance !

Cheers !


EDIT :

Ok, I've manage to make the stencil buffer work but the FBO are just not working. I think OpenGL ES 2.0 is not fully supported by android (using r5b here btw). I think method stubs are defined, but not fully implemented. Or the GlSurfaceView created doesn't link correctly with the FBOs.

As for the stencil buffer, I had to do

glEnable(GL_DEPTH_TEST);

and remove the usage of glDepthMask in order for them to work correctly.


Solution

  • @ Zennichimaro, For the stencil buffer usage !

    During Initialisation :

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glEnable(GL_DEPTH_TEST);
    

    During the rendering :

    glViewport(0, 0, GetViewWidth(), GetViewHeight());
    checkGlError("glViewport");
    
    if (_firstRenderDone == false)
    {
        glClearDepthf( 0.9f );
        glDepthMask( GL_TRUE );
        glClear( GL_DEPTH_BUFFER_BIT );
        glDepthMask( GL_FALSE );
        _firstRenderDone = true;
    }
    
    glClearColor(M_channelToFloat(_backgroundColor.r),
                   M_channelToFloat(_backgroundColor.g),
                   M_channelToFloat(_backgroundColor.b),
                   M_channelToFloat(_backgroundColor.a));
    checkGlError("glClearColor");
    glClearStencil( 0 );
    checkGlError("glClearStencil");
    glClear( GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
    checkGlError("glClear");
    
    _stencilLayer = 1;
    
    //use our custom shaders
    if( _program )
    {
        glUseProgram(_program);
    
    
        if( transformMatrix3x3 != NULL )
        {
            glUniformMatrix3fv( _uniforms[OGL_UNIFORM_TRANSFORM], 1, false, transformMatrix3x3 );
        }
    
         // reset the shading.
        glUniform1f( _uniforms[ OGL_UNIFORM_SHADE ], 0.0f );
    }
    
    
    //Do the actual drawing (Triangle Slip)
     if( object )
      {
         _isRender = true;
        object->OglDraw(this);
        _isRender = false;
      }
    

    When I need to use stencil I use the following methods depending on what I need :

    void GlEs2Renderer::StencilStartMask()
    {
    if (!USE_STENCIL)   //For debugging purpose
        return;
    
    
    glEnable(GL_STENCIL_TEST);
    
    //Turn off writing to the Color Buffer and Depth Buffer
    //We want to draw to the Stencil Buffer only
    glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
    
    //Set 1 into the stencil buffer
    glStencilFunc( GL_ALWAYS, NewStencilLayer(), 0xFFFFFFFF );
    glStencilOp( GL_ZERO, GL_ZERO, GL_REPLACE );
    }
    
    void GlEs2Renderer::StencilUseMask()
    {
    if (!USE_STENCIL)   //For debugging purpose
            return;
    
    //Turn back on Color Buffer and Depth Buffer
    glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
    
    //Only write to the Stencil Buffer where 1 is set
    glStencilFunc( GL_EQUAL, StencilLayer(), 0xFFFFFFFF);
    
    //Keep the content of the Stencil Buffer
    glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
    }
    
    void GlEs2Renderer::StencilOverlayMask()
    {
    if (!USE_STENCIL)   //For debugging purpose
            return;
    
    //Turn back on Color Buffer and Depth Buffer
    glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
    glDepthMask(true);
    
    //Only write to the Stencil Buffer where 1 is set
    glStencilFunc( GL_EQUAL, StencilLayer(), 0xFFFFFFFF);
    
    //Keep the content of the Stencil Buffer and increase when z passed
    glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
    }
    

    And Finally I do the double pass technic to draw inside the stencil ... Here's an example :

    glVertexAttribPointer(OGL_ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, _triangles);
    glEnableVertexAttribArray(OGL_ATTRIB_VERTEX);
    glVertexAttribPointer(OGL_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, _colors);
    glEnableVertexAttribArray(OGL_ATTRIB_COLOR);
    
    glContext->StencilStartMask();
    glDrawArrays(GL_TRIANGLE_STRIP, 0, _nPoints);
    
    glContext->StencilUseMask();;
    glDrawArrays(GL_TRIANGLE_STRIP, 0, _nPoints);
    
    glContext->StencilEndMask();
    

    My code is fairly complex so it's hard to only post what's related to the stencil, But I hope It'll help ;)