mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-07-12 19:51:42 +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)
|
||||
, 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();
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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
|
||||
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…
Reference in a new issue