Geometry shader stereo functional

pull/615/head
Mads Buvik Sandvei 4 years ago
parent b3f2373875
commit 9c171869cb

@ -23,12 +23,16 @@
#include <components/sdlutil/sdlgraphicswindow.hpp>
#include <components/sdlutil/imagetosurface.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/resource/stats.hpp>
#include <components/compiler/extensions0.hpp>
#include <components/misc/stereo.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/files/configurationmanager.hpp>
@ -692,12 +696,31 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
window->playVideo(logo, true);
}
mStereoEnabled = true; //!< TODO: TEMP
// geometry shader must be enabled before the RenderingManager sets up any shaders
// therefore this part is separate from the rest of stereo setup.
if (mStereoEnabled)
{
mResourceSystem->getSceneManager()->getShaderManager().enableGeometryShader(true);
}
// Create the world
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
mEnvironment.getWorld()->setupPlayer();
// Set up stereo
if (mStereoEnabled)
{
// Mask in everything that does not currently use shaders.
// Remove that altogether when the sky finally uses them.
auto noShaderMask = MWRender::VisMask::Mask_Sky | MWRender::VisMask::Mask_Sun | MWRender::VisMask::Mask_WeatherParticles;
auto geometryShaderMask = mViewer->getCamera()->getCullMask() & ~noShaderMask;
mStereoView.reset(new Misc::StereoView(mViewer, geometryShaderMask, noShaderMask | MWRender::VisMask::Mask_Scene));
}
window->setStore(mEnvironment.getWorld()->getStore());
window->initUI();

@ -33,6 +33,11 @@ namespace Compiler
class Context;
}
namespace Misc
{
class StereoView;
}
namespace MWScript
{
class ScriptManager;
@ -85,6 +90,10 @@ namespace OMW
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
std::string mCellName;
std::vector<std::string> mContentFiles;
bool mStereoEnabled;
std::unique_ptr<Misc::StereoView> mStereoView;
bool mSkipMenu;
bool mUseSound;
bool mCompileAll;

@ -372,20 +372,6 @@ namespace MWRender
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
mStateUpdater->setFogEnd(mViewDistance);
////// Indexed viewports and related uniforms
sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoViewOffsets", 2));
sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoProjections", 2));
mUniformStereoViewOffsets = sceneRoot->getOrCreateStateSet()->getUniform("stereoViewOffsets");
mUniformStereoProjections = sceneRoot->getOrCreateStateSet()->getUniform("stereoProjections");
mUniformStereoViewOffsets->setElement(0, osg::Matrix::identity());
mUniformStereoViewOffsets->setElement(1, osg::Matrix::identity());
auto width = mViewer->getCamera()->getViewport()->width();
auto height = mViewer->getCamera()->getViewport()->height();
sceneRoot->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height));
sceneRoot->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(1, width / 2, 0, width / 2, height));
////// Near far uniforms
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
@ -1243,9 +1229,6 @@ namespace MWRender
fov = mFieldOfViewOverride;
mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance);
mUniformStereoProjections->setElement(0, mViewer->getCamera()->getProjectionMatrix());
mUniformStereoProjections->setElement(1, mViewer->getCamera()->getProjectionMatrix());
mUniformNear->set(mNearClip);
mUniformFar->set(mViewDistance);

@ -10,6 +10,7 @@
#include <osg/PositionAttitudeTransform>
#include <osg/ClipNode>
#include <osg/FrontFace>
#include <osg/ViewportIndexed>
#include <osgDB/ReadFile>
@ -29,6 +30,7 @@
#include <components/sceneutil/waterutil.hpp>
#include <components/misc/constants.hpp>
#include <components/misc/stereo.hpp>
#include <components/nifosg/controller.hpp>
@ -246,6 +248,7 @@ public:
setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting);
setNodeMask(Mask_RenderToTexture);
setViewport(0, 0, rttSize, rttSize);
Misc::enableStereoForCamera(this, true);
// No need for Update traversal since the scene is already updated as part of the main scene graph
// A double update would mess with the light collection (in addition to being plain redundant)
@ -341,6 +344,8 @@ public:
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
setViewport(0, 0, rttSize, rttSize);
Misc::enableStereoForCamera(this, true);
// No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph
// A double update would mess with the light collection (in addition to being plain redundant)
@ -443,6 +448,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900);
mWaterGeom->setDrawCallback(new DepthClampCallback);
mWaterGeom->setNodeMask(Mask_Water);
mWaterGeom->setDataVariance(osg::Object::STATIC);
mWaterNode = new osg::PositionAttitudeTransform;
mWaterNode->setName("Water Root");
@ -592,6 +598,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
// use a define map to conditionally compile the shader
std::map<std::string, std::string> defineMap;
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(refraction ? "1" : "0")));
defineMap["geometryShader"] = "1";
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
osg::ref_ptr<osg::Shader> vertexShader (shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX));
@ -637,9 +644,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
shaderStateset->addUniform(mRainIntensityUniform.get());
osg::ref_ptr<osg::Program> program (new osg::Program);
program->addShader(vertexShader);
program->addShader(fragmentShader);
auto program = shaderMgr.getProgram(vertexShader, fragmentShader);
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
node->setStateSet(shaderStateset);

@ -86,7 +86,7 @@ add_component_dir (esmterrain
)
add_component_dir (misc
gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache
gcd constants utf8stream stringops resourcehelpers rng messageformatparser weakcache stereo
)
add_component_dir (debug

@ -0,0 +1,463 @@
#include "stereo.hpp"
#include <osg/io_utils>
#include <osg/ViewportIndexed>
#include <osgUtil/CullVisitor>
#include <osgViewer/Viewer>
#include <iostream>
#include <components/debug/debuglog.hpp>
#include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/visitor.hpp>
namespace Misc
{
Pose Pose::operator+(const Pose& rhs)
{
Pose pose = *this;
pose.position += this->orientation * rhs.position;
pose.orientation = rhs.orientation * this->orientation;
return pose;
}
const Pose& Pose::operator+=(const Pose& rhs)
{
*this = *this + rhs;
return *this;
}
Pose Pose::operator*(float scalar)
{
Pose pose = *this;
pose.position *= scalar;
return pose;
}
const Pose& Pose::operator*=(float scalar)
{
*this = *this * scalar;
return *this;
}
Pose Pose::operator/(float scalar)
{
Pose pose = *this;
pose.position /= scalar;
return pose;
}
const Pose& Pose::operator/=(float scalar)
{
*this = *this / scalar;
return *this;
}
bool Pose::operator==(const Pose& rhs) const
{
return position == rhs.position && orientation == rhs.orientation;
}
osg::Matrix Pose::viewMatrix(bool useGLConventions)
{
if (useGLConventions)
{
// When applied as an offset to an existing view matrix,
// that view matrix will already convert points to a camera space
// with opengl conventions. So we need to convert offsets to opengl
// conventions.
float y = position.y();
float z = position.z();
position.y() = z;
position.z() = -y;
y = orientation.y();
z = orientation.z();
orientation.y() = z;
orientation.z() = -y;
osg::Matrix viewMatrix;
viewMatrix.setTrans(-position);
viewMatrix.postMultRotate(orientation.conj());
return viewMatrix;
}
else
{
osg::Vec3d forward = orientation * osg::Vec3d(0, 1, 0);
osg::Vec3d up = orientation * osg::Vec3d(0, 0, 1);
osg::Matrix viewMatrix;
viewMatrix.makeLookAt(position, position + forward, up);
return viewMatrix;
}
}
bool FieldOfView::operator==(const FieldOfView& rhs) const
{
return angleDown == rhs.angleDown
&& angleUp == rhs.angleUp
&& angleLeft == rhs.angleLeft
&& angleRight == rhs.angleRight;
}
// near and far named with an underscore because of windows' headers galaxy brain defines.
osg::Matrix FieldOfView::perspectiveMatrix(float near_, float far_)
{
const float tanLeft = tanf(angleLeft);
const float tanRight = tanf(angleRight);
const float tanDown = tanf(angleDown);
const float tanUp = tanf(angleUp);
const float tanWidth = tanRight - tanLeft;
const float tanHeight = tanUp - tanDown;
const float offset = near_;
float matrix[16] = {};
matrix[0] = 2 / tanWidth;
matrix[4] = 0;
matrix[8] = (tanRight + tanLeft) / tanWidth;
matrix[12] = 0;
matrix[1] = 0;
matrix[5] = 2 / tanHeight;
matrix[9] = (tanUp + tanDown) / tanHeight;
matrix[13] = 0;
if (far_ <= near_) {
matrix[2] = 0;
matrix[6] = 0;
matrix[10] = -1;
matrix[14] = -(near_ + offset);
}
else {
matrix[2] = 0;
matrix[6] = 0;
matrix[10] = -(far_ + offset) / (far_ - near_);
matrix[14] = -(far_ * (near_ + offset)) / (far_ - near_);
}
matrix[3] = 0;
matrix[7] = 0;
matrix[11] = -1;
matrix[15] = 0;
return osg::Matrix(matrix);
}
bool View::operator==(const View& rhs) const
{
return pose == rhs.pose && fov == rhs.fov;
}
std::ostream& operator <<(
std::ostream& os,
const Pose& pose)
{
os << "position=" << pose.position << ", orientation=" << pose.orientation;
return os;
}
std::ostream& operator <<(
std::ostream& os,
const FieldOfView& fov)
{
os << "left=" << fov.angleLeft << ", right=" << fov.angleRight << ", down=" << fov.angleDown << ", up=" << fov.angleUp;
return os;
}
std::ostream& operator <<(
std::ostream& os,
const View& view)
{
os << "pose=< " << view.pose << " >, fov=< " << view.fov << " >";
return os;
}
/// Why are you like this
class TestCullCallback : public osg::NodeCallback
{
public:
TestCullCallback() {}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
//Log(Debug::Verbose) << "Cull: " << node->getName();
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
traverse(node, nv);
}
};
class StereoUpdateCallback : public osg::Callback
{
public:
StereoUpdateCallback(StereoView* node) : mNode(node) {}
bool run(osg::Object* object, osg::Object* data) override
{
//Log(Debug::Verbose) << "StereoUpdateCallback";
auto b = traverse(object, data);
//mNode->update();
return b;
}
StereoView* mNode;
};
class StereoUpdater : public SceneUtil::StateSetUpdater
{
public:
StereoUpdater(StereoView* view)
: stereoView(view)
{
}
protected:
virtual void setDefaults(osg::StateSet* stateset)
{
auto stereoViewMatrixUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoViewMatrices", 2);
stateset->addUniform(stereoViewMatrixUniform, osg::StateAttribute::OVERRIDE);
auto stereoViewProjectionsUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "stereoViewProjections", 2);
stateset->addUniform(stereoViewProjectionsUniform);
auto geometryPassthroughUniform = new osg::Uniform("geometryPassthrough", false);
stateset->addUniform(geometryPassthroughUniform);
}
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/)
{
stereoView->update(stateset);
}
private:
StereoView* stereoView;
};
StereoView::StereoView(osgViewer::Viewer* viewer, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask bruteForceMask)
: osg::Group()
, mViewer(viewer)
, mMainCamera(mViewer->getCamera())
, mRoot(viewer->getSceneData()->asGroup())
, mGeometryShaderMask(geometryShaderMask)
, mBruteForceMask(bruteForceMask)
{
SceneUtil::FindByNameVisitor findScene("Scene Root");
mRoot->accept(findScene);
mScene = findScene.mFoundNode;
if (!mScene)
throw std::logic_error("Couldn't find scene root");
setName("Sky Root");
mRoot->setDataVariance(osg::Object::STATIC);
setDataVariance(osg::Object::STATIC);
mLeftCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
mLeftCamera->setProjectionResizePolicy(osg::Camera::FIXED);
mLeftCamera->setProjectionMatrix(osg::Matrix::identity());
mLeftCamera->setViewMatrix(osg::Matrix::identity());
mLeftCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
mLeftCamera->setClearMask(GL_NONE);
mLeftCamera->setCullMask(bruteForceMask);
mLeftCamera->setName("Stereo Left");
mLeftCamera->setDataVariance(osg::Object::STATIC);
mRightCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
mRightCamera->setProjectionResizePolicy(osg::Camera::FIXED);
mRightCamera->setProjectionMatrix(osg::Matrix::identity());
mRightCamera->setViewMatrix(osg::Matrix::identity());
mRightCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
mRightCamera->setClearMask(GL_NONE);
mRightCamera->setCullMask(bruteForceMask);
mRightCamera->setName("Stereo Right");
mRightCamera->setDataVariance(osg::Object::STATIC);
mMainCamera->setCullMask(geometryShaderMask);
// Inject self as the root of the scene graph, and split into geometry-shader stereo and brute force stereo.
addChild(mStereoGeometryShaderRoot);
mStereoGeometryShaderRoot->addChild(mRoot);
addChild(mStereoBruteForceRoot);
mStereoBruteForceRoot->addChild(mLeftCamera);
mLeftCamera->addChild(mScene); // Use scene directly to avoid redundant shadow computation.
mStereoBruteForceRoot->addChild(mRightCamera);
mRightCamera->addChild(mScene);
viewer->setSceneData(this);
addCullCallback(new StereoUpdater(this));
// Do a blank double buffering of camera statesets on update. StereoView::Update() apply actual changes during cull;
mLeftCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
mRightCamera->setUpdateCallback(new SceneUtil::StateSetUpdater());
}
void StereoView::update(osg::StateSet* stateset)
{
auto viewMatrix = mViewer->getCamera()->getViewMatrix();
auto projectionMatrix = mViewer->getCamera()->getProjectionMatrix();
View left{};
View right{};
double near = 1.f;
double far = 10000.f;
if (!cb)
{
Log(Debug::Error) << "No update view callback. Stereo rendering will not work.";
}
cb->updateView(left, right, near, far);
osg::Vec3d leftEye = left.pose.position;
osg::Vec3d rightEye = right.pose.position;
osg::Matrix leftViewOffset = left.pose.viewMatrix(true);
osg::Matrix rightViewOffset = right.pose.viewMatrix(true);
osg::Matrix leftViewMatrix = viewMatrix * leftViewOffset;
osg::Matrix rightViewMatrix = viewMatrix * rightViewOffset;
osg::Matrix leftProjectionMatrix = left.fov.perspectiveMatrix(near, far);
osg::Matrix rightProjectionMatrix = right.fov.perspectiveMatrix(near, far);
mRightCamera->setViewMatrix(leftViewMatrix);
mLeftCamera->setViewMatrix(rightViewMatrix);
mRightCamera->setProjectionMatrix(leftProjectionMatrix);
mLeftCamera->setProjectionMatrix(rightProjectionMatrix);
// Manage viewports in update to automatically catch window/resolution changes.
auto width = mMainCamera->getViewport()->width();
auto height = mMainCamera->getViewport()->height();
mLeftCamera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height), osg::StateAttribute::OVERRIDE);
mRightCamera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, width / 2, 0, width / 2, height), osg::StateAttribute::OVERRIDE);
stateset->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height));
stateset->setAttribute(new osg::ViewportIndexed(1, width / 2, 0, width / 2, height));
// The persepctive frustum will be computed 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;
// 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);
auto frustumViewMatrixInverse = osg::Matrix::inverse(projectionMatrix) * osg::Matrix::inverse(viewMatrix);
// Update camera with frustum matrices
mMainCamera->setViewMatrix(frustumViewMatrix);
mMainCamera->setProjectionMatrix(frustumProjectionMatrix);
mStereoGeometryShaderRoot->setStateSet(stateset);
// Create and/or update stereo uniforms
auto* stereoViewMatrixUniform = stateset->getUniform("stereoViewMatrices");
auto* stereoViewProjectionsUniform = stateset->getUniform("stereoViewProjections");
stereoViewMatrixUniform->setElement(1, frustumViewMatrixInverse * leftViewMatrix);
stereoViewMatrixUniform->setElement(0, frustumViewMatrixInverse * rightViewMatrix);
stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * rightViewMatrix * leftProjectionMatrix);
stereoViewProjectionsUniform->setElement(0, frustumViewMatrixInverse * rightViewMatrix * rightProjectionMatrix);
}
void StereoView::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb)
{
this->cb = cb;
}
void StereoView::useSlaveCameraAtIndex(int index)
{
if (mViewer->getNumSlaves() <= index)
{
Log(Debug::Error) << "Requested slave at index " << index << " but no such slave exists";
return;
}
mMainCamera = mViewer->getSlave(index)._camera;
mMainCamera->setCullMask(mGeometryShaderMask);
}
void disableStereoForCamera(osg::Camera* camera)
{
auto* viewport = camera->getViewport();
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, viewport->x(), viewport->y(), viewport->width(), viewport->height()), osg::StateAttribute::OVERRIDE);
camera->getOrCreateStateSet()->addUniform(new osg::Uniform("geometryPassthrough", true), osg::StateAttribute::OVERRIDE);
}
void enableStereoForCamera(osg::Camera* camera, bool horizontalSplit)
{
auto* viewport = camera->getViewport();
auto x1 = viewport->x();
auto y1 = viewport->y();
auto width = viewport->width();
auto height = viewport->height();
auto x2 = x1;
auto y2 = y1;
if (horizontalSplit)
{
width /= 2;
x2 += width;
}
else
{
height /= 2;
y2 += height;
}
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(0, x1, y1, width, height));
camera->getOrCreateStateSet()->setAttribute(new osg::ViewportIndexed(1, x2, y2, width, height));
camera->getOrCreateStateSet()->addUniform(new osg::Uniform("geometryPassthrough", false));
}
void StereoView::DefaultUpdateViewCallback::updateView(View& left, View& right, double& near, double& far)
{
left.pose.position = osg::Vec3(-2.2, 0, 0);
right.pose.position = osg::Vec3(2.2, 0, 0);
left.fov = { -0.767549932, 0.620896876, -0.837898076, 0.726982594 };
right.fov = { -0.620896876, 0.767549932, -0.837898076, 0.726982594 };
near = 1;
far = 10000;
}
}

@ -0,0 +1,126 @@
#ifndef MISC_STEREO_H
#define MISC_STEREO_H
#include <osg/Matrix>
#include <osg/Vec3>
#include <osg/Camera>
#include <osg/StateSet>
// Some cursed headers like to define these
#if defined(near) || defined(far)
#undef near
#undef far
#endif
namespace osgViewer
{
class Viewer;
}
namespace Misc
{
//! Represents the relative pose in space of some object
struct Pose
{
//! Position in space
osg::Vec3 position{ 0,0,0 };
//! Orientation in space.
osg::Quat orientation{ 0,0,0,1 };
//! Add one pose to another
Pose operator+(const Pose& rhs);
const Pose& operator+=(const Pose& rhs);
//! Scale a pose (does not affect orientation)
Pose operator*(float scalar);
const Pose& operator*=(float scalar);
Pose operator/(float scalar);
const Pose& operator/=(float scalar);
bool operator==(const Pose& rhs) const;
osg::Matrix viewMatrix(bool useGLConventions);
};
//! Fov that defines all 4 angles from center
struct FieldOfView {
float angleLeft{ -osg::PI_2 };
float angleRight{ osg::PI_2 };
float angleDown{ -osg::PI_2 };
float angleUp{ osg::PI_2 };
bool operator==(const FieldOfView& rhs) const;
//! Generate a perspective matrix from this fov
osg::Matrix perspectiveMatrix(float near, float far);
};
//! Represents an eye including both pose and fov.
struct View
{
Pose pose;
FieldOfView fov;
bool operator==(const View& rhs) const;
};
//! Represent two eyes. The eyes are in relative terms, and are assumed to lie on the horizon plane.
struct StereoView : public osg::Group
{
struct UpdateViewCallback
{
//! Called during the update traversal of every frame to source updated stereo values.
virtual void updateView(View& left, View& right, double& near, double& far) = 0;
};
//! Default implementation of UpdateViewCallback that just provides some hardcoded values for debugging purposes
struct DefaultUpdateViewCallback : public UpdateViewCallback
{
virtual void updateView(View& left, View& right, double& near, double& far);
};
//! Adds two cameras in stereo to the mainCamera.
//! All nodes matching the mask are rendered in stereo using brute force via two camera transforms, the rest are rendered in stereo via a geometry shader.
//! \note The mask is removed from the mainCamera, so do not put Scene in this mask.
//! \note Brute force does not support shadows. But that's fine because currently this only applies to things that don't use shaders and that's only the sky, which will use shaders in the future.
StereoView(osgViewer::Viewer* viewer, osg::Node::NodeMask geometryShaderMask, osg::Node::NodeMask bruteForceMask);
//! Updates uniforms with the view and projection matrices of each stereo view, and replaces the camera's view and projection matrix
//! with a view and projection that closely envelopes the frustums of the two eyes.
void update(osg::StateSet* stateset);
//! Callback that updates stereo configuration during the update pass
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
//! Use the slave camera at index instead of the main viewer camera.
void useSlaveCameraAtIndex(int index);
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osg::Camera> mMainCamera;
osg::ref_ptr<osg::Group> mRoot;
osg::ref_ptr<osg::Group> mScene;
// Keeps state relevant to doing stereo via the geometry shader
osg::ref_ptr<osg::Group> mStereoGeometryShaderRoot{ new osg::Group };
osg::Node::NodeMask mGeometryShaderMask;
// Keeps state and cameras relevant to doing stereo via brute force
osg::ref_ptr<osg::Group> mStereoBruteForceRoot{ new osg::Group };
osg::Node::NodeMask mBruteForceMask;
osg::ref_ptr<osg::Camera> mLeftCamera{ new osg::Camera };
osg::ref_ptr<osg::Camera> mRightCamera{ new osg::Camera };
// Camera viewports
bool flipViewOrder{ true };
// Updates stereo configuration during the update pass
std::shared_ptr<UpdateViewCallback> cb{ new DefaultUpdateViewCallback };
};
//! Overrides all stereo-related states/uniforms to disable stereo for the scene rendered by camera
void disableStereoForCamera(osg::Camera* camera);
//! Overrides all stereo-related states/uniforms to enable stereo for the scene rendered by camera
void enableStereoForCamera(osg::Camera* camera, bool horizontalSplit);
}
#endif

@ -382,18 +382,30 @@ namespace Resource
return false;
static std::vector<std::string> reservedNames;
if (reservedNames.empty())
static std::mutex reservedNamesMutex;
{
const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"};
std::lock_guard<std::mutex> lock(reservedNamesMutex);
if (reservedNames.empty())
{
// This keeps somehow accessing garbage so i rewrote it using safer types.
//const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
// "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
// "Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"};
//reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(const char*));
reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
//for (unsigned int i=0; i<sizeof(reserved)/sizeof(const char*); ++i)
// reservedNames.push_back(std::string("Tri ") + reserved[i]);
for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)
reservedNames.push_back(std::string("Tri ") + reserved[i]);
std::vector<std::string> r = { "Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm",
"Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle",
"Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera" };
reservedNames = std::vector<std::string>(r.begin(), r.end());
for (auto& reservedName : r)
reservedNames.emplace_back(std::string("Tri ") + reservedName);
std::sort(reservedNames.begin(), reservedNames.end(), Misc::StringUtils::ciLess);
std::sort(reservedNames.begin(), reservedNames.end(), Misc::StringUtils::ciLess);
}
}
std::vector<std::string>::iterator it = Misc::StringUtils::partialBinarySearch(reservedNames.begin(), reservedNames.end(), name);
@ -763,7 +775,7 @@ namespace Resource
Shader::ShaderVisitor *SceneManager::createShaderVisitor()
{
Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, "objects_vertex.glsl", "objects_fragment.glsl", "objects_geometry.glsl");
Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, "objects_vertex.glsl", "objects_fragment.glsl");
shaderVisitor->setForceShaders(mForceShaders);
shaderVisitor->setAutoUseNormalMaps(mAutoUseNormalMaps);
shaderVisitor->setNormalMapPattern(mNormalMapPattern);

@ -18,6 +18,8 @@
#include "mwshadowtechnique.hpp"
#include <components/misc/stereo.hpp>
#include <osgShadow/ShadowedScene>
#include <osg/CullFace>
#include <osg/Geometry>
@ -575,6 +577,9 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData*
// set viewport
_camera->setViewport(0,0,textureSize.x(),textureSize.y());
// Shadow casting should not obey indexed viewports
Misc::disableStereoForCamera(_camera);
if (debug)
{

@ -3,6 +3,7 @@
#include <fstream>
#include <algorithm>
#include <sstream>
#include <regex>
#include <osg/Program>
@ -15,7 +16,7 @@
namespace Shader
{
void ShaderManager::setShaderPath(const std::string &path)
void ShaderManager::setShaderPath(const std::string& path)
{
mPath = path;
}
@ -34,6 +35,9 @@ namespace Shader
foundPos = source.find_first_of("\n\r", foundPos);
foundPos = source.find_first_not_of("\n\r", foundPos);
if (foundPos == std::string::npos)
break;
size_t lineDirectivePosition = source.rfind("#line", foundPos);
int lineNumber;
if (lineDirectivePosition != std::string::npos)
@ -58,45 +62,39 @@ namespace Shader
return true;
}
bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& templateName)
// Recursively replaces include statements with the actual source of the included files.
// Adjusts #line statements accordingly and detects cyclic includes.
// includingFiles is the set of files that include this file directly or indirectly, and is intentionally not a reference to allow automatic cleanup.
static bool parseIncludes(boost::filesystem::path shaderPath, std::string& source, const std::string& fileName, int& fileNumber, std::set<boost::filesystem::path> includingFiles)
{
// An include is cyclic if it is being included by itself
if (includingFiles.insert(shaderPath / fileName).second == false)
{
Log(Debug::Error) << "Shader " << fileName << " error: Detected cyclic #includes";
return false;
}
Misc::StringUtils::replaceAll(source, "\r\n", "\n");
std::set<boost::filesystem::path> includedFiles;
size_t foundPos = 0;
int fileNumber = 1;
while ((foundPos = source.find("#include")) != std::string::npos)
{
size_t start = source.find('"', foundPos);
if (start == std::string::npos || start == source.size()-1)
if (start == std::string::npos || start == source.size() - 1)
{
Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include";
Log(Debug::Error) << "Shader " << fileName << " error: Invalid #include";
return false;
}
size_t end = source.find('"', start+1);
size_t end = source.find('"', start + 1);
if (end == std::string::npos)
{
Log(Debug::Error) << "Shader " << templateName << " error: Invalid #include";
Log(Debug::Error) << "Shader " << fileName << " error: Invalid #include";
return false;
}
std::string includeFilename = source.substr(start+1, end-(start+1));
std::string includeFilename = source.substr(start + 1, end - (start + 1));
boost::filesystem::path includePath = shaderPath / includeFilename;
boost::filesystem::ifstream includeFstream;
includeFstream.open(includePath);
if (includeFstream.fail())
{
Log(Debug::Error) << "Shader " << templateName << " error: Failed to open include " << includePath.string();
return false;
}
std::stringstream buffer;
buffer << includeFstream.rdbuf();
std::string stringRepresentation = buffer.str();
addLineDirectivesAfterConditionalBlocks(stringRepresentation);
// insert #line directives so we get correct line numbers in compiler errors
int includedFileNumber = fileNumber++;
// Determine the line number that will be used for the #line directive following the included source
size_t lineDirectivePosition = source.rfind("#line", foundPos);
int lineNumber;
if (lineDirectivePosition != std::string::npos)
@ -113,18 +111,180 @@ namespace Shader
}
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n');
// Include the file recursively
boost::filesystem::ifstream includeFstream;
includeFstream.open(includePath);
if (includeFstream.fail())
{
Log(Debug::Error) << "Shader " << fileName << " error: Failed to open include " << includePath.string();
return false;
}
int includedFileNumber = fileNumber++;
std::stringstream buffer;
buffer << includeFstream.rdbuf();
std::string stringRepresentation = buffer.str();
if (!addLineDirectivesAfterConditionalBlocks(stringRepresentation)
|| !parseIncludes(shaderPath, stringRepresentation, includeFilename, fileNumber, includingFiles))
{
Log(Debug::Error) << "In file included from " << fileName << "." << lineNumber;
return false;
}
std::stringstream toInsert;
toInsert << "#line 0 " << includedFileNumber << "\n" << stringRepresentation << "\n#line " << lineNumber << " 0\n";
source.replace(foundPos, (end-foundPos+1), toInsert.str());
source.replace(foundPos, (end - foundPos + 1), toInsert.str());
}
return true;
}
if (includedFiles.insert(includePath).second == false)
struct DeclarationMeta
{
std::string interpolationType;
std::string interfaceKeyword;
std::string type;
std::string identifier;
std::string mangledIdentifier;
};
// Mangle identifiers of the interface declarations of this shader source, updating all identifiers, and returning a list of the declarations for use in generating
// a geometry shader.
// IN/OUT source: The source to mangle
// IN interfaceKeywordPattern: A regular expression matching all interface keywords to look for (e.g. "out|varying" when mangling output variables). Must not contain subexpressions.
// IN mangleString: Identifiers are mangled by prepending this string. Must be a valid identifier prefix.
// OUT declarations: All mangled declarations are added to this vector. Includes interpolation, interface, and type information as well as both the mangled and unmangled identifier.
static void mangleInterface(std::string& source, const std::string& interfaceKeywordPattern, const std::string& mangleString, std::vector<DeclarationMeta>& declarations)
{
std::string commentPattern = "//.*";
std::regex commentRegex(commentPattern);
std::string commentlessSource = std::regex_replace(source, commentRegex, "");
std::string identifierPattern = "[a-zA-Z_][0-9a-zA-Z_]*";
std::string declarationPattern = "(centroid|flat)?\\s*\\b(" + interfaceKeywordPattern + ")\\s+(" + identifierPattern + ")\\s+(" + identifierPattern + ")\\s*;";
std::regex declarationRegex(declarationPattern);
std::vector<std::smatch> matches(std::sregex_iterator(commentlessSource.begin(), commentlessSource.end(), declarationRegex), std::sregex_iterator());
std::string replacementPattern;
for (auto& match : matches)
{
declarations.emplace_back(DeclarationMeta{ match[1].str(), match[2].str(), match[3].str(), match[4].str(), mangleString + match[4].str() });
if (!replacementPattern.empty())
replacementPattern += "|";
replacementPattern = replacementPattern + "(" + declarations.back().identifier + "\\b)";
}
if (!replacementPattern.empty())
{
std::regex replacementRegex(replacementPattern);
source = std::regex_replace(source, replacementRegex, mangleString + "$&");
}
}
static std::string generateGeometryShader(const std::vector<DeclarationMeta>& declarations)
{
static const char* geometryTemplate =
"#version 150 compatibility\n"
"#extension GL_NV_viewport_array : enable\n"
"#extension GL_ARB_gpu_shader5 : enable\n"
"layout (triangles, invocations = 2) in;\n"
"layout (triangle_strip, max_vertices = 3) out;\n"
"\n"
"// Geometry Shader Inputs\n"
"@INPUTS\n"
"\n"
"// Geometry Shader Outputs\n"
"@OUTPUTS\n"
"\n"
"// Stereo matrices\n"
"uniform mat4 stereoViewMatrices[2];\n"
"uniform mat4 stereoViewProjections[2];\n"
"\n"
"void main() {\n"
" for(int i = 0; i < gl_in.length(); i++)\n"
" {\n"
" gl_ViewportIndex = gl_InvocationID;\n"
" // Re-project\n"
" gl_Position = stereoViewProjections[gl_InvocationID] * gl_in[i].gl_Position;\n"
" vec4 viewPos = stereoViewMatrices[gl_InvocationID] * gl_in[i].gl_Position;\n"
" gl_ClipVertex = vec4(viewPos.xyz,1);\n"
"\n"
" // Input -> output\n"
"@FORWARDING\n"
"\n"
" // TODO: deal with passNormal, depths, etc.\n"
"@EXTRA\n"
" EmitVertex();\n"
" }\n"
"\n"
" EndPrimitive();\n"
"}\n"
;
static std::map<std::string, std::string> overriddenForwardStatements =
{
{"linearDepth", "linearDepth = gl_Position.z;"},
{"euclideanDepth", "euclideanDepth = length(viewPos.xyz);"},
{"passViewPos", "passViewPos = viewPos.xyz;"},
};
std::stringstream ssInputDeclarations;
std::stringstream ssOutputDeclarations;
std::stringstream ssForwardStatements;
std::stringstream ssExtraStatements;
std::set<std::string> identifiers;
for (auto& declaration : declarations)
{
if (!declaration.interpolationType.empty())
{
Log(Debug::Error) << "Shader " << templateName << " error: Detected cyclic #includes";
return false;
ssInputDeclarations << declaration.interpolationType << " ";
ssOutputDeclarations << declaration.interpolationType << " ";
}
ssInputDeclarations << "in " << declaration.type << " " << declaration.mangledIdentifier << "[];\n";
ssOutputDeclarations << "out " << declaration.type << " " << declaration.identifier << ";\n";
if (overriddenForwardStatements.count(declaration.identifier) > 0)
ssForwardStatements << overriddenForwardStatements[declaration.identifier] << ";\n";
else
ssForwardStatements << " " << declaration.identifier << " = " << declaration.mangledIdentifier << "[i];\n";
identifiers.insert(declaration.identifier);
}
return true;
Log(Debug::Verbose) << "Forward statements: \n" << ssForwardStatements.str();
// passViewPos output is required
//if (identifiers.find("passViewPos") == identifiers.end())
//{
// Log(Debug::Error) << "Vertex shader is missing 'vec3 passViewPos' on its interface. Geometry shader will NOT work.";
// return "";
//}
if (identifiers.find("screenCoordsPassthrough") != identifiers.end())
{
// TODO: This corrects basic sampling but the screenCoordsOffset value in the fragment shader is still fucked.
static const char* screenCordsAssignmentCode =
" mat4 scalemat = mat4(0.25, 0.0, 0.0, 0.0,\n"
" 0.0, -0.5, 0.0, 0.0,\n"
" 0.0, 0.0, 0.5, 0.0,\n"
" 0.25, 0.5, 0.5, 1.0);\n"
" vec4 texcoordProj = ((scalemat) * (gl_Position));\n"
" screenCoordsPassthrough = texcoordProj.xyw;\n"
" if(gl_InvocationID == 1)\n"
" screenCoordsPassthrough.x += 0.5 * screenCoordsPassthrough.z;\n"
;
ssExtraStatements << screenCordsAssignmentCode;
}
//if()
std::string geometryShader = geometryTemplate;
geometryShader = std::regex_replace(geometryShader, std::regex("@INPUTS"), ssInputDeclarations.str());
geometryShader = std::regex_replace(geometryShader, std::regex("@OUTPUTS"), ssOutputDeclarations.str());
geometryShader = std::regex_replace(geometryShader, std::regex("@FORWARDING"), ssForwardStatements.str());
geometryShader = std::regex_replace(geometryShader, std::regex("@EXTRA"), ssExtraStatements.str());
return geometryShader;
}
bool parseFors(std::string& source, const std::string& templateName)
@ -165,7 +325,7 @@ namespace Shader
std::string list = source.substr(listStart, listEnd - listStart);
std::vector<std::string> listElements;
if (list != "")
Misc::StringUtils::split (list, listElements, ",");
Misc::StringUtils::split(list, listElements, ",");
size_t contentStart = source.find_first_not_of("\n\r", listEnd);
size_t contentEnd = source.find("$endforeach", contentStart);
@ -224,7 +384,7 @@ namespace Shader
Log(Debug::Error) << "Shader " << templateName << " error: Unexpected EOF";
return false;
}
std::string define = source.substr(foundPos+1, endPos - (foundPos+1));
std::string define = source.substr(foundPos + 1, endPos - (foundPos + 1));
ShaderManager::DefineMap::const_iterator defineFound = defines.find(define);
ShaderManager::DefineMap::const_iterator globalDefineFound = globalDefines.find(define);
if (define == "foreach")
@ -271,7 +431,7 @@ namespace Shader
return true;
}
osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType)
osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string& templateName, const ShaderManager::DefineMap& defines, osg::Shader::Type shaderType)
{
std::lock_guard<std::mutex> lock(mMutex);
@ -279,21 +439,22 @@ namespace Shader
TemplateMap::iterator templateIt = mShaderTemplates.find(templateName);
if (templateIt == mShaderTemplates.end())
{
boost::filesystem::path p = (boost::filesystem::path(mPath) / templateName);
boost::filesystem::path path = (boost::filesystem::path(mPath) / templateName);
boost::filesystem::ifstream stream;
stream.open(p);
stream.open(path);
if (stream.fail())
{
Log(Debug::Error) << "Failed to open " << p.string();
Log(Debug::Error) << "Failed to open " << path.string();
return nullptr;
}
std::stringstream buffer;
buffer << stream.rdbuf();
// parse includes
int fileNumber = 1;
std::string source = buffer.str();
if (!addLineDirectivesAfterConditionalBlocks(source)
|| !parseIncludes(boost::filesystem::path(mPath), source, templateName))
|| !parseIncludes(boost::filesystem::path(mPath), source, templateName, fileNumber, {}))
return nullptr;
templateIt = mShaderTemplates.insert(std::make_pair(templateName, source)).first;
@ -310,12 +471,31 @@ namespace Shader
return nullptr;
}
osg::ref_ptr<osg::Shader> shader (new osg::Shader(shaderType));
shader->setShaderSource(shaderSource);
osg::ref_ptr<osg::Shader> shader(new osg::Shader(shaderType));
// Assign a unique name to allow the SharedStateManager to compare shaders efficiently
static unsigned int counter = 0;
shader->setName(std::to_string(counter++));
if (mGeometryShadersEnabled && defines.count("geometryShader") && defines.find("geometryShader")->second == "1" && shaderType == osg::Shader::VERTEX)
{
std::vector<DeclarationMeta> declarations;
mangleInterface(shaderSource, "out|varying", "vertex_", declarations);
std::string geometryShaderSource = generateGeometryShader(declarations);
if (!geometryShaderSource.empty())
{
osg::ref_ptr<osg::Shader> geometryShader(new osg::Shader(osg::Shader::GEOMETRY));
geometryShader->setShaderSource(geometryShaderSource);
geometryShader->setName(shader->getName() + ".geom");
mGeometryShaders[shader] = geometryShader;
}
else
{
Log(Debug::Error) << "Failed to generate geometry shader for " << templateName;
}
}
shader->setShaderSource(shaderSource);
shaderIt = mShaders.insert(std::make_pair(std::make_pair(templateName, defines), shader)).first;
}
return shaderIt->second;
@ -327,9 +507,16 @@ namespace Shader
ProgramMap::iterator found = mPrograms.find(std::make_pair(vertexShader, fragmentShader));
if (found == mPrograms.end())
{
osg::ref_ptr<osg::Program> program (new osg::Program);
osg::ref_ptr<osg::Program> program(new osg::Program);
program->addShader(vertexShader);
program->addShader(fragmentShader);
auto git = mGeometryShaders.find(vertexShader);
if (git != mGeometryShaders.end())
{
program->addShader(git->second);
}
found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first;
}
return found->second;
@ -340,10 +527,15 @@ namespace Shader
return DefineMap(mGlobalDefines);
}
void ShaderManager::setGlobalDefines(DefineMap & globalDefines)
void ShaderManager::enableGeometryShader(bool enabled)
{
mGeometryShadersEnabled = enabled;
}
void ShaderManager::setGlobalDefines(DefineMap& globalDefines)
{
mGlobalDefines = globalDefines;
for (auto shaderMapElement: mShaders)
for (auto shaderMapElement : mShaders)
{
std::string templateId = shaderMapElement.first.first;
ShaderManager::DefineMap defines = shaderMapElement.first.second;
@ -360,7 +552,7 @@ namespace Shader
}
}
void ShaderManager::releaseGLObjects(osg::State *state)
void ShaderManager::releaseGLObjects(osg::State* state)
{
std::lock_guard<std::mutex> lock(mMutex);
for (auto shader : mShaders)

@ -36,6 +36,13 @@ namespace Shader
/// Get (a copy of) the DefineMap used to construct all shaders
DefineMap getGlobalDefines();
/// Enable or disable automatic stereo geometry shader.
/// If enabled, a stereo geometry shader will be automatically generated for any vertex shader
/// whose defines include "geometryShader" set to "1".
/// This geometry shader is automatically included in any program using that vertex shader.
/// \note Does not affect programs that have already been created, set this during startup.
void enableGeometryShader(bool enabled);
/// Set the DefineMap used to construct all shaders
/// @param defines The DefineMap to use
/// @note This will change the source code for any shaders already created, potentially causing problems if they're being used to render a frame. It is recommended that any associated Viewers have their threading stopped while this function is running if any shaders are in use.
@ -59,6 +66,10 @@ namespace Shader
typedef std::map<MapKey, osg::ref_ptr<osg::Shader> > ShaderMap;
ShaderMap mShaders;
typedef std::map<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> > GeometryShaderMap;
GeometryShaderMap mGeometryShaders;
bool mGeometryShadersEnabled{ false };
typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap;
ProgramMap mPrograms;

@ -37,7 +37,7 @@ namespace Shader
}
ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultVsTemplate, const std::string &defaultFsTemplate, const std::string& defaultGsTemplate)
ShaderVisitor::ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string &defaultVsTemplate, const std::string &defaultFsTemplate)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mForceShaders(false)
, mAllowedToModifyStateSets(true)
@ -47,7 +47,6 @@ namespace Shader
, mImageManager(imageManager)
, mDefaultVsTemplate(defaultVsTemplate)
, mDefaultFsTemplate(defaultFsTemplate)
, mDefaultGsTemplate(defaultGsTemplate)
{
mRequirements.push_back(ShaderRequirements());
}
@ -345,17 +344,16 @@ namespace Shader
}
defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0";
defineMap["geometryShader"] = "1";
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
osg::ref_ptr<osg::Shader> vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX));
osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
osg::ref_ptr<osg::Shader> geometryShader (mShaderManager.getShader(mDefaultGsTemplate, defineMap, osg::Shader::GEOMETRY));
//osg::ref_ptr<osg::Shader> geometryShader = nullptr;
if (vertexShader && fragmentShader)
{
writableStateSet->setAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader, geometryShader), osg::StateAttribute::ON);
writableStateSet->setAttributeAndModes(mShaderManager.getProgram(vertexShader, fragmentShader), osg::StateAttribute::ON);
for (std::map<int, std::string>::const_iterator texIt = reqs.mTextures.begin(); texIt != reqs.mTextures.end(); ++texIt)
{

@ -17,7 +17,7 @@ namespace Shader
class ShaderVisitor : public osg::NodeVisitor
{
public:
ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string& defaultVsTemplate, const std::string& defaultFsTemplate, const std::string& defaultGsTemplate);
ShaderVisitor(ShaderManager& shaderManager, Resource::ImageManager& imageManager, const std::string& defaultVsTemplate, const std::string& defaultFsTemplate);
/// By default, only bump mapped objects will have a shader added to them.
/// Setting force = true will cause all objects to render using shaders, regardless of having a bump map.
@ -89,7 +89,6 @@ namespace Shader
std::string mDefaultVsTemplate;
std::string mDefaultFsTemplate;
std::string mDefaultGsTemplate;
void createProgram(const ShaderRequirements& reqs);
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);

@ -232,10 +232,9 @@ namespace Terrain
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
defineMap["geometryShader"] = "1";
osg::ref_ptr<osg::Shader> vertexShader = shaderManager->getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> geometryShader = shaderManager->getShader("terrain_geometry.glsl", defineMap, osg::Shader::GEOMETRY);
//osg::ref_ptr<osg::Shader> geometryShader = nullptr;
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
if (!vertexShader || !fragmentShader)
{
@ -243,7 +242,7 @@ namespace Terrain
return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize);
}
stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader, geometryShader));
stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader));
stateset->addUniform(new osg::Uniform("colorMode", 2));
}
else

@ -11,21 +11,17 @@ set(SHADER_FILES
water_fragment.glsl
water_nm.png
objects_vertex.glsl
objects_geometry.glsl
objects_fragment.glsl
terrain_vertex.glsl
terrain_geometry.glsl
terrain_fragment.glsl
lighting.glsl
parallax.glsl
s360_fragment.glsl
s360_vertex.glsl
shadows_vertex.glsl
shadows_geometry.glsl
shadows_fragment.glsl
shadowcasting_vertex.glsl
shadowcasting_fragment.glsl
interface_util.glsl
)
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")

@ -1,12 +0,0 @@
// Because GLSL is an abomination we have to mangle the names of all
// vertex outputs when using a geometry shader.
#ifndef INTERFACE_UTIL_GLSL
#define INTERFACE_UTIL_GLSL
#if 1 // Placeholder
#define VS_NAME(name) vertex_##name
#else
#define VS_NAME(name) name
#endif
#endif // INTERFACE_UTIL_GLSL

@ -1,17 +0,0 @@
#version 330 core
#extension GL_NV_viewport_array : enable
#extension GL_ARB_gpu_shader5 : enable
layout (triangles, invocations = 2) in;
layout (triangle_strip, max_vertices = 3) out;
#include "interface_util.glsl"
void main() {
for(int i = 0; i < gl_in.length(); i++)
{
gl_ViewportIndex = gl_InvocationID;
gl_Position = gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}

@ -1,7 +1,5 @@
#version 120
#include "interface_util.glsl"
#if @diffuseMap
varying vec2 diffuseMapUV;
#endif

@ -110,6 +110,4 @@ void main()
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
applyShadowDebugOverlay();
//gl_FragData[0] = vec4(passNormal,1);
}

@ -1,61 +0,0 @@
#version 330 core
#extension GL_NV_viewport_array : enable
#extension GL_ARB_gpu_shader5 : enable
layout (triangles, invocations = 2) in;
layout (triangle_strip, max_vertices = 3) out;
#include "interface_util.glsl"
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
#if !PER_PIXEL_LIGHTING
centroid in vec4 VS_NAME(lighting)[];
centroid in vec3 VS_NAME(shadowDiffuseLighting)[];
centroid out vec4 lighting;
centroid out vec3 shadowDiffuseLighting;
#endif
// TERRAIN INPUT
in vec2 VS_NAME(uv)[];
in float VS_NAME(euclideanDepth)[];
in float VS_NAME(linearDepth)[];
centroid in vec4 VS_NAME(passColor)[];
in vec3 VS_NAME(passViewPos)[];
in vec3 VS_NAME(passNormal)[];
#if(@shadows_enabled)
#endif
// TERRAIN OUTPUT
out vec2 uv;
out float euclideanDepth;
out float linearDepth;
centroid out vec4 passColor;
out vec3 passViewPos;
out vec3 passNormal;
void main() {
for(int i = 0; i < gl_in.length(); i++)
{
gl_ViewportIndex = gl_InvocationID;
gl_Position = gl_in[i].gl_Position;
uv = VS_NAME(uv)[i];
euclideanDepth = VS_NAME(euclideanDepth)[i];
linearDepth = VS_NAME(linearDepth)[i];
#if !PER_PIXEL_LIGHTING
lighting = VS_NAME(lighting)[i];
shadowDiffuseLighting = VS_NAME(shadowDiffuseLighting)[i];
#endif
passColor = VS_NAME(passColor)[i];
passViewPos = VS_NAME(passViewPos)[i];
passNormal = VS_NAME(passNormal)[i];
#if(@shadows_enabled)
#endif
EmitVertex();
}
EndPrimitive();
}

@ -1,47 +1,44 @@
#version 120
#version 130
#include "interface_util.glsl"
varying vec2 VS_NAME(uv);
varying float VS_NAME(euclideanDepth);
varying float VS_NAME(linearDepth);
varying vec2 uv;
varying float euclideanDepth;
varying float linearDepth;
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
#if !PER_PIXEL_LIGHTING
centroid varying vec4 VS_NAME(lighting);
centroid varying vec3 VS_NAME(shadowDiffuseLighting);
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#endif
centroid varying vec4 VS_NAME(passColor);
varying vec3 VS_NAME(passViewPos);
varying vec3 VS_NAME(passNormal);
centroid varying vec4 passColor;
varying vec3 passViewPos;
varying vec3 passNormal;
#include "shadows_vertex.glsl"
#include "lighting.glsl"
void main(void)
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);
gl_ClipVertex = viewPos;
VS_NAME(euclideanDepth) = length(viewPos.xyz);
VS_NAME(linearDepth) = gl_Position.z;
euclideanDepth = length(viewPos.xyz);
linearDepth = gl_Position.z;
#if (!PER_PIXEL_LIGHTING || @shadows_enabled)
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
#endif
#if !PER_PIXEL_LIGHTING
VS_NAME(lighting) = doLighting(viewPos.xyz, viewNormal, gl_Color, VS_NAME(shadowDiffuseLighting));
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
#endif
VS_NAME(passColor) = gl_Color;
VS_NAME(passNormal) = gl_Normal.xyz;
VS_NAME(passViewPos) = viewPos.xyz;
passColor = gl_Color;
passNormal = gl_Normal.xyz;
passViewPos = viewPos.xyz;
VS_NAME(uv) = gl_MultiTexCoord0.xy;
uv = gl_MultiTexCoord0.xy;
#if (@shadows_enabled)
setupShadowCoords(viewPos, viewNormal);

Loading…
Cancel
Save