예제 #1
0
// Renders the result of the "MotionBlur" sample for comparison purposes.
// NOTE: this cannot show fullscreen motion blur.
void MotionBlur::renderSceneBlurred(const nv::matrix4f& houseXform,
                                    const nv::matrix4f& sailsXform,
                                    const nv::matrix4f& prevSailsXform) const
{    
    // Render to color/depth FBO
    glBindFramebuffer(GL_FRAMEBUFFER, mFboID);
    glViewport(0, 0, NvSampleApp::m_width, NvSampleApp::m_height);

    // Black
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    drawModelUnblurred(mSailsModel, sailsXform);

    // Pass 2: Render the motion blurred moving geometry over static geometry.
    // Render the Static geometry's depth, Use pass2 (color texture) as motion
    // blur lookup and write onto Pass1 (color texture).
    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());
    glViewport(0, 0, NvSampleApp::m_width, NvSampleApp::m_height);

    // Yellow, high-contrast if we're not showing the sky box.
    glClearColor(1.0f, 1.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    drawModelUnblurred(mHouseModel, houseXform);
    glDepthMask(false);
    drawSkybox();
    drawModelBlurred(mSailsModel, sailsXform, prevSailsXform);
    glDepthMask(true);
}
예제 #2
0
bool FXAA::genRenderTexture()
{
    int width = getGLContext()->width();
    int height = getGLContext()->height();

    glGenFramebuffers(1, &m_sourceFrameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, m_sourceFrameBuffer);

    glGenTextures(1, &m_sourceTexture);
    glBindTexture(GL_TEXTURE_2D, m_sourceTexture);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(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, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_sourceTexture, 0);

    glGenRenderbuffers(1, &m_sourceDepthBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, m_sourceDepthBuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_sourceDepthBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);

    // Finalize
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        return false;
    }
    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());
    return true;
}
void DeferredShadingMSAA::PerformSinglePassLighting()
{
    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());

    glDisable(GL_DEPTH_TEST);

    m_shaderLighting->enable();
    // Pass our current matrices to the shader
    m_shaderLighting->setUniformMatrix4fv("uProjViewMatrix", inverse(m_proj)._array);
    m_shaderLighting->setUniformMatrix4fv("uViewWorldMatrix", inverse(m_transformer->getModelViewMat())._array);

    // Pass all of the parameters that define our lighting and MSAA mode to the shader
    m_shaderLighting->setUniform1i("uSeparateEdgePass", m_bSeparateComplexPass);
    m_shaderLighting->setUniform1i("uSecondPass", GL_FALSE);
    m_shaderLighting->setUniform1i("uUseDiscontinuity", (m_approach == APPROACH_DISCONTINUITY));
    m_shaderLighting->setUniform1i("uAdaptiveShading", m_bAdaptiveShading);
    m_shaderLighting->setUniform1i("uShowComplexPixels", m_bMarkComplexPixels);
    m_shaderLighting->setUniform1i("uLightingModel", m_brdf);
    m_shaderLighting->setUniform1i("uMSAACount", m_iMSAACount);
    m_shaderLighting->setUniform1i("uSampleMask", (1 << m_iMSAACount) - 1);

    // Use our G-Buffer as source textures
    m_shaderLighting->bindTexture2DMultisample("uTexGBuffer1", 0, m_texGBuffer[0]);
    m_shaderLighting->bindTexture2DMultisample("uTexGBuffer2", 1, m_texGBuffer[1]);
    m_shaderLighting->bindTexture2DMultisample("uTexCoverage", 2, m_texGBuffer[2]);

    // Provide the resolved non-MSAA color buffer so the shader can use it for certain lighting modes
    m_shaderLighting->bindTextureRect("uResolvedColorBuffer", 3, m_texGBufResolved2);

    RenderFullscreenQuad(m_shaderLighting);

    m_shaderLighting->disable();
}
예제 #4
0
// Additional rendering setup methods
void MotionBlur::freeGLBindings(void) const
{
    glBindFramebuffer(GL_FRAMEBUFFER,     getMainFBO());
    glBindRenderbuffer(GL_RENDERBUFFER,   0);
    glBindBuffer(GL_ARRAY_BUFFER,         0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindTexture(GL_TEXTURE_2D,          0);
    glBindTexture(GL_TEXTURE_CUBE_MAP,    0);
    CHECK_GL_ERROR();
}
예제 #5
0
void XBScene1::setup(XBBaseGUI *_gui)
{
    XBBaseScene::setup(_gui);

    wavesMask.allocate(ofGetWidth(), ofGetHeight(), GL_RGB);
    wavesMask.begin();
    ofSetBackgroundAuto(false);
    ofBackground(0, 0, 0);
    wavesMask.end();

    initWindows();
    initParticles();
    initWaves();
    initStones();
    initLines();

    blur.setup(getMainFBO().getWidth(), getMainFBO().getHeight(), 0);
    XBScene1GUI *myGUI = (XBScene1GUI *) gui;
    myGUI->flashDirector.getParameter().cast<bool>().addListener(this, &XBScene1::flashDirector);
}
void DeferredShadingMSAA::reshape(int32_t width, int32_t height)
{
    // Avoid destroying render targets unnecessarily.  Only do it if our dimensions have actually changed.
    if (m_imageWidth != width || m_imageHeight != height)
    {
        m_imageWidth = width;
        m_imageHeight = height;

        // Destroy the current render targets and let them get recreated on our next draw
        DestroyRenderTargets();
    }

    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());

    glViewport(0, 0, m_imageWidth, m_imageHeight);
}
예제 #7
0
void MotionBlur::initFBO(void)
{
    // *** Clean up (in case of a reshape)
    cleanFBO();

    // *** TEXTURES (for FBO attachments)

    glGenTextures(1, &mTargetTexID);
    glBindTexture(GL_TEXTURE_2D, mTargetTexID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                 NvSampleApp::m_width, NvSampleApp::m_height, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, NULL);
    CHECK_GL_ERROR();

    glBindTexture(GL_TEXTURE_2D, 0);

    // *** RENDERBUFFERS (for FBO attachments)

    glGenRenderbuffers(1, &mRboID);
    glBindRenderbuffer(GL_RENDERBUFFER, mRboID);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
                          NvSampleApp::m_width, NvSampleApp::m_height);
    CHECK_GL_ERROR();

    glBindRenderbuffer(GL_RENDERBUFFER, 0);

    // *** FRAMEBUFFER OBJECTS (for individual pass)

    glGenFramebuffers(1, &mFboID);
    glBindFramebuffer(GL_FRAMEBUFFER, mFboID);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                           GL_TEXTURE_2D, mTargetTexID, 0);
    CHECK_GL_ERROR();
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                             GL_RENDERBUFFER, mRboID);
    CHECK_GL_ERROR();
    GLint st = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    LOGI("Frame Buffer Status : %d", st);
    if(st == GL_FRAMEBUFFER_COMPLETE)
        LOGI("GL_FRAMEBUFFER_COMPLETE");

    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());
}
void DeferredShadingMSAA::initRendering(void)
{
    NvAssetLoaderAddSearchPath("gl4-kepler/DeferredShadingMSAA");
    NvAssetLoaderAddSearchPath("gl4-kepler/DeferredShadingMSAA/models");

    if(!requireMinAPIVersion(NvGLAPIVersionGL4(), true))
        return;

    // Require at least one of these multisample extensions
    if (!requireExtension("GL_NV_framebuffer_multisample", false))
    {
        if (!requireExtension("GL_ARB_multisample", true))
            return;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());

    BuildShaders();

    // As a demo, we'll use both methods of loading meshes, and time each of them,
    // to illustrate the differences
    NvStopWatch* pStopwatch = createStopWatch();

    pStopwatch->start();
    m_models[SPONZA_MODEL] = LoadModel("models/sponza.obj");
    pStopwatch->stop();
    float time = pStopwatch->getTime();
    LOGI("Standard Model Loading Complete: %f seconds", time);

    pStopwatch->start();
    m_modelsExt[SPONZA_MODEL] = LoadModelExt("models/sponza.obj");
    pStopwatch->stop();
    time = pStopwatch->getTime();
    LOGI("Extended Model Loading Complete: %f seconds", time);

    m_modelID = SPONZA_MODEL;

    glDisable(GL_CULL_FACE);
    glGenQueries(1, &m_queryId);

#if ENABLE_GPU_TIMERS
    for (uint32_t i = 0; i < TIMER_COUNT; ++i)
    {
        m_timers[i].init();
    }
#endif
}
예제 #9
0
// Renders the color buffer for the scene and skybox
void MotionBlur::renderSceneUnblurred(const nv::matrix4f& houseXform,
                                      const nv::matrix4f& sailsXform) const
{
    // Render to framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());
    glViewport(0, 0, NvSampleApp::m_width, NvSampleApp::m_height);

    // Yellow, high-contrast if we're not showing the sky box.
    glClearColor(1.0f, 1.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    drawModelUnblurred(mSailsModel, sailsXform);
    drawModelUnblurred(mHouseModel, houseXform);
    glDepthMask(false);
    drawSkybox();
    glDepthMask(true);
}
예제 #10
0
void FXAA::draw(void)
{
    nv::matrix4f rotation;
    nv::matrix4f model;
    nv::matrix4f mvp;
    float color0[4]= {0.5,0.5,0.8,1.0};
    float color1[4]= {0.5,0.9,0.5,1.0};

    m_transformer->setRotationVel(nv::vec3f(0.0f, m_autoSpin ? (NV_PI*0.05f) : 0.0f, 0.0f));

    glBindFramebuffer(GL_FRAMEBUFFER, m_sourceFrameBuffer);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLineWidth(m_lineWidth);

    //draw lined sphere
    nv::matrix4f vp = m_projection_matrix * m_transformer->getModelViewMat();
    nv::rotationX(rotation, float(0.2));
    mvp = m_projection_matrix * m_transformer->getModelViewMat() * rotation;
    drawSphere(&mvp);

    //draw rings
    model.make_identity();
    model.set_scale(0.04);
    nv::rotationX(rotation, float(0.1));
    mvp = m_projection_matrix * m_transformer->getModelViewMat() * rotation* model;
    drawObjects(&mvp, color0, 0);

    model.make_identity();
    model.set_scale(0.04);
    nv::rotationX(rotation, float(NV_PI/2+0.1));
    mvp = m_projection_matrix * m_transformer->getModelViewMat() * rotation * model;
    drawObjects(&mvp, color0, 0);

    //draw triangle
    model.make_identity();
    model.set_scale(0.03);
    model.set_translate(nv::vec3f(0.0f, -0.3f, 0.0f));
    mvp = m_projection_matrix * m_transformer->getModelViewMat() * model;
    drawObjects(&mvp, color1, 1);

    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());
    FXAABlit(m_FXAALevel);
    return;
}
void DeferredShadingMSAA::PerformSinglePassLighting_NoMSAA()
{
    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_STENCIL_TEST);

    m_shaderLighting_NoMSAA->enable();
    m_shaderLighting_NoMSAA->setUniformMatrix4fv("uProjViewMatrix", inverse(m_proj)._array);
    m_shaderLighting_NoMSAA->setUniformMatrix4fv("uViewWorldMatrix", inverse(m_transformer->getModelViewMat())._array);

    m_shaderLighting_NoMSAA->setUniform1i("uLightingModel", m_brdf);
    m_shaderLighting_NoMSAA->bindTextureRect("uTexGBuffer1", 0, m_texGBuffer[0]);
    m_shaderLighting_NoMSAA->bindTextureRect("uTexGBuffer2", 1, m_texGBuffer[1]);

    RenderFullscreenQuad(m_shaderLighting_NoMSAA);

    m_shaderLighting_NoMSAA->disable();
}
void DeferredShadingMSAA::draw(void)
{
    nv::perspective(m_proj, 45.0f, (GLfloat)m_width / (GLfloat)m_height, 0.1f, 100.0f);
    m_MVP = m_proj * m_transformer->getModelViewMat();
    m_normalMat = m_transformer->getRotationMat();

    if (!UpdateRenderTargetMode())
    {
        // We weren't able to update our render targets properly, so 
        // there's no reason to render anything.
        return;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());

    glClearDepth(1.0f);
    glClearStencil(0);
    glStencilMask(0xFF);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    // Render scene geometry to G-Buffer
    if (m_currentRTMode == RTMODE_NOMSAA)
    {
        RenderToGBuffer_NoMSAA();
    }
    else
    { 
        RenderToGBuffer();
    }

    // Resolve G-Buffer if we're using a technique that requires it
    if (m_approach == APPROACH_COVERAGEMASK)
    {
        ResolveMSAAGBuffer();
    }

    if (m_currentRTMode == RTMODE_NOMSAA)
    {
        PerformSinglePassLighting_NoMSAA();
    }
    else if (m_bSeparateComplexPass)
    {
        // Since we are doing two passes, we need a stencil buffer to identify 
        // which pixels to process as complex (edge) pixels
        FillStencilBuffer();

        if (m_bUsePerSamplePixelShader)
        {
            PerformDualPassLighting_PerSample();
        }
        else
        {
            PerformDualPassLighting_PerPixel();
        }
    }
    else
    {
        PerformSinglePassLighting();
    }

#if ENABLE_GPU_TIMERS
    if (m_statsText)
    {
        static float gpuTimesMS[TIMER_COUNT] = { 0.f };
        for (int32_t i = 0; i < TIMER_COUNT; ++i)
        {
            if (m_timers[i].getStartStopCycles() >= NUM_FRAMES_PER_GPU_TIME)
            {
                gpuTimesMS[i] = m_timers[i].getScaledCycles() / m_timers[i].getStartStopCycles();
                m_timers[i].reset();
            }
        }

        std::ostringstream s;
        s << "geometry: " << std::fixed << std::setprecision(2) << gpuTimesMS[TIMER_GEOMETRY] << " ms" << std::endl;
        s << "compositing: " << std::fixed << std::setprecision(2) << gpuTimesMS[TIMER_COMPOSITING] << " ms" << std::endl;
        m_statsText->SetString(s.str().c_str());
    }
#endif
}
void DeferredShadingMSAA::PerformDualPassLighting_PerSample()
{
    glBindFramebuffer(GL_FRAMEBUFFER, m_texOffscreenMSAAFboId);

    glDisable(GL_DEPTH_TEST);

    m_shaderLighting->enable();
    // Pass our current matrices to the shader
    m_shaderLighting->setUniformMatrix4fv("uProjViewMatrix", inverse(m_proj)._array);
    m_shaderLighting->setUniformMatrix4fv("uViewWorldMatrix", inverse(m_transformer->getModelViewMat())._array);

    // Pass all of the parameters that define our lighting and MSAA mode to the shader
    m_shaderLighting->setUniform1i("uSeparateEdgePass", m_bSeparateComplexPass);
    m_shaderLighting->setUniform1i("uSecondPass", GL_FALSE);
    m_shaderLighting->setUniform1i("uUseDiscontinuity", (m_approach == APPROACH_DISCONTINUITY));
    m_shaderLighting->setUniform1i("uAdaptiveShading", m_bAdaptiveShading);
    m_shaderLighting->setUniform1i("uShowComplexPixels", m_bMarkComplexPixels);
    m_shaderLighting->setUniform1i("uLightingModel", m_brdf);
    m_shaderLighting->setUniform1i("uMSAACount", m_iMSAACount);
    m_shaderLighting->setUniform1i("uSampleMask", (1 << m_iMSAACount) - 1);

    // Use our G-Buffer as source textures
    m_shaderLighting->bindTexture2DMultisample("uTexGBuffer1", 0, m_texGBuffer[0]);
    m_shaderLighting->bindTexture2DMultisample("uTexGBuffer2", 1, m_texGBuffer[1]);
    m_shaderLighting->bindTexture2DMultisample("uTexCoverage", 2, m_texGBuffer[2]);

    // Provide the resolved non-MSAA color buffer so the shader can use it for certain lighting modes
    m_shaderLighting->bindTextureRect("uResolvedColorBuffer", 3, m_texGBufResolved2);

    // Render First (Simple Pixel) Pass
    glEnable(GL_STENCIL_TEST);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glStencilFunc(GL_EQUAL, 1, 0xFF);
    glStencilMask(0x00); // Disable writing to the stencil mask

    RenderFullscreenQuad(m_shaderLighting);

    m_shaderLighting->disable();

    // Render Second (Complex Pixel) Pass
    glStencilFunc(GL_EQUAL, 0, 0xFF);

    if (m_bMarkComplexPixels)
    {
        m_shaderLightingMarkComplex->enable();
        RenderFullscreenQuad(m_shaderLightingMarkComplex);
        m_shaderLightingMarkComplex->disable();
    }
    else
    {
        m_shaderLightingPerSample->enable();
        m_shaderLightingPerSample->setUniformMatrix4fv("uProjViewMatrix", inverse(m_proj)._array);
        m_shaderLightingPerSample->setUniformMatrix4fv("uViewWorldMatrix", inverse(m_transformer->getModelViewMat())._array);
        m_shaderLightingPerSample->setUniform1i("uSecondPass", GL_TRUE);
        m_shaderLightingPerSample->setUniform1i("uLightingModel", m_brdf);

        m_shaderLightingPerSample->bindTexture2DMultisample("uTexGBuffer1", 0, m_texGBuffer[0]);
        m_shaderLightingPerSample->bindTexture2DMultisample("uTexGBuffer2", 1, m_texGBuffer[1]);
        m_shaderLightingPerSample->bindTexture2DMultisample("uTexCoverage", 2, m_texGBuffer[2]);
        m_shaderLightingPerSample->bindTextureRect("uResolvedColorBuffer", 3, m_texGBufResolved2);
        
        glEnable(GL_SAMPLE_SHADING);
        RenderFullscreenQuad(m_shaderLightingPerSample);
        glDisable(GL_SAMPLE_SHADING);

        m_shaderLightingPerSample->disable();
    }

    // Now resolve from our off-screen MSAA lighting accumulation buffer to our final, presentable framebuffer
    glBindFramebuffer(GL_READ_FRAMEBUFFER, m_texOffscreenMSAAFboId);
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getMainFBO());

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_STENCIL_TEST);
    glBlitFramebuffer(0, 0, m_imageWidth, m_imageWidth, 0, 0, m_imageWidth, m_imageWidth, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
예제 #14
0
void NormalBlendedDecal::draw(void)
{
    if ((m_width == 0) || (m_height == 0)) return;

    nv::matrix4f proj;
    nv::perspective(proj, 45.0f, (GLfloat)m_width / (GLfloat)m_height, 0.01f, 100.0f);

    float uScreenSize[] = { (float)m_width, (float)m_height, 0, 0 };
    static const GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };

    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);

    // Draw world geometries into gbuffer
    {
        glBindFramebuffer(GL_FRAMEBUFFER, mGbufferFBO);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        // Enable all color buffers
        glDrawBuffers(3, drawBuffers);

        // Clear render targets
        static const GLfloat floatZeroes[] = { 0.0f, 0.0f, 0.0f, 0.0f };
        static const GLfloat floatOnes[] = { 1.0f, 1.0f, 1.0f, 1.0f };
        glClearBufferfv(GL_COLOR, 0, floatZeroes);
        glClearBufferfv(GL_COLOR, 1, floatZeroes);
        glClearBufferfv(GL_COLOR, 2, floatZeroes);
        glClearBufferfv(GL_DEPTH, 0, floatOnes);
        
        // Enable GBuffer program
        mGbufferProgram->enable();

        // Draw scene objects
        for (unsigned int j = 0; j < mSqrtNumModels; j++) {
            for (unsigned int i = 0; i < mSqrtNumModels; i++) {
                // Model transform
                nv::matrix4f matModel;
                matModel.set_scale(1.0f);
                matModel.set_translate(nv::vec3f(
                    .5f*mModelDistance*(mSqrtNumModels - 1.0f) - mModelDistance*i, 
                    .5f*mModelDistance*(mSqrtNumModels - 1.0f) - mModelDistance*j, 0.0f));

                // Model view transform
                nv::matrix4f matMV = m_transformer->getModelViewMat() * matModel;

                // Movel view projection transform
                nv::matrix4f matMVP = proj * matMV;

                // Set uniforms
                mGbufferProgram->setUniformMatrix4fv("uModelViewMatrix", matMV._array);
                mGbufferProgram->setUniformMatrix4fv("uModelViewProjMatrix", matMVP._array);
                mGbufferProgram->setUniformMatrix4fv("uModelMatrix", matModel._array);

                // Draw model
                mModels[mModelID]->drawElements(0, 1, 2, 3);
            }
        }
    }

    // Draw decal box into gbuffer
    {
        glBindFramebuffer(GL_FRAMEBUFFER, mGbufferDecalFBO);
        
        // Enable only color buffer
        glDrawBuffers(1, drawBuffers);
      
        if (mViewOption == VIEW_DECAL_BOXES) {
            glDisable(GL_DEPTH_TEST);
            glEnable(GL_BLEND);
            //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glBlendFunc(GL_ONE, GL_ONE);
        }

        mDecalProgram->enable();

        // uNormalImage
        glBindImageTexture(0, mGbufferTextures[NORMAL_TEXTURE], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA16F);

        // uWorldPosTex
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mGbufferTextures[WORLDPOS_TEXTURE]);

        mDecalProgram->setUniform4fv("uScreenSize", uScreenSize);
        mDecalProgram->setUniform1i("uShowDecalBox", (mViewOption == VIEW_DECAL_BOXES) ? true : false);

        float gridSize = mModelDistance*(mSqrtNumModels - 1.0f);

        for (unsigned int idx = 0; idx < mNumDecals; idx++) {
            nv::matrix4f matDecal;
            matDecal.make_identity();
            matDecal.set_scale(nv::vec3f(mDecalSize, mDecalSize, 2.0f));
            matDecal.set_translate(nv::vec3f(gridSize*mDecalDistance*mDecalPos[idx].x,
                gridSize*mDecalDistance*mDecalPos[idx].y, 0));
            nv::matrix4f matMVP = proj * m_transformer->getModelViewMat() * matDecal;
            nv::matrix4f invMVP = inverse(matMVP);
            nv::matrix4f invModel = inverse(matDecal);
            mDecalProgram->setUniformMatrix4fv("uModelMatrix", matDecal._array);
            mDecalProgram->setUniformMatrix4fv("uInvModelMatrix", invModel._array);
            mDecalProgram->setUniformMatrix4fv("uModelViewProjMatrix", matMVP._array);
            mDecalProgram->setUniformMatrix4fv("uViewMatrix", m_transformer->getModelViewMat()._array);

            float weight = mBlendWeight;
            for (int blendIdx = 0; blendIdx < NUM_DECAL_BLENDS; ++blendIdx) {
                // uDecalNormalTex
                glActiveTexture(GL_TEXTURE2);
                glBindTexture(GL_TEXTURE_2D, mDecalNormals[blendIdx]);

                mDecalProgram->setUniform1f("uBlendWeight", weight);
                weight = (blendIdx == (NUM_DECAL_BLENDS - 2)) ? (1.0f - weight) : weight*mBlendWeight;

                // Draw decal cube
                mCube->drawElements(0, 1, 2, 3);

                if (mLock == LOCK_MEMORY_BARRIER) {
                    glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
                }
            }
        }

        // Restore states
        glEnable(GL_DEPTH_TEST);
        glDisable(GL_BLEND);
    }

    // Draw combiner pass
    {
        // Back to main FBO
        glBindFramebuffer(GL_FRAMEBUFFER, getMainFBO());

        // Disable depth testing
        glDisable(GL_DEPTH_TEST);

        // Enable combiner program
        mCombinerProgram->enable();

        // uColorTex
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mGbufferTextures[COLOR_TEXTURE]);

        // uNormalTex
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, mGbufferTextures[NORMAL_TEXTURE]);

        // Set decal box option
        mCombinerProgram->setUniform1i("uShowDecalBox", (mViewOption == VIEW_DECAL_BOXES) ? true : false);

        // Set decal box option
        mCombinerProgram->setUniform1i("uShowNormals", (mViewOption == VIEW_NORMALS) ? true : false);

        // Set light direction
        nv::matrix4f invViewMatrix = inverse(m_transformer->getModelViewMat());
        nv::vec4f lightDir = invViewMatrix * nv::vec4f(0, 0, 1.0f, 0);
        lightDir = normalize(lightDir);
        mCombinerProgram->setUniform4fv("uLightDir", lightDir);

        // Set view direction
        nv::vec4f viewDir = invViewMatrix * nv::vec4f(0, 0, -1.0f, 0);
        viewDir = normalize(viewDir);
        mCombinerProgram->setUniform4fv("uViewDir", viewDir);

        // Draw combiner program with screen quad
        draw2d(0, mCombinerProgram);
        
        // Restore states
        glEnable(GL_DEPTH_TEST);
    }
}