Make use of shared shadow maps in the brute force method

pull/615/head
Mads Buvik Sandvei 4 years ago
parent 5beb0bc799
commit 1bebe51c29

@ -233,11 +233,19 @@ namespace Misc
, mTechnique(technique)
, mGeometryShaderMask(geometryShaderMask)
, mNoShaderMask(noShaderMask)
, mMasterConfig(new SharedShadowMapConfig)
, mSlaveConfig(new SharedShadowMapConfig)
, mSharedShadowMaps(Settings::Manager::getBool("shared shadow maps", "Stereo"))
{
if (technique == Technique::None)
// Do nothing
return;
mMasterConfig->_id = "STEREO";
mMasterConfig->_master = true;
mSlaveConfig->_id = "STEREO";
mSlaveConfig->_master = false;
SceneUtil::FindByNameVisitor findScene("Scene Root");
mRoot->accept(findScene);
mScene = findScene.mFoundNode;
@ -292,6 +300,12 @@ namespace Misc
mRightCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mRightCamera->setCullMask(mMainCamera->getCullMask());
if (mSharedShadowMaps)
{
mLeftCamera->setUserData(mMasterConfig);
mRightCamera->setUserData(mSlaveConfig);
}
// Slave cameras must have their viewports defined immediately
auto width = mMainCamera->getViewport()->width();
auto height = mMainCamera->getViewport()->height();
@ -366,65 +380,66 @@ namespace Misc
auto width = mMainCamera->getViewport()->width();
auto height = mMainCamera->getViewport()->height();
if (mTechnique == Technique::GeometryShader_IndexedViewports)
// To correctly cull when drawing stereo using the geometry shader, the main camera must
// draw a fake view+perspective that includes the full frustums of both the left and right eyes.
// This frustum will be computed as a perspective frustum from a position P slightly behind the eyes L and R
// where it creates the minimum frustum encompassing both eyes' frustums.
// NOTE: I make an assumption that the eyes lie in a horizontal plane relative to the base view,
// and lie mirrored around the Y axis (straight ahead).
// Re-think this if that turns out to be a bad assumption.
View frustumView;
// Compute Frustum angles. A simple min/max.
/* Example values for reference:
Left:
angleLeft -0.767549932 float
angleRight 0.620896876 float
angleDown -0.837898076 float
angleUp 0.726982594 float
Right:
angleLeft -0.620896876 float
angleRight 0.767549932 float
angleDown -0.837898076 float
angleUp 0.726982594 float
*/
frustumView.fov.angleLeft = std::min(left.fov.angleLeft, right.fov.angleLeft);
frustumView.fov.angleRight = std::max(left.fov.angleRight, right.fov.angleRight);
frustumView.fov.angleDown = std::min(left.fov.angleDown, right.fov.angleDown);
frustumView.fov.angleUp = std::max(left.fov.angleUp, right.fov.angleUp);
// Check that the case works for this approach
auto maxAngle = std::max(frustumView.fov.angleRight - frustumView.fov.angleLeft, frustumView.fov.angleUp - frustumView.fov.angleDown);
if (maxAngle > osg::PI)
{
// To correctly cull when drawing stereo using the geometry shader, the main camera must
// draw a fake view+perspective that includes the full frustums of both the left and right eyes.
// This frustum will be computed as a perspective frustum from a position P slightly behind the eyes L and R
// where it creates the minimum frustum encompassing both eyes' frustums.
// NOTE: I make an assumption that the eyes lie in a horizontal plane relative to the base view,
// and lie mirrored around the Y axis (straight ahead).
// Re-think this if that turns out to be a bad assumption.
View frustumView;
// Compute Frustum angles. A simple min/max.
/* Example values for reference:
Left:
angleLeft -0.767549932 float
angleRight 0.620896876 float
angleDown -0.837898076 float
angleUp 0.726982594 float
Right:
angleLeft -0.620896876 float
angleRight 0.767549932 float
angleDown -0.837898076 float
angleUp 0.726982594 float
*/
frustumView.fov.angleLeft = std::min(left.fov.angleLeft, right.fov.angleLeft);
frustumView.fov.angleRight = std::max(left.fov.angleRight, right.fov.angleRight);
frustumView.fov.angleDown = std::min(left.fov.angleDown, right.fov.angleDown);
frustumView.fov.angleUp = std::max(left.fov.angleUp, right.fov.angleUp);
// Check that the case works for this approach
auto maxAngle = std::max(frustumView.fov.angleRight - frustumView.fov.angleLeft, frustumView.fov.angleUp - frustumView.fov.angleDown);
if (maxAngle > osg::PI)
{
Log(Debug::Error) << "Total FOV exceeds 180 degrees. Case cannot be culled in single-pass VR. Disabling culling to cope. Consider switching to dual-pass VR.";
mMainCamera->setCullingActive(false);
return;
// TODO: An explicit frustum projection could cope, so implement that later. Guarantee you there will be VR headsets with total fov > 180 in the future. Maybe already.
}
Log(Debug::Error) << "Total FOV exceeds 180 degrees. Case cannot be culled in single-pass VR. Disabling culling to cope. Consider switching to dual-pass VR.";
mMainCamera->setCullingActive(false);
return;
// TODO: An explicit frustum projection could cope, so implement that later. Guarantee you there will be VR headsets with total horizontal fov > 180 in the future. Maybe already.
}
// Use the law of sines on the triangle spanning PLR to determine P
double angleLeft = std::abs(frustumView.fov.angleLeft);
double angleRight = std::abs(frustumView.fov.angleRight);
double lengthRL = (rightEye - leftEye).length();
double ratioRL = lengthRL / std::sin(osg::PI - angleLeft - angleRight);
double lengthLP = ratioRL * std::sin(angleRight);
// Use the law of sines on the triangle spanning PLR to determine P
double angleLeft = std::abs(frustumView.fov.angleLeft);
double angleRight = std::abs(frustumView.fov.angleRight);
double lengthRL = (rightEye - leftEye).length();
double ratioRL = lengthRL / std::sin(osg::PI - angleLeft - angleRight);
double lengthLP = ratioRL * std::sin(angleRight);
osg::Vec3d directionLP = osg::Vec3(std::cos(-angleLeft), std::sin(-angleLeft), 0);
osg::Vec3d LP = directionLP * lengthLP;
frustumView.pose.position = leftEye + LP;
//frustumView.pose.position.x() += 1000;
osg::Vec3d directionLP = osg::Vec3(std::cos(-angleLeft), std::sin(-angleLeft), 0);
osg::Vec3d LP = directionLP * lengthLP;
frustumView.pose.position = leftEye + LP;
//frustumView.pose.position.x() += 1000;
// Base view position is 0.0, by definition.
// The length of the vector P is therefore the required offset to near/far.
auto nearFarOffset = frustumView.pose.position.length();
// Base view position is 0.0, by definition.
// The length of the vector P is therefore the required offset to near/far.
auto nearFarOffset = frustumView.pose.position.length();
// Generate the frustum matrices
auto frustumViewMatrix = viewMatrix * frustumView.pose.viewMatrix(true);
auto frustumProjectionMatrix = frustumView.fov.perspectiveMatrix(near + nearFarOffset, far + nearFarOffset);
// Generate the frustum matrices
auto frustumViewMatrix = viewMatrix * frustumView.pose.viewMatrix(true);
auto frustumProjectionMatrix = frustumView.fov.perspectiveMatrix(near + nearFarOffset, far + nearFarOffset);
if (mTechnique == Technique::GeometryShader_IndexedViewports)
{
// Update camera with frustum matrices
mMainCamera->setViewMatrix(frustumViewMatrix);
@ -439,6 +454,18 @@ namespace Misc
mLeftCamera->setViewport(0, 0, width / 2, height);
mRightCamera->setViewport(width / 2, 0, width / 2, height);
if (mMasterConfig->_projection == nullptr)
mMasterConfig->_projection = new osg::RefMatrix;
if (mMasterConfig->_modelView == nullptr)
mMasterConfig->_modelView = new osg::RefMatrix;
if (mSharedShadowMaps)
{
mMasterConfig->_referenceFrame = mMainCamera->getReferenceFrame();
mMasterConfig->_modelView->set(frustumViewMatrix);
mMasterConfig->_projection->set(projectionMatrix);
}
}
}
@ -462,9 +489,9 @@ namespace Misc
stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix() * mRightCamera->getProjectionMatrix());
}
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb)
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb_)
{
this->cb = cb;
cb = cb_;
}
void disableStereoForCamera(osg::Camera* camera)

@ -8,6 +8,8 @@
#include <memory>
#include <components/sceneutil/mwshadowtechnique.hpp>
// Some cursed headers like to define these
#if defined(near) || defined(far)
#undef near
@ -122,6 +124,11 @@ namespace Misc
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
using SharedShadowMapConfig = SceneUtil::MWShadowTechnique::SharedShadowMapConfig;
osg::ref_ptr<SharedShadowMapConfig> mMasterConfig;
osg::ref_ptr<SharedShadowMapConfig> mSlaveConfig;
bool mSharedShadowMaps;
// Camera viewports
bool flipViewOrder{ true };

@ -964,3 +964,6 @@ stereo enabled = false
# BruteForce: Generates stereo using two cameras and two cull/render passes. Choose this if your game is GPU-bound.
# GeometryShader: Generates stereo in a single pass using automatically generated geometry shaders. May break custom shaders. Choose this if your game is CPU-bound.
stereo method = GeometryShader
# May accelerate the BruteForce method when shadows are enabled
shared shadow maps = true
Loading…
Cancel
Save