no message
This commit is contained in:
290
cocos/renderer/pipeline/shadow/CSMLayers.cpp
Normal file
290
cocos/renderer/pipeline/shadow/CSMLayers.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
http://www.cocos.com
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "CSMLayers.h"
|
||||
#include "core/Root.h"
|
||||
#include "gfx-base/GFXDevice.h"
|
||||
#include "pipeline/PipelineSceneData.h"
|
||||
#include "pipeline/RenderPipeline.h"
|
||||
#include "pipeline/custom/RenderInterfaceTypes.h"
|
||||
#include "scene/RenderScene.h"
|
||||
#include "scene/Shadow.h"
|
||||
|
||||
namespace cc {
|
||||
namespace pipeline {
|
||||
|
||||
float ShadowTransformInfo::_maxLayerPosz{0.0F};
|
||||
float ShadowTransformInfo::_maxLayerFarPlane{0.0F};
|
||||
|
||||
ShadowTransformInfo::ShadowTransformInfo(uint32_t level) : _level(level) {
|
||||
_validFrustum.setType(geometry::ShapeEnum::SHAPE_FRUSTUM_ACCURATE);
|
||||
_splitFrustum.setType(geometry::ShapeEnum::SHAPE_FRUSTUM_ACCURATE);
|
||||
_lightViewFrustum.setType(geometry::ShapeEnum::SHAPE_FRUSTUM_ACCURATE);
|
||||
}
|
||||
|
||||
void ShadowTransformInfo::createMatrix(const geometry::Frustum &splitFrustum, const scene::DirectionalLight *dirLight, float shadowMapWidth, bool isOnlyCulling) {
|
||||
const float invisibleOcclusionRange = dirLight->getShadowInvisibleOcclusionRange();
|
||||
const gfx::Device *device = gfx::Device::getInstance();
|
||||
const Root *root = Root::getInstance();
|
||||
geometry::Frustum::copy(&_lightViewFrustum, splitFrustum);
|
||||
const float projectionSinY = device->getCapabilities().clipSpaceSignY;
|
||||
const float clipSpaceMinZ = device->getCapabilities().clipSpaceMinZ;
|
||||
|
||||
// view matrix with range back
|
||||
Mat4 matShadowTrans;
|
||||
Mat4::fromRT(dirLight->getNode()->getRotation(), Vec3::ZERO, &matShadowTrans);
|
||||
Mat4 matShadowView = matShadowTrans.getInversed();
|
||||
const Mat4 shadowViewArbitaryPos = matShadowView.clone();
|
||||
_lightViewFrustum.transform(matShadowView);
|
||||
|
||||
// bounding box in light space
|
||||
geometry::AABB::fromPoints(Vec3(100000.0F, 100000.0F, 100000.0F),
|
||||
Vec3(-100000.0F, -100000.0F, -100000.0F),
|
||||
&_castLightViewBoundingBox);
|
||||
_castLightViewBoundingBox.merge(_lightViewFrustum);
|
||||
float orthoSizeWidth = 0.0F;
|
||||
float orthoSizeHeight = 0.0F;
|
||||
if (dirLight->getCSMOptimizationMode() == scene::CSMOptimizationMode::DISABLE_ROTATION_FIX) {
|
||||
orthoSizeWidth = _castLightViewBoundingBox.halfExtents.x;
|
||||
orthoSizeHeight = _castLightViewBoundingBox.halfExtents.y;
|
||||
} else {
|
||||
orthoSizeWidth = orthoSizeHeight = _lightViewFrustum.vertices[0].distance(_lightViewFrustum.vertices[6]);
|
||||
}
|
||||
|
||||
const auto csmLevel = root->getPipeline()->getPipelineSceneData()->getCSMSupported() ? dirLight->getCSMLevel() : scene::CSMLevel::LEVEL_1;
|
||||
if (csmLevel != scene::CSMLevel::LEVEL_1 && dirLight->getCSMOptimizationMode() ==
|
||||
scene::CSMOptimizationMode::REMOVE_DUPLICATES) {
|
||||
if (_level >= static_cast<uint32_t>(csmLevel) - 1U) {
|
||||
_maxLayerFarPlane = _castLightViewBoundingBox.halfExtents.z;
|
||||
_maxLayerPosz = _castLightViewBoundingBox.center.z;
|
||||
} else {
|
||||
const float alignFarPlaneDist = fabsf(_castLightViewBoundingBox.center.z - _maxLayerPosz) + _maxLayerFarPlane;
|
||||
_castLightViewBoundingBox.halfExtents.z = fmaxf(_castLightViewBoundingBox.center.z, alignFarPlaneDist);
|
||||
}
|
||||
}
|
||||
|
||||
const float r = _castLightViewBoundingBox.getHalfExtents().z;
|
||||
_shadowCameraFar = r * 2.0F + invisibleOcclusionRange;
|
||||
const Vec3 ¢er = _castLightViewBoundingBox.getCenter();
|
||||
Vec3 shadowPos(center.x, center.y, center.z + r + invisibleOcclusionRange);
|
||||
shadowPos.transformMat4(shadowPos, matShadowTrans);
|
||||
|
||||
Mat4::fromRT(dirLight->getNode()->getRotation(), shadowPos, &matShadowTrans);
|
||||
matShadowView = matShadowTrans.getInversed();
|
||||
|
||||
if (!isOnlyCulling) {
|
||||
// snap to whole texels
|
||||
const float halfOrthoSizeWidth = orthoSizeWidth * 0.5F;
|
||||
const float halfOrthoSizeHeight = orthoSizeHeight * 0.5F;
|
||||
Mat4 matShadowProj;
|
||||
Mat4::createOrthographicOffCenter(-halfOrthoSizeWidth, halfOrthoSizeWidth, -halfOrthoSizeHeight, halfOrthoSizeHeight,
|
||||
0.1F, _shadowCameraFar, clipSpaceMinZ, projectionSinY, 0U, &matShadowProj);
|
||||
|
||||
Mat4 matShadowViewProjArbitaryPos;
|
||||
Mat4::multiply(matShadowProj, shadowViewArbitaryPos, &matShadowViewProjArbitaryPos);
|
||||
Vec3 projPos;
|
||||
projPos.transformMat4(shadowPos, matShadowViewProjArbitaryPos);
|
||||
const float invActualSize = 2.0F / shadowMapWidth;
|
||||
const Vec2 texelSize(invActualSize, invActualSize);
|
||||
const float modX = fmodf(projPos.x, texelSize.x);
|
||||
const float modY = fmodf(projPos.y, texelSize.y);
|
||||
const Vec3 projSnap(projPos.x - modX, projPos.y - modY, projPos.z);
|
||||
const Mat4 matShadowViewProjArbitaryPosInv = matShadowViewProjArbitaryPos.getInversed();
|
||||
Vec3 snap;
|
||||
snap.transformMat4(projSnap, matShadowViewProjArbitaryPosInv);
|
||||
|
||||
Mat4::fromRT(dirLight->getNode()->getRotation(), snap, &matShadowTrans);
|
||||
matShadowView = matShadowTrans.getInversed();
|
||||
|
||||
// fill data
|
||||
Mat4 matShadowViewProj;
|
||||
Mat4::multiply(matShadowProj, matShadowView, &matShadowViewProj);
|
||||
_matShadowView = matShadowView.clone();
|
||||
_matShadowProj = matShadowProj.clone();
|
||||
_matShadowViewProj = matShadowViewProj.clone();
|
||||
}
|
||||
_validFrustum.createOrtho(orthoSizeWidth, orthoSizeHeight, 0.1F, _shadowCameraFar, matShadowTrans);
|
||||
}
|
||||
|
||||
void ShadowTransformInfo::copyToValidFrustum(const geometry::Frustum &validFrustum) {
|
||||
geometry::Frustum::copy(&_validFrustum, validFrustum);
|
||||
}
|
||||
|
||||
void ShadowTransformInfo::calculateValidFrustumOrtho(float width, float height, float nearClamp, float farClamp, const Mat4 &transform) {
|
||||
_validFrustum.createOrtho(width, height, nearClamp, farClamp, transform);
|
||||
}
|
||||
|
||||
void ShadowTransformInfo::calculateSplitFrustum(float start, float end, float aspect, float fov, const Mat4 &transform) {
|
||||
_splitFrustum.split(start, end, aspect, fov, transform);
|
||||
}
|
||||
|
||||
CSMLayerInfo::CSMLayerInfo(uint32_t level) : ShadowTransformInfo(level) {
|
||||
calculateAtlas(level);
|
||||
}
|
||||
|
||||
void CSMLayerInfo::calculateAtlas(uint32_t level) {
|
||||
const gfx::Device *device = gfx::Device::getInstance();
|
||||
const float clipSpaceSignY = device->getCapabilities().clipSpaceSignY;
|
||||
const float x = floorf(static_cast<float>(level % 2U)) - 0.5F;
|
||||
const float y = clipSpaceSignY * (0.5F - floorf(static_cast<float>(level) / 2U));
|
||||
_csmAtlas.set(0.5F, 0.5F, x, y);
|
||||
}
|
||||
|
||||
CSMLayers::CSMLayers() {
|
||||
_specialLayer = ccnew ShadowTransformInfo(1U);
|
||||
|
||||
for (uint32_t i = 0; i < static_cast<uint32_t>(scene::CSMLevel::LEVEL_4); ++i) {
|
||||
_layers[i] = ccnew CSMLayerInfo(i);
|
||||
}
|
||||
}
|
||||
|
||||
CSMLayers::~CSMLayers() {
|
||||
CC_SAFE_DELETE(_specialLayer);
|
||||
|
||||
for (const auto *shadowCSMLayer : _layers) {
|
||||
CC_SAFE_DELETE(shadowCSMLayer);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMLayers::update(const PipelineSceneData *sceneData, const scene::Camera *camera) {
|
||||
CC_ASSERT(sceneData);
|
||||
CC_ASSERT(camera);
|
||||
|
||||
const scene::Shadows *shadowInfo = sceneData->getShadows();
|
||||
const scene::RenderScene *const scene = camera->getScene();
|
||||
const Root *root = Root::getInstance();
|
||||
scene::DirectionalLight *dirLight = scene->getMainLight();
|
||||
|
||||
CC_ASSERT(dirLight);
|
||||
|
||||
const auto levelCount = root->getPipeline()->getPipelineSceneData()->getCSMSupported() ? static_cast<uint32_t>(dirLight->getCSMLevel()) : 1U;
|
||||
CC_ASSERT(levelCount <= static_cast<uint32_t>(scene::CSMLevel::LEVEL_4));
|
||||
const float shadowDistance = dirLight->getShadowDistance();
|
||||
|
||||
if (!shadowInfo->isEnabled() || !dirLight->isShadowEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dirLight->isShadowFixedArea()) {
|
||||
updateFixedArea(dirLight);
|
||||
} else {
|
||||
if (dirLight->isCSMNeedUpdate() || _levelCount != levelCount ||
|
||||
std::abs(_shadowDistance - shadowDistance) > 1.0F) {
|
||||
splitFrustumLevels(dirLight);
|
||||
_levelCount = levelCount;
|
||||
_shadowDistance = shadowDistance;
|
||||
}
|
||||
|
||||
calculateCSM(camera, dirLight, shadowInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMLayers::updateFixedArea(const scene::DirectionalLight *dirLight) const {
|
||||
const gfx::Device *device = gfx::Device::getInstance();
|
||||
const float x = dirLight->getShadowOrthoSize();
|
||||
const float y = dirLight->getShadowOrthoSize();
|
||||
const float nearClamp = dirLight->getShadowNear();
|
||||
const float farClamp = dirLight->getShadowFar();
|
||||
const float projectionSinY = device->getCapabilities().clipSpaceSignY;
|
||||
const float clipSpaceMinZ = device->getCapabilities().clipSpaceMinZ;
|
||||
|
||||
Mat4 matShadowTrans;
|
||||
Mat4::fromRT(dirLight->getNode()->getWorldRotation(),
|
||||
dirLight->getNode()->getWorldPosition(), &matShadowTrans);
|
||||
const Mat4 matShadowView = matShadowTrans.getInversed();
|
||||
Mat4 matShadowProj;
|
||||
Mat4::createOrthographicOffCenter(-x, x, -y, y, -nearClamp,
|
||||
farClamp, clipSpaceMinZ, projectionSinY, 0U, &matShadowProj);
|
||||
Mat4 matShadowViewProj;
|
||||
Mat4::multiply(matShadowProj, matShadowView, &matShadowViewProj);
|
||||
_specialLayer->setMatShadowView(matShadowView);
|
||||
_specialLayer->setMatShadowProj(matShadowProj);
|
||||
_specialLayer->setMatShadowViewProj(matShadowViewProj);
|
||||
|
||||
_specialLayer->calculateValidFrustumOrtho(x * 2.0F, y * 2.0F, nearClamp, farClamp, matShadowTrans);
|
||||
}
|
||||
|
||||
void CSMLayers::splitFrustumLevels(scene::DirectionalLight *dirLight) {
|
||||
const Root *root = Root::getInstance();
|
||||
constexpr float nd = 0.1F;
|
||||
const float fd = dirLight->getShadowDistance();
|
||||
const float ratio = fd / nd;
|
||||
const auto level = root->getPipeline()->getPipelineSceneData()->getCSMSupported() ? static_cast<uint32_t>(dirLight->getCSMLevel()) : 1U;
|
||||
const float lambda = dirLight->getCSMLayerLambda();
|
||||
_layers.at(0)->setSplitCameraNear(nd);
|
||||
for (uint32_t i = 1; i < level; ++i) {
|
||||
// i ÷ numbers of level
|
||||
const float si = static_cast<float>(i) / static_cast<float>(level);
|
||||
const float preNear = lambda * (nd * powf(ratio, si)) + (1.0F - lambda) * (nd + (fd - nd) * si);
|
||||
// Slightly increase the overlap to avoid fracture
|
||||
const float nextFar = preNear * 1.005F;
|
||||
_layers[i]->setSplitCameraNear(preNear);
|
||||
_layers[i - 1]->setSplitCameraFar(nextFar);
|
||||
}
|
||||
// numbers of level - 1
|
||||
_layers[level - 1]->setSplitCameraFar(fd);
|
||||
|
||||
dirLight->setCSMNeedUpdate(false);
|
||||
}
|
||||
|
||||
void CSMLayers::calculateCSM(const scene::Camera *camera, const scene::DirectionalLight *dirLight, const scene::Shadows *shadowInfo) {
|
||||
const Root *root = Root::getInstance();
|
||||
const auto level = root->getPipeline()->getPipelineSceneData()->getCSMSupported() ? dirLight->getCSMLevel() : scene::CSMLevel::LEVEL_1;
|
||||
const float shadowMapWidth = level != scene::CSMLevel::LEVEL_1 ? shadowInfo->getSize().x * 0.5F : shadowInfo->getSize().x;
|
||||
|
||||
if (shadowMapWidth < 0.999F) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Mat4 mat4Trans = getCameraWorldMatrix(camera);
|
||||
for (int i = static_cast<int>(level) - 1; i >= 0; --i) {
|
||||
auto *layer = _layers[i];
|
||||
const float nearClamp = layer->getSplitCameraNear();
|
||||
const float farClamp = layer->getSplitCameraFar();
|
||||
layer->calculateSplitFrustum(nearClamp, farClamp, camera->getAspect(), camera->getFov(), mat4Trans);
|
||||
layer->createMatrix(layer->getSplitFrustum(), dirLight, shadowMapWidth, false);
|
||||
}
|
||||
|
||||
if (level == scene::CSMLevel::LEVEL_1) {
|
||||
_specialLayer->setShadowCameraFar(_layers[0]->getShadowCameraFar());
|
||||
_specialLayer->setMatShadowView(_layers[0]->getMatShadowView().clone());
|
||||
_specialLayer->setMatShadowProj(_layers[0]->getMatShadowProj().clone());
|
||||
_specialLayer->setMatShadowViewProj(_layers[0]->getMatShadowViewProj().clone());
|
||||
_specialLayer->copyToValidFrustum(_layers[0]->getValidFrustum());
|
||||
} else {
|
||||
_specialLayer->calculateSplitFrustum(0.1F, dirLight->getShadowDistance(), camera->getAspect(), camera->getFov(), mat4Trans);
|
||||
_specialLayer->createMatrix(_specialLayer->getSplitFrustum(), dirLight, shadowMapWidth, true);
|
||||
}
|
||||
}
|
||||
|
||||
Mat4 CSMLayers::getCameraWorldMatrix(const scene::Camera *camera) {
|
||||
const Node *cameraNode = camera->getNode();
|
||||
const Vec3 &position = cameraNode->getWorldPosition();
|
||||
const Quaternion &rotation = cameraNode->getWorldRotation();
|
||||
|
||||
Mat4 out;
|
||||
Mat4::fromRT(rotation, position, &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace cc
|
||||
164
cocos/renderer/pipeline/shadow/CSMLayers.h
Normal file
164
cocos/renderer/pipeline/shadow/CSMLayers.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
http://www.cocos.com
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/TypeDef.h"
|
||||
#include "core/geometry/Frustum.h"
|
||||
#include "math/Mat4.h"
|
||||
#include "pipeline/Define.h"
|
||||
#include "scene/Camera.h"
|
||||
#include "scene/DirectionalLight.h"
|
||||
#include "scene/Shadow.h"
|
||||
|
||||
namespace cc {
|
||||
namespace pipeline {
|
||||
class PipelineSceneData;
|
||||
|
||||
class ShadowTransformInfo {
|
||||
public:
|
||||
explicit ShadowTransformInfo(uint32_t level);
|
||||
~ShadowTransformInfo() = default;
|
||||
|
||||
inline uint32_t getLevel() const { return _level; }
|
||||
|
||||
inline RenderObjectList &getShadowObjects() { return _shadowObjects; }
|
||||
inline void setShadowObjects(RenderObjectList &&ro) { _shadowObjects = std::forward<RenderObjectList>(ro); }
|
||||
inline void addShadowObject(RenderObject &&obj) { _shadowObjects.emplace_back(obj); }
|
||||
inline void clearShadowObjects() { _shadowObjects.clear(); }
|
||||
|
||||
inline float getShadowCameraFar() const { return _shadowCameraFar; }
|
||||
inline void setShadowCameraFar(float shadowCameraFar) { _shadowCameraFar = shadowCameraFar; }
|
||||
|
||||
inline const Mat4 &getMatShadowView() const { return _matShadowView; }
|
||||
inline void setMatShadowView(const Mat4 &matShadowView) { _matShadowView = matShadowView; }
|
||||
|
||||
inline const Mat4 &getMatShadowProj() const { return _matShadowProj; }
|
||||
inline void setMatShadowProj(const Mat4 &matShadowProj) { _matShadowProj = matShadowProj; }
|
||||
|
||||
inline const Mat4 &getMatShadowViewProj() const { return _matShadowViewProj; }
|
||||
inline void setMatShadowViewProj(const Mat4 &matShadowViewProj) { _matShadowViewProj = matShadowViewProj; }
|
||||
|
||||
inline const geometry::Frustum &getValidFrustum() const { return _validFrustum; }
|
||||
|
||||
inline const geometry::Frustum &getSplitFrustum() const { return _splitFrustum; }
|
||||
|
||||
inline const geometry::Frustum &getLightViewFrustum() const { return _lightViewFrustum; }
|
||||
|
||||
inline const geometry::AABB &getCastLightViewBoundingBox() const { return _castLightViewBoundingBox; }
|
||||
|
||||
void createMatrix(const geometry::Frustum &splitFrustum, const scene::DirectionalLight *dirLight, float shadowMapWidth, bool isOnlyCulling);
|
||||
|
||||
void copyToValidFrustum(const geometry::Frustum &validFrustum);
|
||||
|
||||
void calculateValidFrustumOrtho(float width, float height, float nearClamp, float farClamp, const Mat4 &transform);
|
||||
|
||||
void calculateSplitFrustum(float start, float end, float aspect, float fov, const Mat4 &transform);
|
||||
|
||||
private:
|
||||
// global set
|
||||
static float _maxLayerPosz;
|
||||
static float _maxLayerFarPlane;
|
||||
|
||||
// Level is a vector, Indicates the location.range: [0 ~ 3]
|
||||
uint32_t _level{1U};
|
||||
|
||||
float _shadowCameraFar{0.0F};
|
||||
|
||||
Mat4 _matShadowView;
|
||||
Mat4 _matShadowProj;
|
||||
Mat4 _matShadowViewProj;
|
||||
geometry::Frustum _validFrustum;
|
||||
|
||||
// debug renderer value
|
||||
geometry::Frustum _splitFrustum;
|
||||
geometry::Frustum _lightViewFrustum;
|
||||
geometry::AABB _castLightViewBoundingBox;
|
||||
|
||||
RenderObjectList _shadowObjects;
|
||||
};
|
||||
|
||||
class CSMLayerInfo : public ShadowTransformInfo {
|
||||
public:
|
||||
explicit CSMLayerInfo(uint32_t level);
|
||||
~CSMLayerInfo() = default;
|
||||
|
||||
inline float getSplitCameraNear() const { return _splitCameraNear; }
|
||||
inline void setSplitCameraNear(float splitCameraNear) { _splitCameraNear = splitCameraNear; }
|
||||
|
||||
inline float getSplitCameraFar() const { return _splitCameraFar; }
|
||||
inline void setSplitCameraFar(float splitCameraFar) { _splitCameraFar = splitCameraFar; }
|
||||
|
||||
inline const Vec4 &getCSMAtlas() const { return _csmAtlas; }
|
||||
|
||||
private:
|
||||
void calculateAtlas(uint32_t level);
|
||||
|
||||
float _splitCameraNear{0.0F};
|
||||
float _splitCameraFar{0.0F};
|
||||
|
||||
Vec4 _csmAtlas;
|
||||
};
|
||||
|
||||
class CSMLayers {
|
||||
public:
|
||||
CSMLayers();
|
||||
~CSMLayers();
|
||||
|
||||
void update(const PipelineSceneData *sceneData, const scene::Camera *camera);
|
||||
|
||||
inline const RenderObjectList &getCastShadowObjects() const { return _castShadowObjects; }
|
||||
inline void setCastShadowObjects(RenderObjectList &&ro) { _castShadowObjects = std::forward<RenderObjectList>(ro); }
|
||||
inline void addCastShadowObject(RenderObject &&obj) { _castShadowObjects.emplace_back(obj); }
|
||||
inline void clearCastShadowObjects() { _castShadowObjects.clear(); }
|
||||
|
||||
inline RenderObjectList &getLayerObjects() { return _layerObjects; }
|
||||
inline void setLayerObjects(RenderObjectList &&ro) { _layerObjects = std::forward<RenderObjectList>(ro); }
|
||||
inline void addLayerObject(RenderObject &&obj) { _layerObjects.emplace_back(obj); }
|
||||
inline void clearLayerObjects() { _layerObjects.clear(); }
|
||||
|
||||
inline const ccstd::array<CSMLayerInfo *, 4> &getLayers() const { return _layers; }
|
||||
|
||||
inline ShadowTransformInfo *getSpecialLayer() const { return _specialLayer; }
|
||||
|
||||
private:
|
||||
static Mat4 getCameraWorldMatrix(const scene::Camera *camera);
|
||||
|
||||
void splitFrustumLevels(scene::DirectionalLight *dirLight);
|
||||
void updateFixedArea(const scene::DirectionalLight *dirLight) const;
|
||||
void calculateCSM(const scene::Camera *camera, const scene::DirectionalLight *dirLight, const scene::Shadows *shadowInfo);
|
||||
|
||||
// LevelCount is a scalar, Indicates the number.
|
||||
uint32_t _levelCount{0U};
|
||||
|
||||
// The ShadowTransformInfo object for 'fixed area shadow' || 'maximum clipping info' || 'CSM layers = 1'.
|
||||
ShadowTransformInfo *_specialLayer{nullptr};
|
||||
|
||||
float _shadowDistance{0.0F};
|
||||
|
||||
ccstd::array<CSMLayerInfo *, 4> _layers{};
|
||||
|
||||
RenderObjectList _castShadowObjects;
|
||||
RenderObjectList _layerObjects;
|
||||
};
|
||||
} // namespace pipeline
|
||||
} // namespace cc
|
||||
360
cocos/renderer/pipeline/shadow/ShadowFlow.cpp
Normal file
360
cocos/renderer/pipeline/shadow/ShadowFlow.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "ShadowFlow.h"
|
||||
|
||||
#include "CSMLayers.h"
|
||||
#include "ShadowStage.h"
|
||||
#include "gfx-base/GFXDevice.h"
|
||||
#include "pipeline//Define.h"
|
||||
#include "pipeline/GlobalDescriptorSetManager.h"
|
||||
#include "pipeline/PipelineSceneData.h"
|
||||
#include "pipeline/RenderPipeline.h"
|
||||
#include "pipeline/SceneCulling.h"
|
||||
#include "profiler/Profiler.h"
|
||||
#include "scene/Camera.h"
|
||||
#include "scene/DirectionalLight.h"
|
||||
#include "scene/RenderScene.h"
|
||||
#include "scene/Shadow.h"
|
||||
#include "scene/SpotLight.h"
|
||||
|
||||
namespace cc {
|
||||
namespace pipeline {
|
||||
ccstd::unordered_map<ccstd::hash_t, IntrusivePtr<cc::gfx::RenderPass>> ShadowFlow::renderPassHashMap;
|
||||
|
||||
RenderFlowInfo ShadowFlow::initInfo = {
|
||||
"ShadowFlow",
|
||||
static_cast<uint32_t>(ForwardFlowPriority::SHADOW),
|
||||
static_cast<uint32_t>(RenderFlowTag::SCENE),
|
||||
{},
|
||||
};
|
||||
const RenderFlowInfo &ShadowFlow::getInitializeInfo() { return ShadowFlow::initInfo; }
|
||||
|
||||
ShadowFlow::ShadowFlow() = default;
|
||||
ShadowFlow::~ShadowFlow() = default;
|
||||
|
||||
bool ShadowFlow::initialize(const RenderFlowInfo &info) {
|
||||
RenderFlow::initialize(info);
|
||||
if (_stages.empty()) {
|
||||
auto *shadowStage = ccnew ShadowStage;
|
||||
shadowStage->initialize(ShadowStage::getInitializeInfo());
|
||||
_stages.emplace_back(shadowStage);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShadowFlow::activate(RenderPipeline *pipeline) {
|
||||
RenderFlow::activate(pipeline);
|
||||
|
||||
// 0: SHADOWMAP_FLOAT, 1: SHADOWMAP_RGBE.
|
||||
const int32_t isRGBE = supportsR32FloatTexture(pipeline->getDevice()) ? 0 : 1;
|
||||
pipeline->setValue("CC_SHADOWMAP_FORMAT", isRGBE);
|
||||
|
||||
// 0: SHADOWMAP_LINER_DEPTH_OFF, 1: SHADOWMAP_LINER_DEPTH_ON.
|
||||
const int32_t isLinear = 0;
|
||||
pipeline->setValue("CC_SHADOWMAP_USE_LINEAR_DEPTH", isLinear);
|
||||
|
||||
// 0: UNIFORM_VECTORS_LESS_EQUAL_64, 1: UNIFORM_VECTORS_GREATER_EQUAL_125.
|
||||
const auto csmSupported = pipeline->getDevice()->getCapabilities().maxFragmentUniformVectors >=
|
||||
(UBOGlobal::COUNT + UBOCamera::COUNT + UBOShadow::COUNT + UBOCSM::COUNT) >> 2;
|
||||
pipeline->getPipelineSceneData()->setCSMSupported(csmSupported);
|
||||
pipeline->setValue("CC_SUPPORT_CASCADED_SHADOW_MAP", csmSupported);
|
||||
|
||||
// 0: CC_SHADOW_NONE, 1: CC_SHADOW_PLANAR, 2: CC_SHADOW_MAP
|
||||
pipeline->setValue("CC_SHADOW_TYPE", 0);
|
||||
|
||||
// 0: PCFType.HARD, 1: PCFType.SOFT, 2: PCFType.SOFT_2X, 3: PCFType.SOFT_4X
|
||||
pipeline->setValue("CC_DIR_SHADOW_PCF_TYPE", static_cast<int32_t>(scene::PCFType::HARD));
|
||||
|
||||
// 0: CC_DIR_LIGHT_SHADOW_PLANAR, 1: CC_DIR_LIGHT_SHADOW_UNIFORM, 2: CC_DIR_LIGHT_SHADOW_CASCADED, 3: CC_DIR_LIGHT_SHADOW_VARIANCE
|
||||
pipeline->setValue("CC_DIR_LIGHT_SHADOW_TYPE", 0);
|
||||
|
||||
// 0: CC_CASCADED_LAYERS_TRANSITION_OFF, 1: CC_CASCADED_LAYERS_TRANSITION_ON
|
||||
pipeline->setValue("CC_CASCADED_LAYERS_TRANSITION", 0);
|
||||
|
||||
pipeline->onGlobalPipelineStateChanged();
|
||||
}
|
||||
|
||||
void ShadowFlow::render(scene::Camera *camera) {
|
||||
CC_PROFILE(ShadowFlowRender);
|
||||
const auto *sceneData = _pipeline->getPipelineSceneData();
|
||||
const auto *csmLayers = sceneData->getCSMLayers();
|
||||
auto *shadowInfo = sceneData->getShadows();
|
||||
if (shadowInfo == nullptr || !shadowInfo->isEnabled() || shadowInfo->getType() != scene::ShadowType::SHADOW_MAP) {
|
||||
return;
|
||||
}
|
||||
|
||||
lightCollecting();
|
||||
|
||||
if (csmLayers->getCastShadowObjects().empty() && sceneData->getRenderObjects().empty()) {
|
||||
clearShadowMap(camera);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shadowInfo->isShadowMapDirty()) {
|
||||
_pipeline->getGlobalDSManager()->bindTexture(SHADOWMAP::BINDING, nullptr);
|
||||
_pipeline->getGlobalDSManager()->bindTexture(SPOTSHADOWMAP::BINDING, nullptr);
|
||||
}
|
||||
|
||||
const auto &shadowFramebufferMap = sceneData->getShadowFramebufferMap();
|
||||
const scene::DirectionalLight *mainLight = camera->getScene()->getMainLight();
|
||||
if (mainLight) {
|
||||
gfx::DescriptorSet *globalDS = _pipeline->getDescriptorSet();
|
||||
if (!shadowFramebufferMap.count(mainLight)) {
|
||||
initShadowFrameBuffer(_pipeline, mainLight);
|
||||
} else {
|
||||
if (shadowInfo->isShadowMapDirty()) {
|
||||
resizeShadowMap(mainLight, globalDS);
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Framebuffer *shadowFrameBuffer = shadowFramebufferMap.at(mainLight);
|
||||
if (mainLight->isShadowFixedArea()) {
|
||||
renderStage(globalDS, camera, mainLight, shadowFrameBuffer);
|
||||
} else {
|
||||
const auto level = _pipeline->getPipelineSceneData()->getCSMSupported() ? static_cast<uint32_t>(mainLight->getCSMLevel()) : 1U;
|
||||
for (uint32_t i = 0; i < level; ++i) {
|
||||
renderStage(globalDS, camera, mainLight, shadowFrameBuffer, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto *light : _validLights) {
|
||||
gfx::DescriptorSet *ds = _pipeline->getGlobalDSManager()->getOrCreateDescriptorSet(light);
|
||||
|
||||
if (!shadowFramebufferMap.count(light)) {
|
||||
initShadowFrameBuffer(_pipeline, light);
|
||||
} else {
|
||||
if (shadowInfo->isShadowMapDirty()) {
|
||||
resizeShadowMap(light, ds);
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Framebuffer *shadowFrameBuffer = shadowFramebufferMap.at(light);
|
||||
renderStage(ds, camera, light, shadowFrameBuffer);
|
||||
}
|
||||
|
||||
shadowInfo->setShadowMapDirty(false);
|
||||
}
|
||||
|
||||
void ShadowFlow::renderStage(gfx::DescriptorSet *globalDS, scene::Camera *camera, const scene::Light *light, gfx::Framebuffer *framebuffer, uint32_t level) {
|
||||
for (auto &stage : _stages) {
|
||||
auto *shadowStage = static_cast<ShadowStage *>(stage.get());
|
||||
shadowStage->setUsage(globalDS, light, framebuffer, level);
|
||||
shadowStage->render(camera);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowFlow::lightCollecting() {
|
||||
_validLights.clear();
|
||||
|
||||
const ccstd::vector<const scene::Light *> validPunctualLights = _pipeline->getPipelineSceneData()->getValidPunctualLights();
|
||||
for (const scene::Light *light : validPunctualLights) {
|
||||
if (light->getType() == scene::LightType::SPOT) {
|
||||
const auto *spotLight = static_cast<const scene::SpotLight *>(light);
|
||||
if (spotLight->isShadowEnabled()) {
|
||||
_validLights.emplace_back(light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowFlow::clearShadowMap(scene::Camera *camera) {
|
||||
const auto *sceneData = _pipeline->getPipelineSceneData();
|
||||
const auto &shadowFramebufferMap = sceneData->getShadowFramebufferMap();
|
||||
const scene::DirectionalLight *mainLight = camera->getScene()->getMainLight();
|
||||
|
||||
if (mainLight) {
|
||||
gfx::DescriptorSet *globalDS = _pipeline->getDescriptorSet();
|
||||
if (!shadowFramebufferMap.count(mainLight)) {
|
||||
initShadowFrameBuffer(_pipeline, mainLight);
|
||||
}
|
||||
|
||||
auto *shadowFrameBuffer = shadowFramebufferMap.at(mainLight).get();
|
||||
for (auto &stage : _stages) {
|
||||
auto *shadowStage = static_cast<ShadowStage *>(stage.get());
|
||||
shadowStage->setUsage(globalDS, mainLight, shadowFrameBuffer);
|
||||
shadowStage->render(camera);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto *light : _validLights) {
|
||||
gfx::DescriptorSet *ds = _pipeline->getGlobalDSManager()->getOrCreateDescriptorSet(light);
|
||||
if (!shadowFramebufferMap.count(light)) {
|
||||
initShadowFrameBuffer(_pipeline, light);
|
||||
}
|
||||
|
||||
auto *shadowFrameBuffer = shadowFramebufferMap.at(light).get();
|
||||
for (auto &stage : _stages) {
|
||||
auto *shadowStage = static_cast<ShadowStage *>(stage.get());
|
||||
shadowStage->setUsage(ds, light, shadowFrameBuffer);
|
||||
shadowStage->clearFramebuffer(camera);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowFlow::resizeShadowMap(const scene::Light *light, gfx::DescriptorSet *ds) {
|
||||
const auto *sceneData = _pipeline->getPipelineSceneData();
|
||||
const auto *shadowInfo = sceneData->getShadows();
|
||||
auto *device = gfx::Device::getInstance();
|
||||
const auto width = static_cast<uint32_t>(shadowInfo->getSize().x);
|
||||
const auto height = static_cast<uint32_t>(shadowInfo->getSize().y);
|
||||
const auto format = supportsR32FloatTexture(device) ? gfx::Format::R32F : gfx::Format::RGBA8;
|
||||
gfx::Framebuffer *framebuffer = sceneData->getShadowFramebufferMap().at(light);
|
||||
|
||||
auto *colorTexture = gfx::Device::getInstance()->createTexture({
|
||||
gfx::TextureType::TEX2D,
|
||||
gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
|
||||
const auto &renderTargets = framebuffer->getColorTextures();
|
||||
for (auto *renderTarget : renderTargets) {
|
||||
const auto iter = std::find(_usedTextures.begin(), _usedTextures.end(), renderTarget);
|
||||
_usedTextures.erase(iter);
|
||||
}
|
||||
_usedTextures.emplace_back(colorTexture);
|
||||
|
||||
switch (light->getType()) {
|
||||
case scene::LightType::DIRECTIONAL:
|
||||
ds->bindTexture(SHADOWMAP::BINDING, colorTexture);
|
||||
break;
|
||||
case scene::LightType::SPOT:
|
||||
ds->bindTexture(SPOTSHADOWMAP::BINDING, colorTexture);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ds->forceUpdate();
|
||||
|
||||
auto *depthStencilTexture = device->createTexture({
|
||||
gfx::TextureType::TEX2D,
|
||||
gfx::TextureUsageBit::DEPTH_STENCIL_ATTACHMENT,
|
||||
gfx::Format::DEPTH,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
|
||||
auto *oldDepthStencilTexture = framebuffer->getDepthStencilTexture();
|
||||
const auto iter = std::find(_usedTextures.begin(), _usedTextures.end(), oldDepthStencilTexture);
|
||||
_usedTextures.erase(iter);
|
||||
_usedTextures.emplace_back(depthStencilTexture);
|
||||
|
||||
framebuffer->destroy();
|
||||
framebuffer->initialize({
|
||||
_renderPass,
|
||||
{colorTexture},
|
||||
depthStencilTexture,
|
||||
});
|
||||
}
|
||||
|
||||
void ShadowFlow::initShadowFrameBuffer(const RenderPipeline *pipeline, const scene::Light *light) {
|
||||
auto *device = gfx::Device::getInstance();
|
||||
const auto *sceneData = _pipeline->getPipelineSceneData();
|
||||
const auto *shadowInfo = sceneData->getShadows();
|
||||
const auto &shadowMapSize = shadowInfo->getSize();
|
||||
const auto width = static_cast<uint32_t>(shadowMapSize.x);
|
||||
const auto height = static_cast<uint32_t>(shadowMapSize.y);
|
||||
const auto format = supportsR32FloatTexture(device) ? gfx::Format::R32F : gfx::Format::RGBA8;
|
||||
|
||||
const gfx::ColorAttachment colorAttachment{
|
||||
format,
|
||||
gfx::SampleCount::X1,
|
||||
gfx::LoadOp::CLEAR,
|
||||
gfx::StoreOp::STORE,
|
||||
device->getGeneralBarrier({
|
||||
gfx::AccessFlagBit::FRAGMENT_SHADER_READ_TEXTURE,
|
||||
gfx::AccessFlagBit::FRAGMENT_SHADER_READ_TEXTURE,
|
||||
}),
|
||||
};
|
||||
|
||||
const gfx::DepthStencilAttachment depthStencilAttachment{
|
||||
gfx::Format::DEPTH,
|
||||
gfx::SampleCount::X1,
|
||||
gfx::LoadOp::CLEAR,
|
||||
gfx::StoreOp::DISCARD,
|
||||
gfx::LoadOp::CLEAR,
|
||||
gfx::StoreOp::DISCARD,
|
||||
device->getGeneralBarrier({
|
||||
gfx::AccessFlagBit::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
||||
gfx::AccessFlagBit::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
||||
}),
|
||||
};
|
||||
|
||||
gfx::RenderPassInfo rpInfo;
|
||||
rpInfo.colorAttachments.emplace_back(colorAttachment);
|
||||
rpInfo.depthStencilAttachment = depthStencilAttachment;
|
||||
|
||||
ccstd::hash_t rpHash = cc::gfx::RenderPass::computeHash(rpInfo);
|
||||
const auto iter = renderPassHashMap.find(rpHash);
|
||||
if (iter != renderPassHashMap.end()) {
|
||||
_renderPass = iter->second;
|
||||
} else {
|
||||
_renderPass = device->createRenderPass(rpInfo);
|
||||
renderPassHashMap.insert({rpHash, _renderPass});
|
||||
}
|
||||
|
||||
auto *colorTexture = device->createTexture({
|
||||
gfx::TextureType::TEX2D,
|
||||
gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
_usedTextures.emplace_back(colorTexture);
|
||||
|
||||
gfx::Texture *depthStencilTexture = device->createTexture({
|
||||
gfx::TextureType::TEX2D,
|
||||
gfx::TextureUsageBit::DEPTH_STENCIL_ATTACHMENT | gfx::TextureUsageBit::SAMPLED,
|
||||
gfx::Format::DEPTH,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
_usedTextures.emplace_back(depthStencilTexture);
|
||||
|
||||
gfx::Framebuffer *framebuffer = device->createFramebuffer({
|
||||
_renderPass,
|
||||
{colorTexture},
|
||||
depthStencilTexture,
|
||||
});
|
||||
pipeline->getPipelineSceneData()->setShadowFramebuffer(light, framebuffer);
|
||||
}
|
||||
|
||||
void ShadowFlow::destroy() {
|
||||
_pipeline->getGlobalDSManager()->bindTexture(SHADOWMAP::BINDING, nullptr);
|
||||
_pipeline->getGlobalDSManager()->bindTexture(SPOTSHADOWMAP::BINDING, nullptr);
|
||||
|
||||
_renderPass = nullptr;
|
||||
renderPassHashMap.clear();
|
||||
_usedTextures.clear();
|
||||
_validLights.clear();
|
||||
|
||||
RenderFlow::destroy();
|
||||
}
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace cc
|
||||
72
cocos/renderer/pipeline/shadow/ShadowFlow.h
Normal file
72
cocos/renderer/pipeline/shadow/ShadowFlow.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../RenderFlow.h"
|
||||
#include "scene/Define.h"
|
||||
|
||||
namespace cc {
|
||||
namespace pipeline {
|
||||
class ForwardPipeline;
|
||||
|
||||
class CC_DLL ShadowFlow : public RenderFlow {
|
||||
public:
|
||||
ShadowFlow();
|
||||
~ShadowFlow() override;
|
||||
|
||||
static const RenderFlowInfo &getInitializeInfo();
|
||||
|
||||
bool initialize(const RenderFlowInfo &info) override;
|
||||
|
||||
void activate(RenderPipeline *pipeline) override;
|
||||
|
||||
void render(scene::Camera *camera) override;
|
||||
|
||||
void destroy() override;
|
||||
|
||||
private:
|
||||
void renderStage(gfx::DescriptorSet *globalDS, scene::Camera *camera, const scene::Light *light, gfx::Framebuffer *framebuffer, uint32_t level = 0);
|
||||
|
||||
void lightCollecting();
|
||||
|
||||
void clearShadowMap(scene::Camera *camera);
|
||||
|
||||
void resizeShadowMap(const scene::Light *light, gfx::DescriptorSet *ds);
|
||||
|
||||
void initShadowFrameBuffer(const RenderPipeline *pipeline, const scene::Light *light);
|
||||
|
||||
static RenderFlowInfo initInfo;
|
||||
|
||||
// weak reference
|
||||
gfx::RenderPass *_renderPass{nullptr};
|
||||
|
||||
// weak reference
|
||||
ccstd::vector<const scene::Light *> _validLights;
|
||||
ccstd::vector<IntrusivePtr<gfx::Texture>> _usedTextures;
|
||||
|
||||
static ccstd::unordered_map<ccstd::hash_t, IntrusivePtr<cc::gfx::RenderPass>> renderPassHashMap;
|
||||
};
|
||||
} // namespace pipeline
|
||||
} // namespace cc
|
||||
178
cocos/renderer/pipeline/shadow/ShadowStage.cpp
Normal file
178
cocos/renderer/pipeline/shadow/ShadowStage.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "ShadowStage.h"
|
||||
#include "../Define.h"
|
||||
#include "../PipelineSceneData.h"
|
||||
#include "../PipelineUBO.h"
|
||||
#include "../RenderPipeline.h"
|
||||
#include "../ShadowMapBatchedQueue.h"
|
||||
#include "gfx-base/GFXCommandBuffer.h"
|
||||
#include "gfx-base/GFXFramebuffer.h"
|
||||
#include "math/Vec2.h"
|
||||
#include "profiler/Profiler.h"
|
||||
#include "scene/Camera.h"
|
||||
#include "scene/DirectionalLight.h"
|
||||
#include "scene/Light.h"
|
||||
#include "scene/Shadow.h"
|
||||
#include "scene/SpotLight.h"
|
||||
|
||||
namespace cc {
|
||||
namespace pipeline {
|
||||
|
||||
ShadowStage::ShadowStage() = default;
|
||||
ShadowStage::~ShadowStage() = default;
|
||||
|
||||
RenderStageInfo ShadowStage::initInfo = {
|
||||
"ShadowStage",
|
||||
static_cast<uint32_t>(ForwardStagePriority::FORWARD),
|
||||
static_cast<uint32_t>(RenderFlowTag::SCENE),
|
||||
{}};
|
||||
const RenderStageInfo &ShadowStage::getInitializeInfo() { return ShadowStage::initInfo; }
|
||||
|
||||
bool ShadowStage::initialize(const RenderStageInfo &info) {
|
||||
RenderStage::initialize(info);
|
||||
auto *descriptor = ccnew RenderQueueDesc(true, RenderQueueSortMode::BACK_TO_FRONT, {"default"});
|
||||
_renderQueueDescriptors.emplace_back(descriptor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShadowStage::activate(RenderPipeline *pipeline, RenderFlow *flow) {
|
||||
RenderStage::activate(pipeline, flow);
|
||||
|
||||
_additiveShadowQueue = ccnew ShadowMapBatchedQueue(pipeline);
|
||||
_isShadowMapCleared = false;
|
||||
}
|
||||
|
||||
void ShadowStage::render(scene::Camera *camera) {
|
||||
CC_PROFILE(ShadowStageRender);
|
||||
const auto *sceneData = _pipeline->getPipelineSceneData();
|
||||
const auto *shadowInfo = sceneData->getShadows();
|
||||
|
||||
if (!_light || !_framebuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_light->getType() == scene::LightType::DIRECTIONAL) {
|
||||
const auto *dirLight = static_cast<const scene::DirectionalLight *>(_light);
|
||||
if (!dirLight->isShadowEnabled()) return;
|
||||
}
|
||||
|
||||
if (_light->getType() == scene::LightType::SPOT) {
|
||||
const auto *spotLight = static_cast<const scene::SpotLight *>(_light);
|
||||
if (!spotLight->isShadowEnabled()) return;
|
||||
}
|
||||
|
||||
auto *cmdBuffer = _pipeline->getCommandBuffers()[0];
|
||||
_pipeline->getPipelineUBO()->updateShadowUBOLight(_globalDS, _light, _level);
|
||||
_additiveShadowQueue->gatherLightPasses(camera, _light, cmdBuffer, _level);
|
||||
|
||||
const Vec2 &shadowMapSize = shadowInfo->getSize();
|
||||
switch (_light->getType()) {
|
||||
case scene::LightType::DIRECTIONAL: {
|
||||
const auto *mainLight = static_cast<const scene::DirectionalLight *>(_light);
|
||||
if (mainLight->isShadowFixedArea() || mainLight->getCSMLevel() == scene::CSMLevel::LEVEL_1 || !sceneData->getCSMSupported()) {
|
||||
_renderArea.x = 0;
|
||||
_renderArea.y = 0;
|
||||
_renderArea.width = static_cast<uint32_t>(shadowMapSize.x);
|
||||
_renderArea.height = static_cast<uint32_t>(shadowMapSize.y);
|
||||
} else {
|
||||
const gfx::Device *device = gfx::Device::getInstance();
|
||||
const float screenSpaceSignY = device->getCapabilities().screenSpaceSignY;
|
||||
_renderArea.x = static_cast<int>(static_cast<float>(_level % 2) * 0.5F * shadowMapSize.x);
|
||||
if (screenSpaceSignY > 0.0F) {
|
||||
_renderArea.y = static_cast<int>((1 - floorf(static_cast<float>(_level) / 2)) * 0.5F * shadowMapSize.y);
|
||||
} else {
|
||||
_renderArea.y = static_cast<int>((floorf(static_cast<float>(_level) / 2)) * 0.5F * shadowMapSize.y);
|
||||
}
|
||||
_renderArea.width = static_cast<int>(0.5F * shadowMapSize.x);
|
||||
_renderArea.height = static_cast<int>(0.5F * shadowMapSize.y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case scene::LightType::SPOT: {
|
||||
_renderArea.x = 0;
|
||||
_renderArea.y = 0;
|
||||
_renderArea.width = static_cast<uint32_t>(shadowMapSize.x);
|
||||
_renderArea.height = static_cast<uint32_t>(shadowMapSize.y);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_clearColors[0] = {1.0F, 1.0F, 1.0F, 1.0F};
|
||||
auto *renderPass = _framebuffer->getRenderPass();
|
||||
|
||||
cmdBuffer->beginRenderPass(renderPass, _framebuffer, _renderArea,
|
||||
_clearColors, camera->getClearDepth(), camera->getClearStencil());
|
||||
|
||||
const ccstd::array<uint32_t, 1> globalOffsets = {_pipeline->getPipelineUBO()->getCurrentCameraUBOOffset()};
|
||||
cmdBuffer->bindDescriptorSet(globalSet, _globalDS, utils::toUint(globalOffsets.size()), globalOffsets.data());
|
||||
_additiveShadowQueue->recordCommandBuffer(_device, renderPass, cmdBuffer);
|
||||
|
||||
cmdBuffer->endRenderPass();
|
||||
_isShadowMapCleared = false;
|
||||
}
|
||||
|
||||
void ShadowStage::destroy() {
|
||||
_framebuffer = nullptr;
|
||||
_globalDS = nullptr;
|
||||
_light = nullptr;
|
||||
|
||||
CC_SAFE_DESTROY_AND_DELETE(_additiveShadowQueue);
|
||||
|
||||
RenderStage::destroy();
|
||||
}
|
||||
|
||||
void ShadowStage::clearFramebuffer(const scene::Camera *camera) {
|
||||
if (!_light || !_framebuffer || _isShadowMapCleared) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto *sceneData = _pipeline->getPipelineSceneData();
|
||||
const auto *shadowInfo = sceneData->getShadows();
|
||||
const Vec4 &viewport = camera->getViewport();
|
||||
const Vec2 &shadowMapSize = shadowInfo->getSize();
|
||||
|
||||
auto *cmdBuffer = _pipeline->getCommandBuffers()[0];
|
||||
|
||||
_renderArea.x = static_cast<int>(viewport.x * shadowMapSize.x);
|
||||
_renderArea.y = static_cast<int>(viewport.y * shadowMapSize.y);
|
||||
_renderArea.width = static_cast<uint32_t>(viewport.z * shadowMapSize.x * sceneData->getShadingScale());
|
||||
_renderArea.height = static_cast<uint32_t>(viewport.w * shadowMapSize.y * sceneData->getShadingScale());
|
||||
|
||||
_clearColors[0] = {1.0F, 1.0F, 1.0F, 1.0F};
|
||||
auto *renderPass = _framebuffer->getRenderPass();
|
||||
|
||||
cmdBuffer->beginRenderPass(renderPass, _framebuffer, _renderArea,
|
||||
_clearColors, camera->getClearDepth(), camera->getClearStencil());
|
||||
|
||||
cmdBuffer->endRenderPass();
|
||||
_isShadowMapCleared = true;
|
||||
}
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace cc
|
||||
71
cocos/renderer/pipeline/shadow/ShadowStage.h
Normal file
71
cocos/renderer/pipeline/shadow/ShadowStage.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "../RenderStage.h"
|
||||
|
||||
namespace cc {
|
||||
namespace pipeline {
|
||||
class RenderQueue;
|
||||
class ShadowMapBatchedQueue;
|
||||
|
||||
class CC_DLL ShadowStage : public RenderStage {
|
||||
public:
|
||||
ShadowStage();
|
||||
~ShadowStage() override;
|
||||
|
||||
static const RenderStageInfo &getInitializeInfo();
|
||||
|
||||
bool initialize(const RenderStageInfo &info) override;
|
||||
void destroy() override;
|
||||
void render(scene::Camera *camera) override;
|
||||
void activate(RenderPipeline *pipeline, RenderFlow *flow) override;
|
||||
|
||||
inline void setFramebuffer(gfx::Framebuffer *framebuffer) { _framebuffer = framebuffer; }
|
||||
inline void setUsage(gfx::DescriptorSet *globalDS, const scene::Light *light, gfx::Framebuffer *framebuffer, uint32_t level = 0) {
|
||||
_globalDS = globalDS;
|
||||
_light = light;
|
||||
_framebuffer = framebuffer;
|
||||
_level = level;
|
||||
}
|
||||
|
||||
void clearFramebuffer(const scene::Camera *camera);
|
||||
|
||||
private:
|
||||
static RenderStageInfo initInfo;
|
||||
|
||||
bool _isShadowMapCleared{false};
|
||||
|
||||
uint32_t _level;
|
||||
|
||||
gfx::DescriptorSet *_globalDS{nullptr};
|
||||
const scene::Light *_light{nullptr};
|
||||
gfx::Framebuffer *_framebuffer{nullptr};
|
||||
ShadowMapBatchedQueue *_additiveShadowQueue{nullptr};
|
||||
|
||||
gfx::Rect _renderArea;
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace cc
|
||||
Reference in New Issue
Block a user