580 lines · 25.4 KB · C++
RawDownload
////  RenderShadowTask.cpp//  render-utils/src/////  Created by Zach Pomerantz on 1/7/2016.//  Copyright 2016 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 "RenderShadowTask.h" #include <gpu/Context.h> #include <ViewFrustum.h> #include <render/CullTask.h>#include <render/SortTask.h>#include <render/DrawTask.h> #include "DeferredLightingEffect.h"#include "FramebufferCache.h" #include "RenderUtilsLogging.h" #include "RenderCommonTask.h"#include "AssembleLightingStageTask.h" #include "FadeEffect.h" // These values are used for culling the objects rendered in the shadow map// but are readjusted afterwards#define SHADOW_FRUSTUM_NEAR 1.0f#define SHADOW_FRUSTUM_FAR  500.0f using namespace render; extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);extern void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds); void RenderShadowTask::configure(const Config& configuration) {    //DependencyManager::get<DeferredLightingEffect>()->setShadowMapEnabled(configuration.isEnabled());    // This is a task, so must still propogate configure() to its Jobs    //    Task::configure(configuration);} void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cameraCullFunctor, uint8_t tagBits, uint8_t tagMask) {    // Prepare the ShapePipeline    static ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();    static std::once_flag once;    std::call_once(once, [] {        initZPassPipelines(*shapePlumber, std::make_shared<gpu::State>(), FadeEffect::getBatchSetter(), FadeEffect::getItemUniformSetter());    });     const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup", input);    const auto queryResolution = setupOutput.getN<RenderShadowSetup::Output>(1);    const auto shadowFrame = setupOutput.getN<RenderShadowSetup::Output>(3);    const auto currentKeyLight = setupOutput.getN<RenderShadowSetup::Output>(4);    // Fetch and cull the items from the scene     static const auto shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);     const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying();    const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);    const auto selectionInputs = CullSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying();    const auto shadowItems = task.addJob<CullSpatialSelection>("FilterShadowSelection", selectionInputs, nullptr, true, RenderDetails::SHADOW);     // Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not    // frustum culling or this will make shadow casters out of the camera frustum disappear.    const auto cameraFrustum = setupOutput.getN<RenderShadowSetup::Output>(2);    const auto applyFunctorInputs = ApplyCullFunctorOnItemBounds::Inputs(shadowItems, cameraFrustum).asVarying();    const auto culledShadowItems = task.addJob<ApplyCullFunctorOnItemBounds>("ShadowCullCamera", applyFunctorInputs, cameraCullFunctor);     // Sort    const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadow", culledShadowItems);    const auto sortedShapes = task.addJob<DepthSortShapes>("DepthSortShadow", sortedPipelines, true);     render::Varying cascadeFrustums[SHADOW_CASCADE_MAX_COUNT] = {        ViewFrustumPointer()#if SHADOW_CASCADE_MAX_COUNT>1        ,ViewFrustumPointer(),        ViewFrustumPointer(),        ViewFrustumPointer()#endif    };     CascadeBoxes cascadeSceneBBoxes;     for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {        char jobName[64];        sprintf(jobName, "ShadowCascadeSetup%d", i);        const auto cascadeSetupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, shadowFrame, i, shadowCasterReceiverFilter);        const auto shadowFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);        auto antiFrustum = render::Varying(ViewFrustumPointer());        cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);        if (i > 1) {            antiFrustum = cascadeFrustums[i - 2];        }         const auto cullInputs = CullShadowBounds::Inputs(sortedShapes, shadowFilter, antiFrustum, currentKeyLight, cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(2)).asVarying();        sprintf(jobName, "CullShadowCascade%d", i);        const auto culledShadowItemsAndBounds = task.addJob<CullShadowBounds>(jobName, cullInputs);         // GPU jobs: Render to shadow map        sprintf(jobName, "RenderShadowMap%d", i);        const auto shadowInputs = RenderShadowMap::Inputs(culledShadowItemsAndBounds.getN<CullShadowBounds::Outputs>(0),            culledShadowItemsAndBounds.getN<CullShadowBounds::Outputs>(1), shadowFrame).asVarying();        task.addJob<RenderShadowMap>(jobName, shadowInputs, shapePlumber, i);        sprintf(jobName, "ShadowCascadeTeardown%d", i);        task.addJob<RenderShadowCascadeTeardown>(jobName, shadowFilter);         cascadeSceneBBoxes[i] = culledShadowItemsAndBounds.getN<CullShadowBounds::Outputs>(1);    }    task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);      output = Output(cascadeSceneBBoxes, setupOutput.getN<RenderShadowSetup::Output>(3)); } static void computeNearFar(const Triangle& triangle, const Plane shadowClipPlanes[4], float& near, float& far) {    static const int MAX_TRIANGLE_COUNT = 16;    Triangle clippedTriangles[MAX_TRIANGLE_COUNT];    auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT);     for (auto i = 0; i < clippedTriangleCount; i++) {        const auto& clippedTriangle = clippedTriangles[i];         near = glm::min(near, -clippedTriangle.v0.z);        near = glm::min(near, -clippedTriangle.v1.z);        near = glm::min(near, -clippedTriangle.v2.z);         far = glm::max(far, -clippedTriangle.v0.z);        far = glm::max(far, -clippedTriangle.v1.z);        far = glm::max(far, -clippedTriangle.v2.z);    }} static void computeNearFar(const glm::vec3 sceneBoundVertices[8], const Plane shadowClipPlanes[4], float& near, float& far) {    // This code is inspired from Microsoft's CascadedShadowMaps11 sample which is under MIT licence.    // See https://code.msdn.microsoft.com/windowsdesktop/Direct3D-Shadow-Win32-2d72a4f2/sourcecode?fileId=121915&pathId=1645833187    // Basically it decomposes the object bounding box in triangles and clips each triangle with the shadow    // frustum planes. Finally it computes the minimum and maximum depth of the clipped triangle vertices     // in shadow space to extract the near and far distances of the shadow frustum.    static const std::array<int[4], 6> boxQuadVertexIndices = { {        { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR },        { TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR },        { TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR },        { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR },        { BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR },        { TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }    }  };    Triangle triangle;     for (auto quadVertexIndices : boxQuadVertexIndices) {        triangle.v0 = sceneBoundVertices[quadVertexIndices[0]];        triangle.v1 = sceneBoundVertices[quadVertexIndices[1]];        triangle.v2 = sceneBoundVertices[quadVertexIndices[2]];        computeNearFar(triangle, shadowClipPlanes, near, far);        triangle.v1 = sceneBoundVertices[quadVertexIndices[3]];        computeNearFar(triangle, shadowClipPlanes, near, far);    }} static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum) {    if (!inShapeBounds.isNull()) {        const Transform shadowView{ shadowFrustum.getView() };        const Transform shadowViewInverse{ shadowView.getInverseMatrix() };         glm::vec3 sceneBoundVertices[8];        // Keep only the left, right, top and bottom shadow frustum planes as we wish to determine        // the near and far        Plane shadowClipPlanes[4];        int i;         // The vertices of the scene bounding box are expressed in the shadow frustum's local space        for (i = 0; i < 8; i++) {            sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast<BoxVertex>(i)));        }        shadowFrustum.getUniformlyTransformedSidePlanes(shadowViewInverse, shadowClipPlanes);         float near = std::numeric_limits<float>::max();        float far = 0.0f;         computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far);        // Limit the far range to the one used originally.        far = glm::min(far, shadowFrustum.getFarClip());        if (near > far) {            near = far;        }         const auto depthEpsilon = 0.1f;        auto projMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, near - depthEpsilon, far + depthEpsilon);        auto shadowProjection = shadowFrustum.getProjection();         shadowProjection[2][2] = projMatrix[2][2];        shadowProjection[3][2] = projMatrix[3][2];        shadowFrustum.setProjection(shadowProjection);        shadowFrustum.calculate();    }} void RenderShadowMap::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {    assert(renderContext->args);    assert(renderContext->args->hasViewFrustum());     const auto& inShapes = inputs.get0();    const auto& inShapeBounds = inputs.get1();    const auto& shadowFrame = inputs.get2();     LightStage::ShadowPointer shadow;    if (shadowFrame && !shadowFrame->_objects.empty()) {        shadow = shadowFrame->_objects.front();    }    if (!shadow || _cascadeIndex >= shadow->getCascadeCount()) {        return;    }     auto& cascade = shadow->getCascade(_cascadeIndex);    auto& fbo = cascade.framebuffer;     RenderArgs* args = renderContext->args;    auto adjustedShadowFrustum = args->getViewFrustum();     // Adjust the frustum near and far depths based on the rendered items bounding box to have    // the minimal Z range.    adjustNearFar(inShapeBounds, adjustedShadowFrustum);    // Reapply the frustum as it has been adjusted    shadow->setCascadeFrustum(_cascadeIndex, adjustedShadowFrustum);    args->popViewFrustum();    args->pushViewFrustum(adjustedShadowFrustum);     gpu::doInBatch("RenderShadowMap::run", args->_context, [&](gpu::Batch& batch) {        args->_batch = &batch;        batch.enableStereo(false);         glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()};        batch.setViewportTransform(viewport);        batch.setStateScissorRect(viewport);         batch.setFramebuffer(fbo);        batch.clearDepthFramebuffer(1.0, false);         if (!inShapeBounds.isNull()) {            glm::mat4 projMat;            Transform viewMat;            args->getViewFrustum().evalProjectionMatrix(projMat);            args->getViewFrustum().evalViewTransform(viewMat);             batch.setProjectionTransform(projMat);            batch.setViewTransform(viewMat, false);             render::ItemBounds itemBounds;            sortAndRenderZPassShapes(_shapePlumber, renderContext, inShapes, itemBounds);        }         args->_batch = nullptr;    });} RenderShadowSetup::RenderShadowSetup() :    _cameraFrustum{ std::make_shared<ViewFrustum>() },    _coarseShadowFrustum{ std::make_shared<ViewFrustum>() } {    _shadowFrameCache = std::make_shared<LightStage::ShadowFrame>();} void RenderShadowSetup::configure(const Config& config) {    constantBias0 = config.constantBias0;    constantBias1 = config.constantBias1;    constantBias2 = config.constantBias2;    constantBias3 = config.constantBias3;    slopeBias0 = config.slopeBias0;    slopeBias1 = config.slopeBias1;    slopeBias2 = config.slopeBias2;    slopeBias3 = config.slopeBias3;    biasInput = config.biasInput;    maxDistance = config.maxDistance;} void RenderShadowSetup::calculateBiases(float biasInput) {    const std::array<float, SHADOW_CASCADE_MAX_COUNT> CONSTANT_CASCADE_SCALE = {{ 0.01f, 0.01f, 0.015f, 0.02f }};    const float SLOPE_BIAS_SCALE = 0.005f;     for (int i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {        auto& cascade = _globalShadowObject->getCascade(i);         // Constant bias is dependent on the depth precision        float cascadeDepth = cascade.getMaxDistance() - cascade.getMinDistance();        float constantBias = CONSTANT_CASCADE_SCALE[i] * biasInput / cascadeDepth;        setConstantBias(i, constantBias);         // Slope bias is dependent on the texel size        float cascadeWidth = cascade.getFrustum()->getWidth();        float cascadeHeight = cascade.getFrustum()->getHeight();        float cascadeTexelMaxDim = glm::max(cascadeWidth, cascadeHeight) / LightStage::Shadow::MAP_SIZE; // TODO: variable cascade resolution        setSlopeBias(i, cascadeTexelMaxDim * constantBias / SLOPE_BIAS_SCALE);    }} void RenderShadowSetup::setConstantBias(int cascadeIndex, float value) {    _bias[cascadeIndex]._constant = value;} void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) {    _bias[cascadeIndex]._slope = value;} void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) {    // Abort all jobs if not casting shadows    auto lightStage = renderContext->_scene->getStage<LightStage>();    assert(lightStage);     const auto lightFrame = *input.get0();    const auto lightingModel = input.get1();     // Clear previous shadow frame always    _shadowFrameCache->_objects.clear();    output.edit3() = _shadowFrameCache;     const auto currentKeyLight = lightStage->getCurrentKeyLight(lightFrame);    if (!lightingModel->isShadowEnabled() || !currentKeyLight || !currentKeyLight->getCastShadows()) {        renderContext->taskFlow.abortTask();        return;    }    output.edit4() = currentKeyLight;     // Cache old render args    RenderArgs* args = renderContext->args;     output.edit0() = args->_renderMode;    // Save main camera frustum    *_cameraFrustum = args->getViewFrustum();    output.edit2() = _cameraFrustum;     if (!_globalShadowObject) {        _globalShadowObject = std::make_shared<LightStage::Shadow>(currentKeyLight, SHADOW_CASCADE_MAX_COUNT);    }    _globalShadowObject->setLight(currentKeyLight);    _globalShadowObject->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);     // Update our biases and maxDistance from the light or config    _globalShadowObject->setMaxDistance(maxDistance > 0.0f ? maxDistance : currentKeyLight->getShadowsMaxDistance());     // Adjust each cascade frustum    for (unsigned int cascadeIndex = 0; cascadeIndex < _globalShadowObject->getCascadeCount(); ++cascadeIndex) {        _globalShadowObject->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);    }     calculateBiases(biasInput > 0.0f ? biasInput : currentKeyLight->getShadowBias());     std::array<float, SHADOW_CASCADE_MAX_COUNT> constantBiases = {{ constantBias0, constantBias1, constantBias2, constantBias3 }};    std::array<float, SHADOW_CASCADE_MAX_COUNT> slopeBiases = {{ slopeBias0, slopeBias1, slopeBias2, slopeBias3 }};    for (unsigned int cascadeIndex = 0; cascadeIndex < _globalShadowObject->getCascadeCount(); ++cascadeIndex) {        float constantBias = constantBiases[cascadeIndex];        if (constantBias > 0.0f) {            setConstantBias(cascadeIndex, constantBias);        }        float slopeBias = slopeBiases[cascadeIndex];        if (slopeBias > 0.0f) {            setSlopeBias(cascadeIndex, slopeBias);        }         auto& bias = _bias[cascadeIndex];        _globalShadowObject->setKeylightCascadeBias(cascadeIndex, bias._constant, bias._slope);    }     _shadowFrameCache->pushShadow(_globalShadowObject);     // Now adjust coarse frustum bounds    auto& firstCascade = _globalShadowObject->getCascade(0);    auto& firstCascadeFrustum = firstCascade.getFrustum();    auto frustumPosition = firstCascadeFrustum->getPosition();    auto farTopLeft = firstCascadeFrustum->getFarTopLeft() - frustumPosition;    auto farBottomRight = firstCascadeFrustum->getFarBottomRight() - frustumPosition;     auto left = glm::dot(farTopLeft, firstCascadeFrustum->getRight());    auto right = glm::dot(farBottomRight, firstCascadeFrustum->getRight());    auto top = glm::dot(farTopLeft, firstCascadeFrustum->getUp());    auto bottom = glm::dot(farBottomRight, firstCascadeFrustum->getUp());    auto near = firstCascadeFrustum->getNearClip();    auto far = firstCascadeFrustum->getFarClip();     for (unsigned int cascadeIndex = 1; cascadeIndex < _globalShadowObject->getCascadeCount(); ++cascadeIndex) {        auto& cascadeFrustum = _globalShadowObject->getCascade(cascadeIndex).getFrustum();         farTopLeft = cascadeFrustum->getFarTopLeft() - frustumPosition;        farBottomRight = cascadeFrustum->getFarBottomRight() - frustumPosition;         auto cascadeLeft = glm::dot(farTopLeft, cascadeFrustum->getRight());        auto cascadeRight = glm::dot(farBottomRight, cascadeFrustum->getRight());        auto cascadeTop = glm::dot(farTopLeft, cascadeFrustum->getUp());        auto cascadeBottom = glm::dot(farBottomRight, cascadeFrustum->getUp());        auto cascadeNear = cascadeFrustum->getNearClip();        auto cascadeFar = cascadeFrustum->getFarClip();        left = glm::min(left, cascadeLeft);        right = glm::max(right, cascadeRight);        bottom = glm::min(bottom, cascadeBottom);        top = glm::max(top, cascadeTop);        near = glm::min(near, cascadeNear);        far = glm::max(far, cascadeFar);    }     _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition());    _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation());    _coarseShadowFrustum->setProjection(glm::ortho<float>(left, right, bottom, top, near, far));    _coarseShadowFrustum->calculate();     // Push frustum for further culling and selection    args->pushViewFrustum(*_coarseShadowFrustum);     args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;     // We want for the octree query enough resolution to catch the details in the lowest cascade. So compute    // the desired resolution for the first cascade frustum and extrapolate it to the coarse frustum.    glm::ivec2 queryResolution = firstCascade.framebuffer->getSize();    queryResolution.x = int(queryResolution.x * _coarseShadowFrustum->getWidth() / firstCascadeFrustum->getWidth());    queryResolution.y = int(queryResolution.y * _coarseShadowFrustum->getHeight() / firstCascadeFrustum->getHeight());    output.edit1() = queryResolution;} void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, const Inputs& input, Outputs& output) {    const auto shadowFrame = input;     // Cache old render args    RenderArgs* args = renderContext->args;     RenderShadowTask::CullFunctor cullFunctor;    if (shadowFrame && !shadowFrame->_objects.empty() && shadowFrame->_objects[0]) {        const auto globalShadow = shadowFrame->_objects[0];         if (globalShadow && _cascadeIndex < globalShadow->getCascadeCount()) {            output.edit0() = _filter;             // Set the keylight render args            auto& cascade = globalShadow->getCascade(_cascadeIndex);            auto& cascadeFrustum = cascade.getFrustum();            args->pushViewFrustum(*cascadeFrustum);            auto texelSize = glm::min(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x;            // Set the cull threshold to 24 shadow texels. This is totally arbitrary            const auto minTexelCount = 24.0f;            // TODO : maybe adapt that with LOD management system?            texelSize *= minTexelCount;            cullFunctor._minSquareSize = texelSize * texelSize;             output.edit1() = cascadeFrustum;         } else {            output.edit0() = ItemFilter::Builder::nothing();            output.edit1() = ViewFrustumPointer();        }    }    else {        output.edit0() = ItemFilter::Builder::nothing();        output.edit1() = ViewFrustumPointer();    }     output.edit2() = cullFunctor;} void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {    RenderArgs* args = renderContext->args;     if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.selectsNothing()) {        args->popViewFrustum();    }    assert(args->hasViewFrustum());} void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {    RenderArgs* args = renderContext->args;     if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {        args->popViewFrustum();    }    assert(args->hasViewFrustum());    // Reset the render args    args->_renderMode = input.get0();} static AABox& merge(AABox& box, const AABox& otherBox, const glm::vec3& dir) {    if (!otherBox.isInvalid()) {        int vertexIndex = 0;        vertexIndex |= ((dir.z > 0.0f) & 1) << 2;        vertexIndex |= ((dir.y > 0.0f) & 1) << 1;        vertexIndex |= ((dir.x < 0.0f) & 1);        auto vertex = otherBox.getVertex((BoxVertex)vertexIndex);        if (!box.isInvalid()) {            const auto boxCenter = box.calcCenter();            vertex -= boxCenter;            vertex = dir * glm::max(0.0f, glm::dot(vertex, dir));            vertex += boxCenter;        }        box += vertex;    }    return box;} void CullShadowBounds::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {    assert(renderContext->args);

Lines 1–500 of 580.