Orthographic octree selection seems to be working

This commit is contained in:
Olivier Prat 2018-02-02 09:40:57 +01:00
parent d422545c78
commit 3804917cf4
15 changed files with 152 additions and 77 deletions

View file

@ -62,7 +62,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) {
samplerDesc._wrapModeU = Sampler::WRAP_BORDER;
samplerDesc._wrapModeV = Sampler::WRAP_BORDER;
samplerDesc._filter = Sampler::FILTER_MIN_MAG_LINEAR;
samplerDesc._comparisonFunc = LESS_EQUAL;
samplerDesc._comparisonFunc = LESS;
depthTexture->setSampler(Sampler(samplerDesc));
framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);

View file

@ -64,8 +64,14 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc
return voxelSizeScale / powf(2.0f, renderLevel);
}
float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
const float maxScale = (float)TREE_SCALE;
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO;
return atan(maxScale / visibleDistanceAtMaxScale);
}
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) {
// Smallest visible element is 1cm
const float smallestSize = 0.01f;
return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
}

View file

@ -25,7 +25,8 @@ float calculateRenderAccuracy(const glm::vec3& position,
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust);
// MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees
const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians

View file

@ -214,9 +214,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
}
const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup");
const auto queryResolution = setupOutput.getN<RenderShadowSetup::Outputs>(2);
// Fetch and cull the items from the scene
static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
const auto fetchInput = render::Varying(shadowCasterFilter);
const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);
const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
const auto shadowItems = task.addJob<FetchSpatialSelection>("FetchShadowSelection", selectionInputs);
@ -281,14 +282,15 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
output.edit0() = args->_renderMode;
output.edit1() = args->_sizeScale;
output.edit2() = glm::ivec2(0, 0);
const auto globalShadow = lightStage->getCurrentKeyShadow();
if (globalShadow) {
globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
auto& firstCascadeFrustum = globalShadow->getCascade(0).getFrustum();
auto& firstCascade = globalShadow->getCascade(0);
auto& firstCascadeFrustum = firstCascade.getFrustum();
unsigned int cascadeIndex;
_coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition());
_coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation());
// Adjust each cascade frustum
for (cascadeIndex = 0; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) {
@ -297,19 +299,29 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR,
bias._constant, bias._slope);
}
// Now adjust coarse frustum bounds
auto left = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getRight());
auto right = glm::dot(firstCascadeFrustum->getFarTopRight(), firstCascadeFrustum->getRight());
auto top = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getUp());
auto bottom = glm::dot(firstCascadeFrustum->getFarBottomRight(), firstCascadeFrustum->getUp());
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 (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) {
auto& cascadeFrustum = globalShadow->getCascade(cascadeIndex).getFrustum();
auto cascadeLeft = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getRight());
auto cascadeRight = glm::dot(cascadeFrustum->getFarTopRight(), cascadeFrustum->getRight());
auto cascadeTop = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getUp());
auto cascadeBottom = glm::dot(cascadeFrustum->getFarBottomRight(), cascadeFrustum->getUp());
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);
@ -319,6 +331,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
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();
@ -326,10 +341,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
args->pushViewFrustum(*_coarseShadowFrustum);
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) {
// Set to ridiculously high amount to prevent solid angle culling in octree selection
args->_sizeScale = 1e16f;
}
// 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.edit2() = queryResolution;
}
}

View file

@ -71,10 +71,10 @@ public:
float constantBias1{ 0.15f };
float constantBias2{ 0.15f };
float constantBias3{ 0.15f };
float slopeBias0{ 0.55f };
float slopeBias1{ 0.55f };
float slopeBias2{ 0.55f };
float slopeBias3{ 0.55f };
float slopeBias0{ 0.6f };
float slopeBias1{ 0.6f };
float slopeBias2{ 0.6f };
float slopeBias3{ 0.6f };
signals:
void dirty();
@ -82,7 +82,7 @@ signals:
class RenderShadowSetup {
public:
using Outputs = render::VaryingSet2<RenderArgs::RenderMode, float>;
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, float, glm::ivec2>;
using Config = RenderShadowSetupConfig;
using JobModel = render::Job::ModelO<RenderShadowSetup, Outputs, Config>;

View file

@ -79,15 +79,10 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) +
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3])
);
return shadowAttenuation;
}
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) {
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
// If a point is not in the map, do not attenuate
return 1.0;
}
float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL;
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
}

View file

@ -53,8 +53,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
}
bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) {
bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0));
bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1));
bvec2 greaterThanZero = greaterThan(cascadeTexCoords.xy, vec2(0));
bvec2 lessThanOne = lessThan(cascadeTexCoords.xy, vec2(1));
return all(greaterThanZero) && all(lessThanOne);
}

View file

@ -14,8 +14,8 @@
#include <algorithm>
#include <assert.h>
#include <OctreeUtils.h>
#include <PerfStat.h>
#include <OctreeUtils.h>
using namespace render;
@ -33,7 +33,7 @@ struct Test {
_renderDetails(renderDetails) {
// FIXME: Keep this code here even though we don't use it yet
/*_eyePos = _args->getViewFrustum().getPosition();
float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust));
float a = glm::degrees(Octree::getPerspectiveAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust));
auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees
angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree
auto tanAlpha = tan(angle);
@ -130,7 +130,8 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inpu
// start fresh
outSelection.clear();
auto& filter = inputs;
auto& filter = inputs.get0();
auto frustumResolution = inputs.get1();
if (!filter.selectsNothing()) {
assert(renderContext->args);
@ -149,8 +150,19 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inpu
}
// Octree selection!
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, angle);
float threshold = 0.0f;
if (queryFrustum.isPerspective()) {
threshold = getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust);
if (frustumResolution.y > 0) {
threshold = glm::max(queryFrustum.getFieldOfView() / frustumResolution.y, threshold);
}
} else {
threshold = getOrthographicAccuracySize(args->_sizeScale, args->_boundaryLevelAdjust);
glm::vec2 frustumSize = glm::vec2(queryFrustum.getWidth(), queryFrustum.getHeight());
const auto pixelResolution = frustumResolution.x > 0 ? frustumResolution : glm::ivec2(2048, 2048);
threshold = glm::max(threshold, glm::min(frustumSize.x / pixelResolution.x, frustumSize.y / pixelResolution.y));
}
scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, threshold);
}
}
@ -371,7 +383,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext,
// Now get the bound, and
// filter individually against the _filter
// inside & fit items: filter only, culling is disabled
// inside & fit items: filter only
{
PerformanceTimer perfTimer("insideFitItems");
for (auto id : inSelection.insideItems) {
@ -383,7 +395,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext,
}
}
// inside & subcell items: filter only, culling is disabled
// inside & subcell items: filter only
{
PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.insideSubcellItems) {
@ -395,7 +407,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext,
}
}
// partial & fit items: filter only, culling is disabled
// partial & fit items: filter only
{
PerformanceTimer perfTimer("partialFitItems");
for (auto id : inSelection.partialItems) {
@ -407,7 +419,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext,
}
}
// partial & subcell items: filter only, culling is disabled
// partial & subcell items: filter only
{
PerformanceTimer perfTimer("partialSmallItems");
for (auto id : inSelection.partialSubcellItems) {

View file

@ -53,9 +53,10 @@ namespace render {
bool _justFrozeFrustum{ false };
ViewFrustum _frozenFrustum;
float _lodAngle;
public:
using Config = FetchSpatialTreeConfig;
using Inputs = ItemFilter;
using Inputs = render::VaryingSet2<ItemFilter, glm::ivec2>;
using JobModel = Job::ModelIO<FetchSpatialTree, Inputs, ItemSpatialTree::ItemSelection, Config>;
FetchSpatialTree() {}

View file

@ -148,7 +148,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS
}
// Draw the LOD Reticle
{
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
float angle = glm::degrees(getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
Transform crosshairModel;
crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0));
crosshairModel.setScale(1000.0f * tanf(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO

View file

@ -23,7 +23,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
// CPU jobs:
// Fetch and cull the items from the scene
const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered();
const auto fetchInput = render::Varying(filter);
const auto fetchInput = FetchSpatialTree::Inputs(filter, glm::ivec2(0,0)).asVarying();
const auto spatialSelection = task.addJob<FetchSpatialTree>("FetchSceneSelection", fetchInput);
const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, render::Varying(filter)).asVarying();
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM);

View file

@ -12,9 +12,26 @@
#include <ViewFrustum.h>
using namespace render;
void Octree::PerspectiveSelector::setAngle(float a) {
const float MAX_LOD_ANGLE = glm::radians(45.0f);
const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f);
angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a));
auto tanAlpha = tan(angle);
squareTanAlpha = (float)(tanAlpha * tanAlpha);
}
float Octree::PerspectiveSelector::testThreshold(const Coord3f& point, float size) const {
auto eyeToPoint = point - eyePos;
return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
}
float Octree::OrthographicSelector::testThreshold(const Coord3f& point, float size) const {
return (size * size) - squareMinSize;
}
const float Octree::INV_DEPTH_DIM[] = {
1.0f,
@ -520,10 +537,9 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum
// Test for lod
auto cellLocation = cell.getlocation();
float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
if (lod < 0.0f) {
float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
if (test < 0.0f) {
return 0;
break;
}
// Select this cell partially in frustum
@ -543,13 +559,13 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum
}
int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const {
int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const {
int numSelectedsIn = (int) selection.size();
auto cell = getConcreteCell(cellID);
auto cellLocation = cell.getlocation();
float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
if (lod < 0.0f) {
float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
if (test < 0.0f) {
return 0;
}
@ -580,24 +596,40 @@ int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside)
return (int) selection.size() - numSelectedsIn;
}
int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const {
int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const {
auto worldPlanes = frustum.getPlanes();
FrustumSelector selector;
for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) {
::Plane octPlane;
octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH));
selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
if (frustum.isPerspective()) {
PerspectiveSelector selector;
for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) {
::Plane octPlane;
octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH));
selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
}
selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH);
selector.setAngle(threshold);
return Octree::select(selection, selector);
} else {
OrthographicSelector selector;
for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) {
::Plane octPlane;
octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH));
selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
}
// Divide the threshold (which is in world distance units) by the dimension of the octree
// as all further computations will be done in normalized octree units
threshold *= getInvCellWidth(ROOT_DEPTH);
selector.setSize(threshold);
return Octree::select(selection, selector);
}
selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH);
selector.setAngle(glm::radians(lodAngle));
return Octree::select(selection, selector);
}
int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const {
selectCells(selection.cellSelection, frustum, lodAngle);
int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum,
float threshold) const {
selectCells(selection.cellSelection, frustum, threshold);
// Just grab the items in every selected bricks
for (auto brickId : selection.cellSelection.insideBricks) {

View file

@ -312,23 +312,27 @@ namespace render {
class FrustumSelector {
public:
Coord4f frustum[6];
virtual ~FrustumSelector() {}
virtual float testThreshold(const Coord3f& point, float size) const = 0;
};
class PerspectiveSelector : public FrustumSelector {
public:
Coord3f eyePos;
float angle;
float squareTanAlpha;
const float MAX_LOD_ANGLE = glm::radians(45.0f);
const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f);
void setAngle(float a);
float testThreshold(const Coord3f& point, float size) const override;
};
void setAngle(float a) {
angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a));
auto tanAlpha = tan(angle);
squareTanAlpha = (float)(tanAlpha * tanAlpha);
}
class OrthographicSelector : public FrustumSelector {
public:
float squareMinSize;
float testSolidAngle(const Coord3f& point, float size) const {
auto eyeToPoint = point - eyePos;
return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
}
void setSize(float a) { squareMinSize = a * a; }
float testThreshold(const Coord3f& point, float size) const override;
};
int select(CellSelection& selection, const FrustumSelector& selector) const;
@ -443,7 +447,7 @@ namespace render {
Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey);
// Selection and traverse
int selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const;
int selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const;
class ItemSelection {
public:
@ -469,7 +473,8 @@ namespace render {
}
};
int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const;
int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum,
float threshold) const;
};
}

View file

@ -847,3 +847,7 @@ void ViewFrustum::tesselateSides(const glm::vec3 points[8], Triangle triangles[8
triangle.v2 = points[vertexIndices[2]];
}
}
bool ViewFrustum::isPerspective() const {
return _projection[3][2] != 0.0f && _projection[2][3] != 0.0f && _projection[3][3] == 0.0f;
}

View file

@ -49,6 +49,7 @@ public:
// setters for lens attributes
void setProjection(const glm::mat4 & projection);
void setFocalLength(float focalLength) { _focalLength = focalLength; }
bool isPerspective() const;
// getters for lens attributes
const glm::mat4& getProjection() const { return _projection; }