Beispiel #1
0
    void ChunkRenderer::renderChunkLocally(const Chunk& chunk, const RenderData& data) {
        
        ProgramObject* programObject = getActivatedProgramWithTileData(
            _localRenderingShaderProvider.get(),
            _localProgramUniformHandler,
            chunk);
        if (programObject == nullptr) {
            return;
        }

        using namespace glm;

        const Ellipsoid& ellipsoid = chunk.owner()->ellipsoid();


        bool performAnyBlending = false;
        for (int i = 0; i < LayeredTextures::NUM_TEXTURE_CATEGORIES; ++i) {
            LayeredTextures::TextureCategory category = (LayeredTextures::TextureCategory)i;
            if (_tileProviderManager->getTileProviderGroup(i).levelBlendingEnabled && _tileProviderManager->getTileProviderGroup(category).getActiveTileProviders().size() > 0) {
                performAnyBlending = true;
                break;
            }
        }
        if (performAnyBlending) {
            float distanceScaleFactor = chunk.owner()->lodScaleFactor * chunk.owner()->ellipsoid().minimumRadius();
            programObject->setUniform("distanceScaleFactor", distanceScaleFactor);
            programObject->setUniform("chunkLevel", chunk.index().level);
        }

        // Calculate other uniform variables needed for rendering
        dmat4 modelTransform = chunk.owner()->modelTransform();
        dmat4 viewTransform = data.camera.combinedViewMatrix();
        dmat4 modelViewTransform = viewTransform * modelTransform;

        std::vector<std::string> cornerNames = { "p01", "p11", "p00", "p10" };
        std::vector<Vec3> cornersCameraSpace(4);
        for (int i = 0; i < 4; ++i) {
            Quad q = (Quad)i;
            Geodetic2 corner = chunk.surfacePatch().getCorner(q);
            Vec3 cornerModelSpace = ellipsoid.cartesianSurfacePosition(corner);
            Vec3 cornerCameraSpace = Vec3(dmat4(modelViewTransform) * glm::dvec4(cornerModelSpace, 1));
            cornersCameraSpace[i] = cornerCameraSpace;
            programObject->setUniform(cornerNames[i], vec3(cornerCameraSpace));
        }

        vec3 patchNormalCameraSpace = normalize(
            cross(cornersCameraSpace[Quad::SOUTH_EAST] - cornersCameraSpace[Quad::SOUTH_WEST],
                cornersCameraSpace[Quad::NORTH_EAST] - cornersCameraSpace[Quad::SOUTH_WEST]));

        programObject->setUniform("patchNormalCameraSpace", patchNormalCameraSpace);
        programObject->setUniform("projectionTransform", data.camera.projectionMatrix());

        if (_tileProviderManager->getTileProviderGroup(
                LayeredTextures::NightTextures).getActiveTileProviders().size() > 0 ||
            _tileProviderManager->getTileProviderGroup(
                LayeredTextures::WaterMasks).getActiveTileProviders().size() > 0) {
            glm::vec3 directionToSunWorldSpace =
                glm::normalize(-data.modelTransform.translation);
            glm::vec3 directionToSunCameraSpace =
                (viewTransform * glm::dvec4(directionToSunWorldSpace, 0));
            data.modelTransform.translation;
            programObject->setUniform("lightDirectionCameraSpace", -directionToSunCameraSpace);
        }


        // OpenGL rendering settings
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);

        // render
        _grid->geometry().drawUsingActiveProgram();

        // disable shader
        programObject->deactivate();
    }
Beispiel #2
0
    ProgramObject* ChunkRenderer::getActivatedProgramWithTileData(
        LayeredTextureShaderProvider* layeredTextureShaderProvider,
        std::shared_ptr<LayeredTextureShaderUniformIdHandler> programUniformHandler,
        const Chunk& chunk)
    {
        const ChunkIndex& chunkIndex = chunk.index();

        std::array<std::vector<std::shared_ptr<TileProvider> >,
            LayeredTextures::NUM_TEXTURE_CATEGORIES> tileProviders;
        LayeredTexturePreprocessingData layeredTexturePreprocessingData;

        for (size_t category = 0; category < LayeredTextures::NUM_TEXTURE_CATEGORIES; category++) {
            tileProviders[category] = _tileProviderManager->getTileProviderGroup(category).getActiveTileProviders();

            LayeredTextureInfo layeredTextureInfo;
            layeredTextureInfo.lastLayerIdx = tileProviders[category].size() - 1;
            layeredTextureInfo.layerBlendingEnabled = _tileProviderManager->getTileProviderGroup(category).levelBlendingEnabled;

            layeredTexturePreprocessingData.layeredTextureInfo[category] = layeredTextureInfo;
        }

        layeredTexturePreprocessingData.keyValuePairs.push_back(
            std::pair<std::string, std::string>(
                "useAtmosphere",
                std::to_string(chunk.owner()->atmosphereEnabled)));

        layeredTexturePreprocessingData.keyValuePairs.push_back(
            std::pair<std::string, std::string>(
                "showChunkEdges",
                std::to_string(chunk.owner()->debugOptions.showChunkEdges)));


        layeredTexturePreprocessingData.keyValuePairs.push_back(
            std::pair<std::string, std::string>(
                "showHeightResolution",
                std::to_string(chunk.owner()->debugOptions.showHeightResolution)));

        layeredTexturePreprocessingData.keyValuePairs.push_back(
            std::pair<std::string, std::string>(
                "showHeightIntensities",
                std::to_string(chunk.owner()->debugOptions.showHeightIntensities)));

        layeredTexturePreprocessingData.keyValuePairs.push_back(
            std::pair<std::string, std::string>(
                "defaultHeight",
                std::to_string(Chunk::DEFAULT_HEIGHT)));
        



        // Now the shader program can be accessed
        ProgramObject* programObject =
            layeredTextureShaderProvider->getUpdatedShaderProgram(
                layeredTexturePreprocessingData);

        programUniformHandler->updateIdsIfNecessary(layeredTextureShaderProvider);

        // Activate the shader program
        programObject->activate();

        // Initialize all texture units
        struct BlendTexUnits {
            ghoul::opengl::TextureUnit blendTexture0;
            ghoul::opengl::TextureUnit blendTexture1;
            ghoul::opengl::TextureUnit blendTexture2;
        };
        std::array<std::vector<BlendTexUnits>, LayeredTextures::NUM_TEXTURE_CATEGORIES> texUnits;
        for (size_t category = 0; category < LayeredTextures::NUM_TEXTURE_CATEGORIES; category++) {
            texUnits[category].resize(tileProviders[category].size());
        }

        // Go through all the categories
        for (size_t category = 0; category < LayeredTextures::NUM_TEXTURE_CATEGORIES; category++) {
            // Go through all the providers in this category
            int i = 0;
            for (auto it = tileProviders[category].begin(); it != tileProviders[category].end(); it++) {
                auto tileProvider = it->get();

                // Get the texture that should be used for rendering
                TileAndTransform tileAndTransform = TileSelector::getHighestResolutionTile(tileProvider, chunkIndex);
                if (tileAndTransform.tile.status == Tile::Status::Unavailable) {
                    tileAndTransform.tile = tileProvider->getDefaultTile();
                    tileAndTransform.uvTransform.uvOffset = { 0, 0 };
                    tileAndTransform.uvTransform.uvScale = { 1, 1 };
                }

                activateTileAndSetTileUniforms(
                    programUniformHandler,
                    LayeredTextures::TextureCategory(category),
                    LayeredTextureShaderUniformIdHandler::BlendLayerSuffixes::none,
                    i,
                    texUnits[category][i].blendTexture0,
                    tileAndTransform);

                // If blending is enabled, two more textures are needed
                if (layeredTexturePreprocessingData.layeredTextureInfo[category].layerBlendingEnabled) {
                    TileAndTransform tileAndTransformParent1 = TileSelector::getHighestResolutionTile(tileProvider, chunkIndex, 1);
                    if (tileAndTransformParent1.tile.status == Tile::Status::Unavailable) {
                        tileAndTransformParent1 = tileAndTransform;
                    }
                    activateTileAndSetTileUniforms(
                        programUniformHandler,
                        LayeredTextures::TextureCategory(category),
                        LayeredTextureShaderUniformIdHandler::BlendLayerSuffixes::Parent1,
                        i,
                        texUnits[category][i].blendTexture1,
                        tileAndTransformParent1);

                    TileAndTransform tileAndTransformParent2 = TileSelector::getHighestResolutionTile(tileProvider, chunkIndex, 2);
                    if (tileAndTransformParent2.tile.status == Tile::Status::Unavailable) {
                        tileAndTransformParent2 = tileAndTransformParent1;
                    }
                    activateTileAndSetTileUniforms(
                        programUniformHandler,
                        LayeredTextures::TextureCategory(category),
                        LayeredTextureShaderUniformIdHandler::BlendLayerSuffixes::Parent2,
                        i,
                        texUnits[category][i].blendTexture2,
                        tileAndTransformParent2);
                }

                /*
                if (category == LayeredTextures::HeightMaps && tileAndTransform.tile.preprocessData) {
                    //auto preprocessingData = tileAndTransform.tile.preprocessData;
                    //float noDataValue = preprocessingData->noDataValues[0];
                    programObject->setUniform(
                        "minimumValidHeight[" + std::to_string(i) + "]",
                        -100000);
                }
                */
                i++;
            }
        }

        // Go through all the height maps and set depth tranforms
        int i = 0;
        auto it = tileProviders[LayeredTextures::HeightMaps].begin();
        auto end = tileProviders[LayeredTextures::HeightMaps].end();
        for (; it != end; it++) {
            auto tileProvider = *it;

            TileDepthTransform depthTransform = tileProvider->depthTransform();
            setDepthTransformUniforms(
                programUniformHandler,
                LayeredTextures::TextureCategory::HeightMaps,
                LayeredTextureShaderUniformIdHandler::BlendLayerSuffixes::none,
                i,
                depthTransform);
            i++;
        }

        // The length of the skirts is proportional to its size
        programObject->setUniform("skirtLength", min(static_cast<float>(chunk.surfacePatch().halfSize().lat * 1000000), 8700.0f));
        programObject->setUniform("xSegments", _grid->xSegments());

        if (chunk.owner()->debugOptions.showHeightResolution) {
            programObject->setUniform("vertexResolution", glm::vec2(_grid->xSegments(), _grid->ySegments()));
        }       
        
        return programObject;
    }
Beispiel #3
0
    void ChunkRenderer::renderChunkGlobally(const Chunk& chunk, const RenderData& data){

        ProgramObject* programObject = getActivatedProgramWithTileData(
            _globalRenderingShaderProvider.get(),
            _globalProgramUniformHandler,
            chunk);
        if (programObject == nullptr) {
            return;
        }

        const Ellipsoid& ellipsoid = chunk.owner()->ellipsoid();

        bool performAnyBlending = false;
        
        for (int i = 0; i < LayeredTextures::NUM_TEXTURE_CATEGORIES; ++i) {
            LayeredTextures::TextureCategory category = (LayeredTextures::TextureCategory)i;
            if(_tileProviderManager->getTileProviderGroup(i).levelBlendingEnabled && _tileProviderManager->getTileProviderGroup(category).getActiveTileProviders().size() > 0){
                performAnyBlending = true; 
                break;
            }
        }
        if (performAnyBlending) {
            // Calculations are done in the reference frame of the globe. Hence, the camera
            // position needs to be transformed with the inverse model matrix
            glm::dmat4 inverseModelTransform = chunk.owner()->inverseModelTransform();
            glm::dvec3 cameraPosition =
                glm::dvec3(inverseModelTransform * glm::dvec4(data.camera.positionVec3(), 1));
            float distanceScaleFactor = chunk.owner()->lodScaleFactor * ellipsoid.minimumRadius();
            programObject->setUniform("cameraPosition", vec3(cameraPosition));
            programObject->setUniform("distanceScaleFactor", distanceScaleFactor);
            programObject->setUniform("chunkLevel", chunk.index().level);
        }
        
        // Calculate other uniform variables needed for rendering
        Geodetic2 swCorner = chunk.surfacePatch().getCorner(Quad::SOUTH_WEST);
        auto patchSize = chunk.surfacePatch().size();
        
        dmat4 modelTransform = chunk.owner()->modelTransform();
        dmat4 viewTransform = data.camera.combinedViewMatrix();
        mat4 modelViewTransform = mat4(viewTransform * modelTransform);
        mat4 modelViewProjectionTransform = data.camera.projectionMatrix() * modelViewTransform;

        // Upload the uniform variables
        programObject->setUniform("modelViewProjectionTransform", modelViewProjectionTransform);
        programObject->setUniform("minLatLon", vec2(swCorner.toLonLatVec2()));
        programObject->setUniform("lonLatScalingFactor", vec2(patchSize.toLonLatVec2()));
        programObject->setUniform("radiiSquared", vec3(ellipsoid.radiiSquared()));

        if (_tileProviderManager->getTileProviderGroup(
                LayeredTextures::NightTextures).getActiveTileProviders().size() > 0 ||
            _tileProviderManager->getTileProviderGroup(
                LayeredTextures::WaterMasks).getActiveTileProviders().size() > 0) {
            glm::vec3 directionToSunWorldSpace =
                glm::normalize(-data.modelTransform.translation);
            glm::vec3 directionToSunCameraSpace =
                (viewTransform * glm::dvec4(directionToSunWorldSpace, 0));
            data.modelTransform.translation;
            programObject->setUniform("modelViewTransform", modelViewTransform);
            programObject->setUniform("lightDirectionCameraSpace", -directionToSunCameraSpace);
        }

        // OpenGL rendering settings
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);

        // render
        _grid->geometry().drawUsingActiveProgram();

        // disable shader
        programObject->deactivate();
    }
void DeferredLightingEffect::render(RenderArgs* args) {
    // perform deferred lighting, rendering to free fbo
    glDisable(GL_BLEND);
    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_COLOR_MATERIAL);
    glDepthMask(false);

    auto textureCache = DependencyManager::get<TextureCache>();
    
    glBindFramebuffer(GL_FRAMEBUFFER, 0 );

    QSize framebufferSize = textureCache->getFrameBufferSize();
    
    // binding the first framebuffer
    auto freeFBO = DependencyManager::get<GlowEffect>()->getFreeFramebuffer();
    glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(freeFBO));
 
    glClear(GL_COLOR_BUFFER_BIT);
   // glEnable(GL_FRAMEBUFFER_SRGB);

   // glBindTexture(GL_TEXTURE_2D, primaryFBO->texture());
    glBindTexture(GL_TEXTURE_2D, textureCache->getPrimaryColorTextureID());
    
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, textureCache->getPrimaryNormalTextureID());
    
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, textureCache->getPrimarySpecularTextureID());
    
    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D, textureCache->getPrimaryDepthTextureID());
        
    // get the viewport side (left, right, both)
    int viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    const int VIEWPORT_X_INDEX = 0;
    const int VIEWPORT_Y_INDEX = 1;
    const int VIEWPORT_WIDTH_INDEX = 2;
    const int VIEWPORT_HEIGHT_INDEX = 3;

    float sMin = viewport[VIEWPORT_X_INDEX] / (float)framebufferSize.width();
    float sWidth = viewport[VIEWPORT_WIDTH_INDEX] / (float)framebufferSize.width();
    float tMin = viewport[VIEWPORT_Y_INDEX] / (float)framebufferSize.height();
    float tHeight = viewport[VIEWPORT_HEIGHT_INDEX] / (float)framebufferSize.height();

    bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap());

    // Fetch the ViewMatrix;
    glm::mat4 invViewMat;
    _viewState->getViewTransform().getMatrix(invViewMat);

    ProgramObject* program = &_directionalLight;
    const LightLocations* locations = &_directionalLightLocations;
    bool shadowsEnabled = _viewState->getShadowsEnabled();
    if (shadowsEnabled) {
        glActiveTexture(GL_TEXTURE4);
        glBindTexture(GL_TEXTURE_2D, textureCache->getShadowDepthTextureID());
        
        program = &_directionalLightShadowMap;
        locations = &_directionalLightShadowMapLocations;
        if (_viewState->getCascadeShadowsEnabled()) {
            program = &_directionalLightCascadedShadowMap;
            locations = &_directionalLightCascadedShadowMapLocations;
            if (useSkyboxCubemap) {
                program = &_directionalSkyboxLightCascadedShadowMap;
                locations = &_directionalSkyboxLightCascadedShadowMapLocations;
            } else if (_ambientLightMode > -1) {
                program = &_directionalAmbientSphereLightCascadedShadowMap;
                locations = &_directionalAmbientSphereLightCascadedShadowMapLocations;
            }
            program->bind();
            program->setUniform(locations->shadowDistances, _viewState->getShadowDistances());
        
        } else {
            if (useSkyboxCubemap) {
                program = &_directionalSkyboxLightShadowMap;
                locations = &_directionalSkyboxLightShadowMapLocations;
            } else if (_ambientLightMode > -1) {
                program = &_directionalAmbientSphereLightShadowMap;
                locations = &_directionalAmbientSphereLightShadowMapLocations;
            }
            program->bind();
        }
        program->setUniformValue(locations->shadowScale,
            1.0f / textureCache->getShadowFramebuffer()->getWidth());
        
    } else {
        if (useSkyboxCubemap) {
                program = &_directionalSkyboxLight;
                locations = &_directionalSkyboxLightLocations;
        } else if (_ambientLightMode > -1) {
            program = &_directionalAmbientSphereLight;
            locations = &_directionalAmbientSphereLightLocations;
        }
        program->bind();
    }

    {
        auto globalLight = _allocatedLights[_globalLights.front()];
    
        if (locations->ambientSphere >= 0) {
            gpu::SphericalHarmonics sh = globalLight->getAmbientSphere();
            if (useSkyboxCubemap && _skybox->getCubemap()->getIrradiance()) {
                sh = (*_skybox->getCubemap()->getIrradiance());
            }
            for (int i =0; i <gpu::SphericalHarmonics::NUM_COEFFICIENTS; i++) {
                program->setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i)); 
            }
        }
    
        if (useSkyboxCubemap) {
            glActiveTexture(GL_TEXTURE5);
            glBindTexture(GL_TEXTURE_CUBE_MAP, gpu::GLBackend::getTextureID(_skybox->getCubemap()));
        }

        if (locations->lightBufferUnit >= 0) {
            gpu::Batch batch;
            batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer());
            gpu::GLBackend::renderBatch(batch);
        }
        
        if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) {
            gpu::Batch batch;
            batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer());
            gpu::GLBackend::renderBatch(batch);
        }
        glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
    }

    float left, right, bottom, top, nearVal, farVal;
    glm::vec4 nearClipPlane, farClipPlane;
    _viewState->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
    program->setUniformValue(locations->nearLocation, nearVal);
    float depthScale = (farVal - nearVal) / farVal;
    program->setUniformValue(locations->depthScale, depthScale);
    float nearScale = -1.0f / nearVal;
    float depthTexCoordScaleS = (right - left) * nearScale / sWidth;
    float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight;
    float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS;
    float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT;
    program->setUniformValue(locations->depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
    program->setUniformValue(locations->depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
    
    renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight);
    
    program->release();

    if (useSkyboxCubemap) {
        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
        if (!shadowsEnabled) {
            glActiveTexture(GL_TEXTURE3);
        }
    }

    if (shadowsEnabled) {
        glBindTexture(GL_TEXTURE_2D, 0);        
        glActiveTexture(GL_TEXTURE3);
    }
    
    // additive blending
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    
    glEnable(GL_CULL_FACE);
    
    glm::vec4 sCoefficients(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f);
    glm::vec4 tCoefficients(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f);
    glTexGenfv(GL_S, GL_OBJECT_PLANE, (const GLfloat*)&sCoefficients);
    glTexGenfv(GL_T, GL_OBJECT_PLANE, (const GLfloat*)&tCoefficients);
    
    // enlarge the scales slightly to account for tesselation
    const float SCALE_EXPANSION = 0.05f;
    
    const glm::vec3& eyePoint = _viewState->getCurrentViewFrustum()->getPosition();
    float nearRadius = glm::distance(eyePoint, _viewState->getCurrentViewFrustum()->getNearTopLeft());

    auto geometryCache = DependencyManager::get<GeometryCache>();
    
    if (!_pointLights.empty()) {
        _pointLight.bind();
        _pointLight.setUniformValue(_pointLightLocations.nearLocation, nearVal);
        _pointLight.setUniformValue(_pointLightLocations.depthScale, depthScale);
        _pointLight.setUniformValue(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
        _pointLight.setUniformValue(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);

        for (auto lightID : _pointLights) {
            auto light = _allocatedLights[lightID];

            if (_pointLightLocations.lightBufferUnit >= 0) {
                gpu::Batch batch;
                batch.setUniformBuffer(_pointLightLocations.lightBufferUnit, light->getSchemaBuffer());
                gpu::GLBackend::renderBatch(batch);
            }
            glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));

            glPushMatrix();
            
            float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
            if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) {
                glLoadIdentity();
                glTranslatef(0.0f, 0.0f, -1.0f);
                
                glMatrixMode(GL_PROJECTION);
                glPushMatrix();
                glLoadIdentity();
                
                renderFullscreenQuad();
            
                glPopMatrix();
                glMatrixMode(GL_MODELVIEW);
                
            } else {
                glTranslatef(light->getPosition().x, light->getPosition().y, light->getPosition().z);   
                geometryCache->renderSphere(expandedRadius, 32, 32, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
            }
            
            glPopMatrix();
        }
        _pointLights.clear();
        
        _pointLight.release();
    }
    
    if (!_spotLights.empty()) {
        _spotLight.bind();
        _spotLight.setUniformValue(_spotLightLocations.nearLocation, nearVal);
        _spotLight.setUniformValue(_spotLightLocations.depthScale, depthScale);
        _spotLight.setUniformValue(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
        _spotLight.setUniformValue(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
        
        for (auto lightID : _spotLights) {
            auto light = _allocatedLights[lightID];

            if (_spotLightLocations.lightBufferUnit >= 0) {
                gpu::Batch batch;
                batch.setUniformBuffer(_spotLightLocations.lightBufferUnit, light->getSchemaBuffer());
                gpu::GLBackend::renderBatch(batch);
            }
            glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));

            glPushMatrix();
            
            float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
            float edgeRadius = expandedRadius / glm::cos(light->getSpotAngle());
            if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < edgeRadius + nearRadius) {
                glLoadIdentity();
                glTranslatef(0.0f, 0.0f, -1.0f);
                
                glMatrixMode(GL_PROJECTION);
                glPushMatrix();
                glLoadIdentity();
                
                renderFullscreenQuad();
                
                glPopMatrix();
                glMatrixMode(GL_MODELVIEW);
                
            } else {
                glTranslatef(light->getPosition().x, light->getPosition().y, light->getPosition().z);
                glm::quat spotRotation = rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), light->getDirection());
                glm::vec3 axis = glm::axis(spotRotation);
                glRotatef(glm::degrees(glm::angle(spotRotation)), axis.x, axis.y, axis.z);   
                glTranslatef(0.0f, 0.0f, -light->getMaximumRadius() * (1.0f + SCALE_EXPANSION * 0.5f));  
                geometryCache->renderCone(expandedRadius * glm::tan(light->getSpotAngle()),
                    expandedRadius, 32, 1);
            }
            
            glPopMatrix();
        }
        _spotLights.clear();
        
        _spotLight.release();
    }
    
    glBindTexture(GL_TEXTURE_2D, 0);
        
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, 0);
    
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, 0);
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, 0);
  //  glDisable(GL_FRAMEBUFFER_SRGB);
    
    // End of the Lighting pass
}