void HBAODrawCallback::operator()( osg::RenderInfo& renderInfo ) const
{
    osg::State& state = *(renderInfo.getState());
    if ( !_aoContext )
    {
        // Initialize AO
        HBAODrawCallback* nonconst = const_cast<HBAODrawCallback*>( this );
        if ( !nonconst->initializeContext(renderInfo) ) return;
    }
    
    // Handle scene elements
    osg::Matrixf projMatrix = state.getProjectionMatrix();
    osg::Matrixf viewMatrix = state.getModelViewMatrix();
    if ( !_depthTexture || !_outputTexture ) return;
    
    unsigned int contextID = renderInfo.getContextID();
    GLuint fboID = _fbo->getHandle(contextID);
    if ( _dirtyFBO )
    {
        _fbo->setAttachment( osg::Camera::COLOR_BUFFER,
                             osg::FrameBufferAttachment(_outputTexture.get()) );
        _dirtyFBO = false;
    }
    
    if ( fboID==0 )
    {
        _fbo->apply( state );
        fboID = _fbo->getHandle(contextID);
    }
    
    osg::Texture::TextureObject* depthObj = _depthTexture->getTextureObject(contextID);
    osg::Texture::TextureObject* normalObj =
        _normalTexture.valid() ? _normalTexture->getTextureObject(contextID) : NULL;
    if ( fboID==0 || !depthObj ) return;
    
    // Render AO
    GFSDK_SSAO_Parameters_GL aoParams;
    aoParams.Radius = 2.0f;
    aoParams.Bias = 0.2f;
    aoParams.DetailAO = 1.0f;
    aoParams.CoarseAO = 1.0f;
    aoParams.DepthStorage = GFSDK_SSAO_FP32_VIEW_DEPTHS;//GFSDK_SSAO_FP16_VIEW_DEPTHS;
    aoParams.PowerExponent = 10.0f;
    aoParams.Output.BlendMode = GFSDK_SSAO_OVERWRITE_RGB;
    aoParams.Blur.Enable = true;
    aoParams.Blur.Sharpness = 4.0f;
    aoParams.Blur.Radius = GFSDK_SSAO_BLUR_RADIUS_2;//GFSDK_SSAO_BLUR_RADIUS_4;//GFSDK_SSAO_BLUR_RADIUS_8;
    
    GFSDK_SSAO_InputData_GL input;
    input.DepthData.DepthTextureType = GFSDK_SSAO_HARDWARE_DEPTHS;
    input.DepthData.FullResDepthTexture = GFSDK_SSAO_Texture_GL(GL_TEXTURE_2D, depthObj->id());
    input.DepthData.ProjectionMatrix.Data = GFSDK_SSAO_Float4x4(projMatrix.ptr());
    input.DepthData.ProjectionMatrix.Layout = GFSDK_SSAO_ROW_MAJOR_ORDER;
    input.DepthData.MetersToViewSpaceUnits = 1.0f;
    if ( normalObj )
    {
        input.NormalData.Enable = true;
        input.NormalData.FullResNormalTexture = GFSDK_SSAO_Texture_GL(GL_TEXTURE_2D, normalObj->id());
        input.NormalData.WorldToViewMatrix.Data = GFSDK_SSAO_Float4x4(viewMatrix.ptr());
        input.NormalData.WorldToViewMatrix.Layout = GFSDK_SSAO_ROW_MAJOR_ORDER;
        input.NormalData.DecodeScale = 2.0f;
        input.NormalData.DecodeBias = -1.0f;
    }
    
    GFSDK_SSAO_RenderMask renderMask = GFSDK_SSAO_RENDER_AO;//GFSDK_SSAO_RENDER_DEBUG_NORMAL;
    glDisable( GL_DEPTH_TEST );
    
    GFSDK_SSAO_Status status = _aoContext->RenderAO( &input, &aoParams, fboID, renderMask );
    if ( status!=GFSDK_SSAO_OK )
    {
        OSG_WARN << "[HBAODrawCallback] Failed to render AO: " << status << std::endl;
    }
}