845 lines · 37.1 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 <gpu/StandardShaderLib.h>

#include "AbstractViewStateInterface.h"
#include "GeometryCache.h"
#include "TextureCache.h"
#include "FramebufferCache.h"


#include "simple_vert.h"
#include "simple_textured_frag.h"
#include "simple_textured_emisive_frag.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_light_shadow_map_frag.h"
#include "directional_light_cascaded_shadow_map_frag.h"

#include "directional_ambient_light_frag.h"
#include "directional_ambient_light_shadow_map_frag.h"
#include "directional_ambient_light_cascaded_shadow_map_frag.h"

#include "directional_skybox_light_frag.h"
#include "directional_skybox_light_shadow_map_frag.h"
#include "directional_skybox_light_cascaded_shadow_map_frag.h"

#include "point_light_frag.h"
#include "spot_light_frag.h"

static const std::string glowIntensityShaderHandle = "glowIntensity";

struct LightLocations {
    int shadowDistances;
    int shadowScale;
    int radius;
    int ambientSphere;
    int lightBufferUnit;
    int atmosphereBufferUnit;
    int texcoordMat;
    int coneParam;
    int deferredTransformBuffer;
};

static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations);


gpu::PipelinePointer DeferredLightingEffect::getPipeline(SimpleProgramKey config) {
    auto it = _simplePrograms.find(config);
    if (it != _simplePrograms.end()) {
        return it.value();
    }
    
    auto state = std::make_shared<gpu::State>();
    if (config.isCulled()) {
        state->setCullMode(gpu::State::CULL_BACK);
    } else {
        state->setCullMode(gpu::State::CULL_NONE);
    }
    state->setDepthTest(true, true, gpu::LESS_EQUAL);
    if (config.hasDepthBias()) {
        state->setDepthBias(1.0f);
        state->setDepthBiasSlopeScale(1.0f);
    }
    state->setBlendFunction(false,
                            gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
                            gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
    
    gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader;
    gpu::PipelinePointer pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
    _simplePrograms.insert(config, pipeline);
    return pipeline;
}

void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
    auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert)));
    auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag)));
    auto PSEmissive = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_emisive_frag)));
    
    _simpleShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS));
    _emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive));
    
    gpu::Shader::BindingSet slotBindings;
    slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT));
    gpu::Shader::makeProgram(*_simpleShader, slotBindings);
    gpu::Shader::makeProgram(*_emissiveShader, slotBindings);

    _viewState = viewState;
    _directionalLightLocations = std::make_shared<LightLocations>();
    _directionalLightShadowMapLocations = std::make_shared<LightLocations>();
    _directionalLightCascadedShadowMapLocations = std::make_shared<LightLocations>();
    _directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();
    _directionalAmbientSphereLightShadowMapLocations = std::make_shared<LightLocations>();
    _directionalAmbientSphereLightCascadedShadowMapLocations = std::make_shared<LightLocations>();
    _directionalSkyboxLightLocations = std::make_shared<LightLocations>();
    _directionalSkyboxLightShadowMapLocations = std::make_shared<LightLocations>();
    _directionalSkyboxLightCascadedShadowMapLocations = 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_light_shadow_map_frag, false, _directionalLightShadowMap,
        _directionalLightShadowMapLocations);
    loadLightProgram(deferred_light_vert, directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap,
        _directionalLightCascadedShadowMapLocations);

    loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
    loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap,
        _directionalAmbientSphereLightShadowMapLocations);
    loadLightProgram(deferred_light_vert, directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap,
        _directionalAmbientSphereLightCascadedShadowMapLocations);

    loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
    loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap,
        _directionalSkyboxLightShadowMapLocations);
    loadLightProgram(deferred_light_vert, directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap,
        _directionalSkyboxLightCascadedShadowMapLocations);


    loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations);
    loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations);

    {
        //auto VSFS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
        //auto PSBlit = gpu::StandardShaderLib::getDrawTexturePS();
        auto blitProgram = gpu::StandardShaderLib::getProgram(gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS, gpu::StandardShaderLib::getDrawTexturePS);
        gpu::Shader::makeProgram(*blitProgram);
        auto blitState = std::make_shared<gpu::State>();
        blitState->setBlendFunction(true,
                                gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
                                gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
        blitState->setColorWriteMask(true, true, true, false);
        _blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState));
    }

    // 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->setDirection(-glm::vec3(1.0f, 1.0f, 1.0f));
    lp->setColor(glm::vec3(1.0f));
    lp->setIntensity(1.0f);
    lp->setType(model::Light::SUN);
    lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET));
}



gpu::PipelinePointer DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled,
                                               bool emmisive, bool depthBias) {
    SimpleProgramKey config{textured, culled, emmisive, depthBias};
    gpu::PipelinePointer pipeline = getPipeline(config);
    batch.setPipeline(pipeline);

    gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader;
    int glowIntensity = program->getUniforms().findLocation("glowIntensity");
    batch._glUniform1f(glowIntensity, 1.0f);
    
    if (!config.isTextured()) {
        // If it is not textured, bind white texture and keep using textured pipeline
        batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
    }

    batch.setResourceTexture(NORMAL_FITTING_MAP_SLOT, DependencyManager::get<TextureCache>()->getNormalFittingTexture());
    return pipeline;
}


void DeferredLightingEffect::renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) {
    bindSimpleProgram(batch);
    DependencyManager::get<GeometryCache>()->renderSphere(batch, radius, slices, stacks, color);
}

void DeferredLightingEffect::renderWireSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) {
    bindSimpleProgram(batch);
    DependencyManager::get<GeometryCache>()->renderSphere(batch, radius, slices, stacks, color, false);
}

uint32_t toCompactColor(const glm::vec4& color) {
    uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) |
        ((int(color.y * 255.0f) & 0xFF) << 8) |
        ((int(color.z * 255.0f) & 0xFF) << 16) |
        ((int(color.w * 255.0f) & 0xFF) << 24);
    return compactColor;
}

void DeferredLightingEffect::renderSolidCubeInstance(gpu::Batch& batch, const Transform& xfm, const glm::vec4& color) {
    static const std::string INSTANCE_NAME = __FUNCTION__;
    static const size_t TRANSFORM_BUFFER = 0;
    static const size_t COLOR_BUFFER = 1;
    {
        gpu::BufferPointer instanceTransformBuffer = batch.getNamedBuffer(INSTANCE_NAME, TRANSFORM_BUFFER);
        glm::mat4 xfmMat4;
        instanceTransformBuffer->append(xfm.getMatrix(xfmMat4));

        gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(INSTANCE_NAME, COLOR_BUFFER);
        auto compactColor = toCompactColor(color);
        instanceColorBuffer->append(compactColor);
    }

    batch.setupNamedCalls(INSTANCE_NAME, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
        auto pipeline = bindSimpleProgram(batch);
        auto location = pipeline->getProgram()->getUniforms().findLocation("Instanced");

        batch._glUniform1i(location, 1);
        DependencyManager::get<GeometryCache>()->renderSolidCubeInstances(batch, data._count,
            data._buffers[TRANSFORM_BUFFER], data._buffers[COLOR_BUFFER]);
        batch._glUniform1i(location, 0);
    });
}

void DeferredLightingEffect::renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color) {
    bindSimpleProgram(batch);
    DependencyManager::get<GeometryCache>()->renderSolidCube(batch, size, color);
}

void DeferredLightingEffect::renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color) {
    bindSimpleProgram(batch);
    DependencyManager::get<GeometryCache>()->renderWireCube(batch, size, color);
}

void DeferredLightingEffect::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner,
                                        const glm::vec4& color) {
    bindSimpleProgram(batch);
    DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, color);
}

void DeferredLightingEffect::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2,
                                        const glm::vec4& color1, const glm::vec4& color2) {
    bindSimpleProgram(batch);
    DependencyManager::get<GeometryCache>()->renderLine(batch, p1, p2, color1, color2);
}

void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color,
        float intensity) {
    addSpotLight(position, radius, color, intensity);    
}

void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color,
    float intensity, const glm::quat& orientation, float exponent, float cutoff) {
    
    unsigned int lightID = _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->setShowContour(quadraticAttenuation);

    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::Batch batch;
    batch.enableStereo(false);

    batch.setStateScissorRect(args->_viewport);

    auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();

    batch.setFramebuffer(primaryFbo);
    // clear the normal and specular buffers
    batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true);
    const float MAX_SPECULAR_EXPONENT = 128.0f;
    batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT), true);

    args->_context->render(batch);
}

gpu::FramebufferPointer _copyFBO;

void DeferredLightingEffect::render(RenderArgs* args) {
    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>();
    
    QSize framebufferSize = framebufferCache->getFrameBufferSize();
    
    // binding the first framebuffer
    _copyFBO = framebufferCache->getFramebuffer();
    batch.setFramebuffer(_copyFBO);

    // Clearing it
    batch.setViewportTransform(args->_viewport);
    batch.setStateScissorRect(args->_viewport);
    batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true);

    // BInd the G-Buffer surfaces
    batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture());
    batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture());
    batch.setResourceTexture(2, framebufferCache->getPrimarySpecularTexture());
    batch.setResourceTexture(3, framebufferCache->getPrimaryDepthTexture());

    // 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->_viewFrustum;

    // 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.5 * 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 =  eyeViews[i] * monoViewMat;
            viewTransforms[i].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
        {
            bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap());

            auto& program = _directionalLight;
            LightLocationsPtr locations = _directionalLightLocations;

            // TODO: At some point bring back the shadows...
            // Setup the global directional pass pipeline
            {
                if (useSkyboxCubemap) {
                    program = _directionalSkyboxLight;
                    locations = _directionalSkyboxLightLocations;
                } else if (_ambientLightMode > -1) {
                    program = _directionalAmbientSphereLight;
                    locations = _directionalAmbientSphereLightLocations;
                }
                batch.setPipeline(program);
            }

            { // Setup the global lighting
                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++) {
                       batch._glUniform4fv(locations->ambientSphere + i, 1, (const float*) (&sh) + i * 4);
                    }
                }
    
                if (useSkyboxCubemap) {
                    batch.setResourceTexture(5, _skybox->getCubemap());
                }

                if (locations->lightBufferUnit >= 0) {
                    batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer());
                }
        
                if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) {
                    batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer());
                }
            }

            {
                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 (useSkyboxCubemap) {
                batch.setResourceTexture(5, nullptr);
            }

Lines 1–500 of 845.