669 lines · 29.5 KB · C++
RawDownload
////  DeferredLightingEffect.cpp//  interface/src/renderer////  Created by Andrzej Kapolka on 9/11/14.//  Copyright 2014 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "DeferredLightingEffect.h" #include <GLMHelpers.h>#include <PathUtils.h>#include <ViewFrustum.h> #include <gpu/Batch.h>#include <gpu/Context.h> #include "AbstractViewStateInterface.h"#include "GeometryCache.h"#include "TextureCache.h"#include "FramebufferCache.h" #include "deferred_light_vert.h"#include "deferred_light_limited_vert.h"#include "deferred_light_spot_vert.h" #include "directional_light_frag.h"#include "directional_ambient_light_frag.h"#include "directional_skybox_light_frag.h" #include "directional_light_shadow_frag.h"#include "directional_ambient_light_shadow_frag.h"#include "directional_skybox_light_shadow_frag.h" #include "point_light_frag.h"#include "spot_light_frag.h" struct LightLocations {    int radius;    int ambientSphere;    int lightBufferUnit;    int texcoordMat;    int coneParam;    int deferredTransformBuffer;    int shadowTransformBuffer;}; enum {    DEFERRED_BUFFER_COLOR_UNIT = 0,    DEFERRED_BUFFER_NORMAL_UNIT = 1,    DEFERRED_BUFFER_EMISSIVE_UNIT = 2,    DEFERRED_BUFFER_DEPTH_UNIT = 3,    DEFERRED_BUFFER_OBSCURANCE_UNIT = 4,    SHADOW_MAP_UNIT = 5,    SKYBOX_MAP_UNIT = 6,};static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations); void DeferredLightingEffect::init() {    _directionalLightLocations = std::make_shared<LightLocations>();    _directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();    _directionalSkyboxLightLocations = std::make_shared<LightLocations>();     _directionalLightShadowLocations = std::make_shared<LightLocations>();    _directionalAmbientSphereLightShadowLocations = std::make_shared<LightLocations>();    _directionalSkyboxLightShadowLocations = std::make_shared<LightLocations>();     _pointLightLocations = std::make_shared<LightLocations>();    _spotLightLocations = std::make_shared<LightLocations>();     loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations);    loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);    loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);     loadLightProgram(deferred_light_vert, directional_light_shadow_frag, false, _directionalLightShadow, _directionalLightShadowLocations);    loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations);    loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations);     loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations);    loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations);     // Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light    _globalLights.push_back(0);    _allocatedLights.push_back(std::make_shared<model::Light>());     model::LightPointer lp = _allocatedLights[0];    lp->setType(model::Light::SUN);     // Add the global light to the light stage (for later shadow rendering)    _lightStage.addLight(lp);     lp->setDirection(glm::vec3(-1.0f));    lp->setColor(glm::vec3(1.0f));    lp->setIntensity(1.0f);    lp->setType(model::Light::SUN);    lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset::OLD_TOWN_SQUARE);} void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color,        float intensity, float falloffRadius) {    addSpotLight(position, radius, color, intensity, falloffRadius);} void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color,    float intensity, float falloffRadius, const glm::quat& orientation, float exponent, float cutoff) {        unsigned int lightID = (unsigned int)(_pointLights.size() + _spotLights.size() + _globalLights.size());    if (lightID >= _allocatedLights.size()) {        _allocatedLights.push_back(std::make_shared<model::Light>());    }    model::LightPointer lp = _allocatedLights[lightID];     lp->setPosition(position);    lp->setMaximumRadius(radius);    lp->setColor(color);    lp->setIntensity(intensity);    lp->setFalloffRadius(falloffRadius);     if (exponent == 0.0f && cutoff == PI) {        lp->setType(model::Light::POINT);        _pointLights.push_back(lightID);            } else {        lp->setOrientation(orientation);        lp->setSpotAngle(cutoff);        lp->setSpotExponent(exponent);        lp->setType(model::Light::SPOT);        _spotLights.push_back(lightID);    }} void DeferredLightingEffect::prepare(RenderArgs* args) {    gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {        batch.enableStereo(false);        batch.setViewportTransform(args->_viewport);        batch.setStateScissorRect(args->_viewport);         // Clear Lighting buffer        auto lightingFbo = DependencyManager::get<FramebufferCache>()->getLightingFramebuffer();         batch.setFramebuffer(lightingFbo);        batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(vec3(0), 0), true);         // Clear deferred        auto deferredFbo = DependencyManager::get<FramebufferCache>()->getDeferredFramebuffer();         batch.setFramebuffer(deferredFbo);         // Clear Color, Depth and Stencil for deferred buffer        batch.clearFramebuffer(            gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_COLOR1 | gpu::Framebuffer::BUFFER_COLOR2 |            gpu::Framebuffer::BUFFER_DEPTH |            gpu::Framebuffer::BUFFER_STENCIL,            vec4(vec3(0), 0), 1.0, 0.0, true);    });} void DeferredLightingEffect::render(const render::RenderContextPointer& renderContext) {    auto args = renderContext->args;    gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {                // Allocate the parameters buffer used by all the deferred shaders        if (!_deferredTransformBuffer[0]._buffer) {            DeferredTransform parameters;            _deferredTransformBuffer[0] = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(DeferredTransform), (const gpu::Byte*) &parameters));            _deferredTransformBuffer[1] = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(DeferredTransform), (const gpu::Byte*) &parameters));        }         // Framebuffer copy operations cannot function as multipass stereo operations.          batch.enableStereo(false);         // perform deferred lighting, rendering to free fbo        auto framebufferCache = DependencyManager::get<FramebufferCache>();        auto textureCache = DependencyManager::get<TextureCache>();            QSize framebufferSize = framebufferCache->getFrameBufferSize();            // binding the first framebuffer        auto lightingFBO = framebufferCache->getLightingFramebuffer();        batch.setFramebuffer(lightingFBO);         batch.setViewportTransform(args->_viewport);        batch.setStateScissorRect(args->_viewport);         // Bind the G-Buffer surfaces        batch.setResourceTexture(DEFERRED_BUFFER_COLOR_UNIT, framebufferCache->getDeferredColorTexture());        batch.setResourceTexture(DEFERRED_BUFFER_NORMAL_UNIT, framebufferCache->getDeferredNormalTexture());        batch.setResourceTexture(DEFERRED_BUFFER_EMISSIVE_UNIT, framebufferCache->getDeferredSpecularTexture());        batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, framebufferCache->getPrimaryDepthTexture());         // FIXME: Different render modes should have different tasks        if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && _ambientOcclusionEnabled) {            batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, framebufferCache->getOcclusionTexture());        } else {            // need to assign the white texture if ao is off            batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, textureCache->getWhiteTexture());        }         assert(_lightStage.lights.size() > 0);        const auto& globalShadow = _lightStage.lights[0]->shadow;         // Bind the shadow buffer        batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow.map);         // THe main viewport is assumed to be the mono viewport (or the 2 stereo faces side by side within that viewport)        auto monoViewport = args->_viewport;        float sMin = args->_viewport.x / (float)framebufferSize.width();        float sWidth = args->_viewport.z / (float)framebufferSize.width();        float tMin = args->_viewport.y / (float)framebufferSize.height();        float tHeight = args->_viewport.w / (float)framebufferSize.height();         // The view frustum is the mono frustum base        auto viewFrustum = args->getViewFrustum();         // Eval the mono projection        mat4 monoProjMat;        viewFrustum.evalProjectionMatrix(monoProjMat);         // The mono view transform        Transform monoViewTransform;        viewFrustum.evalViewTransform(monoViewTransform);         // THe mono view matrix coming from the mono view transform        glm::mat4 monoViewMat;        monoViewTransform.getMatrix(monoViewMat);         // Running in stero ?        bool isStereo = args->_context->isStereo();        int numPasses = 1;         mat4 projMats[2];        Transform viewTransforms[2];        ivec4 viewports[2];        vec4 clipQuad[2];        vec2 screenBottomLeftCorners[2];        vec2 screenTopRightCorners[2];        vec4 fetchTexcoordRects[2];         DeferredTransform deferredTransforms[2];        auto geometryCache = DependencyManager::get<GeometryCache>();         if (isStereo) {            numPasses = 2;             mat4 eyeViews[2];            args->_context->getStereoProjections(projMats);            args->_context->getStereoViews(eyeViews);             float halfWidth = 0.5f * sWidth;             for (int i = 0; i < numPasses; i++) {                // In stereo, the 2 sides are layout side by side in the mono viewport and their width is half                int sideWidth = monoViewport.z >> 1;                viewports[i] = ivec4(monoViewport.x + (i * sideWidth), monoViewport.y, sideWidth, monoViewport.w);                 deferredTransforms[i].projection = projMats[i];                 auto sideViewMat = monoViewMat * glm::inverse(eyeViews[i]);              //  viewTransforms[i].evalFromRawMatrix(sideViewMat);                viewTransforms[i] = monoViewTransform;                viewTransforms[i].postTranslate(-glm::vec3((eyeViews[i][3])));// evalFromRawMatrix(sideViewMat);                deferredTransforms[i].viewInverse = sideViewMat;                 deferredTransforms[i].stereoSide = (i == 0 ? -1.0f : 1.0f);                 clipQuad[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight);                screenBottomLeftCorners[i] = glm::vec2(-1.0f + i * 1.0f, -1.0f);                screenTopRightCorners[i] = glm::vec2(i * 1.0f, 1.0f);                 fetchTexcoordRects[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight);            }        } else {             viewports[0] = monoViewport;            projMats[0] = monoProjMat;             deferredTransforms[0].projection = monoProjMat;                 deferredTransforms[0].viewInverse = monoViewMat;            viewTransforms[0] = monoViewTransform;             deferredTransforms[0].stereoSide = 0.0f;             clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight);            screenBottomLeftCorners[0] = glm::vec2(-1.0f, -1.0f);            screenTopRightCorners[0] = glm::vec2(1.0f, 1.0f);             fetchTexcoordRects[0] = glm::vec4(sMin, tMin, sWidth, tHeight);        }         auto eyePoint = viewFrustum.getPosition();        float nearRadius = glm::distance(eyePoint, viewFrustum.getNearTopLeft());          for (int side = 0; side < numPasses; side++) {            // Render in this side's viewport            batch.setViewportTransform(viewports[side]);            batch.setStateScissorRect(viewports[side]);             // Sync and Bind the correct DeferredTransform ubo            _deferredTransformBuffer[side]._buffer->setSubData(0, sizeof(DeferredTransform), (const gpu::Byte*) &deferredTransforms[side]);            batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, _deferredTransformBuffer[side]);             glm::vec2 topLeft(-1.0f, -1.0f);            glm::vec2 bottomRight(1.0f, 1.0f);            glm::vec2 texCoordTopLeft(clipQuad[side].x, clipQuad[side].y);            glm::vec2 texCoordBottomRight(clipQuad[side].x + clipQuad[side].z, clipQuad[side].y + clipQuad[side].w);             // First Global directional light and ambient pass            {                auto& program = _shadowMapEnabled ? _directionalLightShadow : _directionalLight;                LightLocationsPtr locations = _shadowMapEnabled ? _directionalLightShadowLocations : _directionalLightLocations;                const auto& keyLight = _allocatedLights[_globalLights.front()];                 // Setup the global directional pass pipeline                {                    if (_shadowMapEnabled) {                        if (keyLight->getAmbientMap()) {                            program = _directionalSkyboxLightShadow;                            locations = _directionalSkyboxLightShadowLocations;                        } else {                            program = _directionalAmbientSphereLightShadow;                            locations = _directionalAmbientSphereLightShadowLocations;                        }                    } else {                        if (keyLight->getAmbientMap()) {                            program = _directionalSkyboxLight;                            locations = _directionalSkyboxLightLocations;                        } else {                            program = _directionalAmbientSphereLight;                            locations = _directionalAmbientSphereLightLocations;                        }                    }                     if (locations->shadowTransformBuffer >= 0) {                        batch.setUniformBuffer(locations->shadowTransformBuffer, globalShadow.getBuffer());                    }                    batch.setPipeline(program);                }                 { // Setup the global lighting                    setupKeyLightBatch(batch, locations->lightBufferUnit, SKYBOX_MAP_UNIT);                }                 {                    batch.setModelTransform(Transform());                    batch.setProjectionTransform(glm::mat4());                    batch.setViewTransform(Transform());                     glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);                   geometryCache->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color);                }                 if (keyLight->getAmbientMap()) {                    batch.setResourceTexture(SKYBOX_MAP_UNIT, nullptr);                }            }             auto texcoordMat = glm::mat4();          /*  texcoordMat[0] = glm::vec4(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f);            texcoordMat[1] = glm::vec4(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f);           */ texcoordMat[0] = glm::vec4(fetchTexcoordRects[side].z / 2.0f, 0.0f, 0.0f, fetchTexcoordRects[side].x + fetchTexcoordRects[side].z / 2.0f);            texcoordMat[1] = glm::vec4(0.0f, fetchTexcoordRects[side].w / 2.0f, 0.0f, fetchTexcoordRects[side].y + fetchTexcoordRects[side].w / 2.0f);            texcoordMat[2] = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);            texcoordMat[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);             // enlarge the scales slightly to account for tesselation            const float SCALE_EXPANSION = 0.05f;              batch.setProjectionTransform(projMats[side]);            batch.setViewTransform(viewTransforms[side]);             // Splat Point lights            if (!_pointLights.empty()) {                batch.setPipeline(_pointLight);                 batch._glUniformMatrix4fv(_pointLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat));                 for (auto lightID : _pointLights) {                    auto& light = _allocatedLights[lightID];                    // IN DEBUG: light->setShowContour(true);                    batch.setUniformBuffer(_pointLightLocations->lightBufferUnit, light->getSchemaBuffer());                     float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);                    // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume,                    // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working...                    if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) {                        Transform model;                        model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f));                        batch.setModelTransform(model);                        batch.setViewTransform(Transform());                        batch.setProjectionTransform(glm::mat4());                         glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);                        DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color);                                        batch.setProjectionTransform(projMats[side]);                        batch.setViewTransform(viewTransforms[side]);                    } else {                        Transform model;                        model.setTranslation(glm::vec3(light->getPosition().x, light->getPosition().y, light->getPosition().z));                        batch.setModelTransform(model.postScale(expandedRadius));                        batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f);                        geometryCache->renderSphere(batch);                    }                }            }                // Splat spot lights            if (!_spotLights.empty()) {                batch.setPipeline(_spotLight);                 batch._glUniformMatrix4fv(_spotLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat));                 for (auto lightID : _spotLights) {                    auto light = _allocatedLights[lightID];                    // IN DEBUG: light->setShowContour(true);                    batch.setUniformBuffer(_spotLightLocations->lightBufferUnit, light->getSchemaBuffer());                     auto eyeLightPos = eyePoint - light->getPosition();                    auto eyeHalfPlaneDistance = glm::dot(eyeLightPos, light->getDirection());                     const float TANGENT_LENGTH_SCALE = 0.666f;                    glm::vec4 coneParam(light->getSpotAngleCosSin(), TANGENT_LENGTH_SCALE * tanf(0.5f * light->getSpotAngle()), 1.0f);                     float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);                    // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume,                    // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working...                    const float OVER_CONSERVATIVE_SCALE = 1.1f;                    if ((eyeHalfPlaneDistance > -nearRadius) &&                        (glm::distance(eyePoint, glm::vec3(light->getPosition())) < (expandedRadius * OVER_CONSERVATIVE_SCALE) + nearRadius)) {                        coneParam.w = 0.0f;                        batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam));                         Transform model;                        model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f));                        batch.setModelTransform(model);                        batch.setViewTransform(Transform());                        batch.setProjectionTransform(glm::mat4());                                        glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);                        DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color);                                        batch.setProjectionTransform( projMats[side]);                        batch.setViewTransform(viewTransforms[side]);                    } else {                        light->setShowContour(false);                        coneParam.w = 1.0f;                        batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam));                         Transform model;                        model.setTranslation(light->getPosition());                        model.postRotate(light->getOrientation());                        model.postScale(glm::vec3(expandedRadius, expandedRadius, expandedRadius));                         batch.setModelTransform(model);                        auto mesh = getSpotLightMesh();                         batch.setIndexBuffer(mesh->getIndexBuffer());                        batch.setInputBuffer(0, mesh->getVertexBuffer());                        batch.setInputFormat(mesh->getVertexFormat());                         {                            auto& part = mesh->getPartBuffer().get<model::Mesh::Part>(0);                            batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex);                        }                    }                }            }        }         // Probably not necessary in the long run because the gpu layer would unbound this texture if used as render target        batch.setResourceTexture(DEFERRED_BUFFER_COLOR_UNIT, nullptr);        batch.setResourceTexture(DEFERRED_BUFFER_NORMAL_UNIT, nullptr);        batch.setResourceTexture(DEFERRED_BUFFER_EMISSIVE_UNIT, nullptr);        batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, nullptr);        batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, nullptr);        batch.setResourceTexture(SHADOW_MAP_UNIT, nullptr);        batch.setResourceTexture(SKYBOX_MAP_UNIT, nullptr);         batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, nullptr);    });     // End of the Lighting pass    if (!_pointLights.empty()) {        _pointLights.clear();    }    if (!_spotLights.empty()) {        _spotLights.clear();    }} void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int skyboxCubemapUnit) {    PerformanceTimer perfTimer("DLE->setupBatch()");    auto keyLight = _allocatedLights[_globalLights.front()]; 

Lines 1–500 of 669.