mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-10-04 12:56:34 +00:00
Make use of shared shadow maps in the brute force method
This commit is contained in:
parent
5beb0bc799
commit
1bebe51c29
3 changed files with 96 additions and 59 deletions
|
@ -233,11 +233,19 @@ namespace Misc
|
||||||
, mTechnique(technique)
|
, mTechnique(technique)
|
||||||
, mGeometryShaderMask(geometryShaderMask)
|
, mGeometryShaderMask(geometryShaderMask)
|
||||||
, mNoShaderMask(noShaderMask)
|
, mNoShaderMask(noShaderMask)
|
||||||
|
, mMasterConfig(new SharedShadowMapConfig)
|
||||||
|
, mSlaveConfig(new SharedShadowMapConfig)
|
||||||
|
, mSharedShadowMaps(Settings::Manager::getBool("shared shadow maps", "Stereo"))
|
||||||
{
|
{
|
||||||
if (technique == Technique::None)
|
if (technique == Technique::None)
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
mMasterConfig->_id = "STEREO";
|
||||||
|
mMasterConfig->_master = true;
|
||||||
|
mSlaveConfig->_id = "STEREO";
|
||||||
|
mSlaveConfig->_master = false;
|
||||||
|
|
||||||
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
SceneUtil::FindByNameVisitor findScene("Scene Root");
|
||||||
mRoot->accept(findScene);
|
mRoot->accept(findScene);
|
||||||
mScene = findScene.mFoundNode;
|
mScene = findScene.mFoundNode;
|
||||||
|
@ -292,6 +300,12 @@ namespace Misc
|
||||||
mRightCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
mRightCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
mRightCamera->setCullMask(mMainCamera->getCullMask());
|
mRightCamera->setCullMask(mMainCamera->getCullMask());
|
||||||
|
|
||||||
|
if (mSharedShadowMaps)
|
||||||
|
{
|
||||||
|
mLeftCamera->setUserData(mMasterConfig);
|
||||||
|
mRightCamera->setUserData(mSlaveConfig);
|
||||||
|
}
|
||||||
|
|
||||||
// Slave cameras must have their viewports defined immediately
|
// Slave cameras must have their viewports defined immediately
|
||||||
auto width = mMainCamera->getViewport()->width();
|
auto width = mMainCamera->getViewport()->width();
|
||||||
auto height = mMainCamera->getViewport()->height();
|
auto height = mMainCamera->getViewport()->height();
|
||||||
|
@ -366,65 +380,66 @@ namespace Misc
|
||||||
auto width = mMainCamera->getViewport()->width();
|
auto width = mMainCamera->getViewport()->width();
|
||||||
auto height = mMainCamera->getViewport()->height();
|
auto height = mMainCamera->getViewport()->height();
|
||||||
|
|
||||||
|
// 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 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);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 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)
|
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)
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Update camera with frustum matrices
|
// Update camera with frustum matrices
|
||||||
mMainCamera->setViewMatrix(frustumViewMatrix);
|
mMainCamera->setViewMatrix(frustumViewMatrix);
|
||||||
|
@ -439,6 +454,18 @@ namespace Misc
|
||||||
|
|
||||||
mLeftCamera->setViewport(0, 0, width / 2, height);
|
mLeftCamera->setViewport(0, 0, width / 2, height);
|
||||||
mRightCamera->setViewport(width / 2, 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());
|
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)
|
void disableStereoForCamera(osg::Camera* camera)
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <components/sceneutil/mwshadowtechnique.hpp>
|
||||||
|
|
||||||
// Some cursed headers like to define these
|
// Some cursed headers like to define these
|
||||||
#if defined(near) || defined(far)
|
#if defined(near) || defined(far)
|
||||||
#undef near
|
#undef near
|
||||||
|
@ -122,6 +124,11 @@ namespace Misc
|
||||||
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
|
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
|
||||||
osg::ref_ptr<osg::Camera> mRightCamera{ 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
|
// Camera viewports
|
||||||
bool flipViewOrder{ true };
|
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.
|
# 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.
|
# 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
|
stereo method = GeometryShader
|
||||||
|
|
||||||
|
# May accelerate the BruteForce method when shadows are enabled
|
||||||
|
shared shadow maps = true
|
Loading…
Reference in a new issue