Adacleanup6b4572710 months agoHistory
728 lines · 27.7 KB · C++
RawDownload
////  LightClusters.cpp////  Created by Sam Gateau on 9/7/2016.//  Copyright 2015 High Fidelity, Inc.//  Copyright 2024 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "LightClusters.h" #include <gpu/Context.h>#include <shaders/Shaders.h>#include <graphics/ShaderConstants.h> #include "RenderUtilsLogging.h"#include "render-utils/ShaderConstants.h"#include "StencilMaskPass.h"  namespace ru {    using render_utils::slot::texture::Texture;    using render_utils::slot::buffer::Buffer;} namespace gr {    using graphics::slot::texture::Texture;    using graphics::slot::buffer::Buffer;} FrustumGrid::FrustumGrid(const FrustumGrid& source) :    frustumNear(source.frustumNear),    rangeNear(source.rangeNear),    rangeFar(source.rangeFar),    frustumFar(source.frustumFar),    dims(source.dims),    spare(source.spare),    eyeToGridProj(source.eyeToGridProj),    worldToEyeMat(source.worldToEyeMat),    eyeToWorldMat(source.eyeToWorldMat){} void FrustumGrid::generateGridPlanes(Planes& xPlanes, Planes& yPlanes, Planes& zPlanes) {    xPlanes.resize(dims.x + 1);    yPlanes.resize(dims.y + 1);    zPlanes.resize(dims.z + 1);     float centerY = float(dims.y) * 0.5f;    float centerX = float(dims.x) * 0.5f;     for (int z = 0; z < (int) zPlanes.size(); z++) {        ivec3 pos(0, 0, z);        zPlanes[z] = glm::vec4(0.0f, 0.0f, 1.0f, -frustumGrid_clusterPosToEye(pos, vec3(0.0)).z);    }     for (int x = 0; x < (int) xPlanes.size(); x++) {        auto slicePos = frustumGrid_clusterPosToEye(glm::vec3((float)x, centerY, 0.0));        auto sliceDir = glm::normalize(slicePos);        xPlanes[x] = glm::vec4(sliceDir.z, 0.0, -sliceDir.x, 0.0);    }     for (int y = 0; y < (int) yPlanes.size(); y++) {        auto slicePos = frustumGrid_clusterPosToEye(glm::vec3(centerX, (float)y, 0.0));        auto sliceDir = glm::normalize(slicePos);        yPlanes[y] = glm::vec4(0.0, sliceDir.z, -sliceDir.y, 0.0);    } } #include "DeferredLightingEffect.h"#ifdef Q_OS_MACconst glm::uvec4 LightClusters::MAX_GRID_DIMENSIONS { 16, 16, 16, 16384 };#elseconst glm::uvec4 LightClusters::MAX_GRID_DIMENSIONS { 32, 32, 31, 16384 };#endif  LightClusters::LightClusters() :    _lightIndicesBuffer(std::make_shared<gpu::Buffer>(gpu::Buffer::UniformBuffer)),    _clusterGridBuffer(/*std::make_shared<gpu::Buffer>(), */gpu::Element::INDEX_INT32),    _clusterContentBuffer(/*std::make_shared<gpu::Buffer>(), */gpu::Element::INDEX_INT32) {} void LightClusters::setDimensions(glm::uvec3 gridDims, uint32_t listBudget) {    ivec3 configDimensions;    auto gridBudget = MAX_GRID_DIMENSIONS.w;    configDimensions.x = std::max(1, (int) std::min(MAX_GRID_DIMENSIONS.x, gridDims.x));    configDimensions.y = std::max(1, (int) std::min(MAX_GRID_DIMENSIONS.y, gridDims.y));    configDimensions.z = std::max(1, (int) std::min(MAX_GRID_DIMENSIONS.z, gridDims.z));     auto sliceCost = configDimensions.x * configDimensions.y;    auto maxNumSlices = (int)(gridBudget / sliceCost) - 1;    configDimensions.z = std::min(maxNumSlices, configDimensions.z);      // Grab the frustumGridBuffer and force it updated    const auto& constFrustumGrid = _frustumGridBuffer.get();    const auto& dims = constFrustumGrid.dims;    if ((dims.x != configDimensions.x) || (dims.y != configDimensions.y) || (dims.z != configDimensions.z)) {        auto& theFrustumGrid = _frustumGridBuffer.edit();        theFrustumGrid.dims = configDimensions;        theFrustumGrid.generateGridPlanes(_gridPlanes[0], _gridPlanes[1], _gridPlanes[2]);        _clusterResourcesInvalid = true;    }     auto configListBudget = std::min(MAX_GRID_DIMENSIONS.w, listBudget);    if (_clusterContentBudget != configListBudget) {        _clusterContentBudget = configListBudget;        _clusterResourcesInvalid = true;    }} uint32_t LightClusters::getNumClusters() const {    auto theFrustumGrid = _frustumGridBuffer.get();    return theFrustumGrid.frustumGrid_numClusters();}  void LightClusters::updateClusterResource() {    if (!_clusterResourcesInvalid) {        return;    }    _clusterResourcesInvalid = false;    auto numClusters = getNumClusters();    if (numClusters != (uint32_t) _clusterGrid.size()) {        _clusterGrid.clear();        _clusterGrid.resize(numClusters, EMPTY_CLUSTER);        _clusterGridBuffer._size = (numClusters * sizeof(uint32_t));        _clusterGridBuffer._buffer = std::make_shared<gpu::Buffer>(gpu::Buffer::UniformBuffer, _clusterGridBuffer._size, (gpu::Byte*) _clusterGrid.data(), _clusterGridBuffer._size);    }     // Since LightIndex is 2bytes, we can fit 2 in a uint32    auto configListBudget = _clusterContentBudget;    if (sizeof(LightIndex) == 2) {        configListBudget *= 2;    }     if (configListBudget != (uint32_t) _clusterContent.size()) {        _clusterContent.clear();        _clusterContent.resize(configListBudget, INVALID_LIGHT);        _clusterContentBuffer._size = (configListBudget * sizeof(LightIndex));        _clusterContentBuffer._buffer = std::make_shared<gpu::Buffer>(gpu::Buffer::UniformBuffer, _clusterContentBuffer._size, (gpu::Byte*) _clusterContent.data(), _clusterContentBuffer._size);    }} void LightClusters::setRangeNearFar(float rangeNear, float rangeFar) {    bool changed = false;     if (_frustumGridBuffer->rangeNear != rangeNear) {        _frustumGridBuffer.edit().rangeNear = rangeNear;        changed = true;    }    if (_frustumGridBuffer->rangeFar != rangeFar) {        _frustumGridBuffer.edit().rangeFar = rangeFar;        changed = true;    }     if (changed) {        _frustumGridBuffer.edit().generateGridPlanes(_gridPlanes[0], _gridPlanes[1], _gridPlanes[2]);    }} void LightClusters::updateFrustum(const ViewFrustum& frustum) {    _frustum = frustum;     _frustumGridBuffer.edit().updateFrustum(frustum);     if (true) {        _frustumGridBuffer.edit().generateGridPlanes(_gridPlanes[0], _gridPlanes[1], _gridPlanes[2]);    }} void LightClusters::updateLightStage(const LightStagePointer& lightStage) {    _lightStage = lightStage;      } void LightClusters::updateLightFrame(const LightStage::FramePointer& lightFrame, bool points, bool spots) {     // start fresh    _visibleLightIndices.clear();     // Now gather the lights    // gather lights    auto& srcPointLights = lightFrame->_pointLights;    auto& srcSpotLights = lightFrame->_spotLights;    int numPointLights = (int)srcPointLights.size();    int numSpotLights = (int)srcSpotLights.size();     _visibleLightIndices.resize(numPointLights + numSpotLights + 1);     _visibleLightIndices[0] = 0;     if (points && !srcPointLights.empty()) {        memcpy(_visibleLightIndices.data() + (_visibleLightIndices[0] + 1), srcPointLights.data(), srcPointLights.size() * sizeof(int));        _visibleLightIndices[0] += (int)srcPointLights.size();    }    if (spots && !srcSpotLights.empty()) {        memcpy(_visibleLightIndices.data() + (_visibleLightIndices[0] + 1), srcSpotLights.data(), srcSpotLights.size() * sizeof(int));        _visibleLightIndices[0] += (int)srcSpotLights.size();    }     _lightIndicesBuffer._buffer->setData(_visibleLightIndices.size() * sizeof(int), (const gpu::Byte*) _visibleLightIndices.data());    _lightIndicesBuffer._size = _visibleLightIndices.size() * sizeof(int);} float distanceToPlane(const glm::vec3& point, const glm::vec4& plane) {    return plane.x * point.x + plane.y * point.y + plane.z * point.z + plane.w;} bool reduceSphereToPlane(const glm::vec4& sphere, const glm::vec4& plane, glm::vec4& reducedSphere) {    float distance = distanceToPlane(glm::vec3(sphere), plane);     if (std::abs(distance) <= sphere.w) {        reducedSphere = glm::vec4(sphere.x - distance * plane.x, sphere.y - distance * plane.y, sphere.z - distance * plane.z, sqrt(sphere.w * sphere.w - distance * distance));        return true;    }     return false;}  uint32_t scanLightVolumeBoxSlice(FrustumGrid& grid, const FrustumGrid::Planes planes[3], int zSlice, int yMin, int yMax, int xMin, int xMax, LightClusters::LightID lightId, const glm::vec4& eyePosRadius,    std::vector< std::vector<LightClusters::LightIndex>>& clusterGrid) {    glm::ivec3 gridPosToOffset(1, grid.dims.x, grid.dims.x * grid.dims.y);    uint32_t numClustersTouched = 0;     for (auto y = yMin; (y <= yMax); y++) {        for (auto x = xMin; (x <= xMax); x++) {            auto index = x + gridPosToOffset.y * y + gridPosToOffset.z * zSlice;            clusterGrid[index].emplace_back(lightId);            numClustersTouched++;        }    }     return numClustersTouched;} uint32_t scanLightVolumeBox(FrustumGrid& grid, const FrustumGrid::Planes planes[3], int zMin, int zMax, int yMin, int yMax, int xMin, int xMax, LightClusters::LightID lightId, const glm::vec4& eyePosRadius,    std::vector< std::vector<LightClusters::LightIndex>>& clusterGrid) {    glm::ivec3 gridPosToOffset(1, grid.dims.x, grid.dims.x * grid.dims.y);    uint32_t numClustersTouched = 0;     for (auto z = zMin; (z <= zMax); z++) {        for (auto y = yMin; (y <= yMax); y++) {            for (auto x = xMin; (x <= xMax); x++) {                auto index = x + gridPosToOffset.y * y + gridPosToOffset.z * z;                clusterGrid[index].emplace_back(lightId);                numClustersTouched++;            }        }    }     return numClustersTouched;} uint32_t scanLightVolumeSphere(FrustumGrid& grid, const FrustumGrid::Planes planes[3], int zMin, int zMax, int yMin, int yMax, int xMin, int xMax, LightClusters::LightID lightId, const glm::vec4& eyePosRadius,    std::vector< std::vector<LightClusters::LightIndex>>& clusterGrid) {    glm::ivec3 gridPosToOffset(1, grid.dims.x, grid.dims.x * grid.dims.y);    uint32_t numClustersTouched = 0;    const auto& xPlanes = planes[0];    const auto& yPlanes = planes[1];    const auto& zPlanes = planes[2];     // FInd the light origin cluster    auto centerCluster = grid.frustumGrid_eyeToClusterPos(glm::vec3(eyePosRadius));     int center_z = centerCluster.z;    int center_y = centerCluster.y;     for (auto z = zMin; (z <= zMax); z++) {        auto zSphere = eyePosRadius;        if (z != center_z) {            auto plane = (z < center_z) ? zPlanes[z + 1] : -zPlanes[z];            if (!reduceSphereToPlane(zSphere, plane, zSphere)) {                // pass this slice!                continue;            }        }        for (auto y = yMin; (y <= yMax); y++) {            auto ySphere = zSphere;            if (y != center_y) {                auto plane = (y < center_y) ? yPlanes[y + 1] : -yPlanes[y];                if (!reduceSphereToPlane(ySphere, plane, ySphere)) {                    // pass this slice!                    continue;                }            }             glm::vec3 spherePoint(ySphere);             auto x = xMin;            for (; (x < xMax); ++x) {                const auto& plane = xPlanes[x + 1];                auto testDistance = distanceToPlane(spherePoint, plane) + ySphere.w;                if (testDistance >= 0.0f) {                    break;                }            }            auto xs = xMax;            for (; (xs >= x); --xs) {                auto plane = -xPlanes[xs];                auto testDistance = distanceToPlane(spherePoint, plane) + ySphere.w;                if (testDistance >= 0.0f) {                    break;                }            }             for (; (x <= xs); x++) {                auto index = grid.frustumGrid_clusterToIndex(ivec3(x, y, z));                if (index < (int)clusterGrid.size()) {                    clusterGrid[index].emplace_back(lightId);                    numClustersTouched++;                } else {                    qCDebug(renderutils) << "WARNING: LightClusters::scanLightVolumeSphere invalid index found ? numClusters = " << clusterGrid.size() << " index = " << index << " found from cluster xyz = " << x << " " << y << " " << z;                }            }        }    }     return numClustersTouched;} glm::ivec3 LightClusters::updateClusters() {    // Make sure resource are in good shape    updateClusterResource();     // Clean up last info    uint32_t numClusters = (uint32_t)_clusterGrid.size();     std::vector< std::vector< LightIndex > > clusterGridPoint(numClusters);    std::vector< std::vector< LightIndex > > clusterGridSpot(numClusters);     _clusterGrid.clear();    _clusterGrid.resize(numClusters, EMPTY_CLUSTER);     uint32_t maxNumIndices = (uint32_t)_clusterContent.size();    _clusterContent.clear();    _clusterContent.resize(maxNumIndices, INVALID_LIGHT);      auto theFrustumGrid(_frustumGridBuffer.get());     glm::ivec3 gridPosToOffset(1, theFrustumGrid.dims.x, theFrustumGrid.dims.x * theFrustumGrid.dims.y);     uint32_t numClusterTouched = 0;    uint32_t numLightsIn = _visibleLightIndices[0];    uint32_t numClusteredLights = 0;    for (size_t lightNum = 1; lightNum < _visibleLightIndices.size(); ++lightNum) {        auto lightId = _visibleLightIndices[lightNum];        auto light = _lightStage->getElement(lightId);        if (!light) {            continue;        }         auto worldOri = light->getPosition();        auto radius = light->getMaximumRadius();        bool isSpot = light->isSpot();         // Bring into frustum eye space        auto eyeOri = theFrustumGrid.frustumGrid_worldToEye(glm::vec4(worldOri, 1.0f));         // Remove light that slipped through and is not in the z range        float eyeZMax = eyeOri.z - radius;        if (eyeZMax > -theFrustumGrid.rangeNear) {            continue;        }        float eyeZMin = eyeOri.z + radius;        bool beyondFar = false;        if (eyeZMin < -theFrustumGrid.rangeFar) {            beyondFar = true;        }         // Get z slices        int zMin = theFrustumGrid.frustumGrid_eyeDepthToClusterLayer(eyeZMin);        int zMax = theFrustumGrid.frustumGrid_eyeDepthToClusterLayer(eyeZMax);        // That should never happen        if (zMin == -2 && zMax == -2) {            continue;        }         // Before Range NEar just apss, range neatr == true near for now        if ((zMin == -1) && (zMax == -1)) {            continue;        }         // CLamp the z range         zMin = std::max(0, zMin);         auto xLeftDistance = radius - distanceToPlane(eyeOri, _gridPlanes[0][0]);        auto xRightDistance = radius + distanceToPlane(eyeOri, _gridPlanes[0].back());         auto yBottomDistance = radius - distanceToPlane(eyeOri, _gridPlanes[1][0]);        auto yTopDistance = radius + distanceToPlane(eyeOri, _gridPlanes[1].back());         if ((xLeftDistance < 0.f) || (xRightDistance < 0.f) || (yBottomDistance < 0.f) || (yTopDistance < 0.f)) {            continue;        }         // find 2D corners of the sphere in grid        int xMin { 0 };        int xMax { theFrustumGrid.dims.x - 1 };        int yMin { 0 };        int yMax { theFrustumGrid.dims.y - 1 };         float radius2 = radius * radius;         auto eyeOriH = glm::vec3(eyeOri);        auto eyeOriV = glm::vec3(eyeOri);         eyeOriH.y = 0.0f;        eyeOriV.x = 0.0f;         float eyeOriLen2H = glm::length2(eyeOriH);        float eyeOriLen2V = glm::length2(eyeOriV);         if ((eyeOriLen2H > radius2)) {            float eyeOriLenH = sqrt(eyeOriLen2H);             auto eyeOriDirH = glm::vec3(eyeOriH) / eyeOriLenH;             float eyeToTangentCircleLenH = sqrt(eyeOriLen2H - radius2);             float eyeToTangentCircleCosH = eyeToTangentCircleLenH / eyeOriLenH;             float eyeToTangentCircleSinH = radius / eyeOriLenH;              // rotate the eyeToOriDir (H & V) in both directions            glm::vec3 leftDir(eyeOriDirH.x * eyeToTangentCircleCosH + eyeOriDirH.z * eyeToTangentCircleSinH, 0.0f, eyeOriDirH.x * -eyeToTangentCircleSinH + eyeOriDirH.z * eyeToTangentCircleCosH);            glm::vec3 rightDir(eyeOriDirH.x * eyeToTangentCircleCosH - eyeOriDirH.z * eyeToTangentCircleSinH, 0.0f, eyeOriDirH.x * eyeToTangentCircleSinH + eyeOriDirH.z * eyeToTangentCircleCosH);             auto lc = theFrustumGrid.frustumGrid_eyeToClusterDirH(leftDir);            if (lc > xMax) {                lc = xMin;            }            auto rc = theFrustumGrid.frustumGrid_eyeToClusterDirH(rightDir);            if (rc < 0) {                rc = xMax;            }            xMin = std::max(xMin, lc);            xMax = std::min(rc, xMax);            assert(xMin <= xMax);        }         if ((eyeOriLen2V > radius2)) {            float eyeOriLenV = sqrt(eyeOriLen2V);             auto eyeOriDirV = glm::vec3(eyeOriV) / eyeOriLenV;             float eyeToTangentCircleLenV = sqrt(eyeOriLen2V - radius2);             float eyeToTangentCircleCosV = eyeToTangentCircleLenV / eyeOriLenV;             float eyeToTangentCircleSinV = radius / eyeOriLenV;              // rotate the eyeToOriDir (H & V) in both directions            glm::vec3 bottomDir(0.0f, eyeOriDirV.y * eyeToTangentCircleCosV + eyeOriDirV.z * eyeToTangentCircleSinV, eyeOriDirV.y * -eyeToTangentCircleSinV + eyeOriDirV.z * eyeToTangentCircleCosV);            glm::vec3 topDir(0.0f, eyeOriDirV.y * eyeToTangentCircleCosV - eyeOriDirV.z * eyeToTangentCircleSinV, eyeOriDirV.y * eyeToTangentCircleSinV + eyeOriDirV.z * eyeToTangentCircleCosV);             auto bc = theFrustumGrid.frustumGrid_eyeToClusterDirV(bottomDir);            auto tc = theFrustumGrid.frustumGrid_eyeToClusterDirV(topDir);            if (bc > yMax) {                bc = yMin;            }            if (tc < 0) {                tc = yMax;            }            yMin = std::max(yMin, bc);            yMax =std::min(tc, yMax);            assert(yMin <= yMax);        }         // now voxelize        auto& clusterGrid = (isSpot ? clusterGridSpot : clusterGridPoint);        if (beyondFar) {            numClusterTouched += scanLightVolumeBoxSlice(theFrustumGrid, _gridPlanes, zMin, yMin, yMax, xMin, xMax, lightId, glm::vec4(glm::vec3(eyeOri), radius), clusterGrid);        } else {            numClusterTouched += scanLightVolumeSphere(theFrustumGrid, _gridPlanes, zMin, zMax, yMin, yMax, xMin, xMax, lightId, glm::vec4(glm::vec3(eyeOri), radius), clusterGrid);        }         numClusteredLights++;    }     // Lights have been gathered now reexpress in terms of 2 sequential buffers    // Start filling from near to far and stops if it overflows    bool checkBudget = false;    if (numClusterTouched > maxNumIndices) {        checkBudget = true;    }    uint16_t indexOffset = 0;    for (int i = 0; i < (int) clusterGridPoint.size(); i++) {        auto& clusterPoint = clusterGridPoint[i];        auto& clusterSpot = clusterGridSpot[i];         uint8_t numLightsPoint = ((uint8_t)clusterPoint.size());        uint8_t numLightsSpot = ((uint8_t)clusterSpot.size());        uint16_t numLights = numLightsPoint + numLightsSpot;

Lines 1–500 of 728.