diff --git a/components/misc/stereo.cpp b/components/misc/stereo.cpp index de7cdd531..214bfd0cd 100644 --- a/components/misc/stereo.cpp +++ b/components/misc/stereo.cpp @@ -176,40 +176,27 @@ namespace Misc 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(nv); - traverse(node, nv); - } - }; - + // Update stereo view/projection during update class StereoUpdateCallback : public osg::Callback { public: - StereoUpdateCallback(StereoView* node) : mNode(node) {} + StereoUpdateCallback(StereoView* stereoView) : stereoView(stereoView) {} bool run(osg::Object* object, osg::Object* data) override { - //Log(Debug::Verbose) << "StereoUpdateCallback"; auto b = traverse(object, data); - //mNode->update(); + stereoView->update(); return b; } - StereoView* mNode; + StereoView* stereoView; }; - class StereoUpdater : public SceneUtil::StateSetUpdater + // Update states during cull + class StereoStatesetUpdateCallback : public SceneUtil::StateSetUpdater { public: - StereoUpdater(StereoView* view) + StereoStatesetUpdateCallback(StereoView* view) : stereoView(view) { } @@ -227,7 +214,7 @@ namespace Misc virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) { - stereoView->update(stateset); + stereoView->updateStateset(stateset); } private: @@ -281,14 +268,21 @@ namespace Misc 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; + // Do a blank double buffering of camera statesets on update. Actual stateset updates are performed in StereoView::Update() mLeftCamera->setUpdateCallback(new SceneUtil::StateSetUpdater()); mRightCamera->setUpdateCallback(new SceneUtil::StateSetUpdater()); + + // Update stereo statesets/matrices, but after the main camera updates. + auto mainCameraCB = mMainCamera->getUpdateCallback(); + mMainCamera->removeUpdateCallback(mainCameraCB); + mMainCamera->addUpdateCallback(new StereoUpdateCallback(this)); + mMainCamera->addUpdateCallback(mainCameraCB); + + addCullCallback(new StereoStatesetUpdateCallback(this)); } - void StereoView::update(osg::StateSet* stateset) + void StereoView::update() { auto viewMatrix = mViewer->getCamera()->getViewMatrix(); auto projectionMatrix = mViewer->getCamera()->getProjectionMatrix(); @@ -315,18 +309,10 @@ namespace Misc 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)); + mRightCamera->setViewMatrix(rightViewMatrix); + mLeftCamera->setViewMatrix(leftViewMatrix); + mRightCamera->setProjectionMatrix(rightProjectionMatrix); + mLeftCamera->setProjectionMatrix(leftProjectionMatrix); // 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. @@ -374,6 +360,7 @@ namespace Misc 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. @@ -382,23 +369,35 @@ namespace Misc // 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); + 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); + } + void StereoView::updateStateset(osg::StateSet * stateset) + { + // Manage viewports in update to automatically catch window/resolution changes. + auto width = mMainCamera->getViewport()->width(); + auto height = mMainCamera->getViewport()->height(); + stateset->setAttribute(new osg::ViewportIndexed(0, 0, 0, width / 2, height)); + stateset->setAttribute(new osg::ViewportIndexed(1, width / 2, 0, width / 2, height)); - // Create and/or update stereo uniforms + // Update stereo uniforms + auto frustumViewMatrixInverse = osg::Matrix::inverse(mMainCamera->getViewMatrix()); + //auto frustumViewProjectionMatrixInverse = osg::Matrix::inverse(mMainCamera->getProjectionMatrix()) * osg::Matrix::inverse(mMainCamera->getViewMatrix()); 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); + stereoViewMatrixUniform->setElement(0, frustumViewMatrixInverse * mLeftCamera->getViewMatrix()); + stereoViewMatrixUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix()); + stereoViewProjectionsUniform->setElement(0, frustumViewMatrixInverse * mLeftCamera->getViewMatrix() * mLeftCamera->getProjectionMatrix()); + stereoViewProjectionsUniform->setElement(1, frustumViewMatrixInverse * mRightCamera->getViewMatrix() * mRightCamera->getProjectionMatrix()); } void StereoView::setUpdateViewCallback(std::shared_ptr cb) diff --git a/components/misc/stereo.hpp b/components/misc/stereo.hpp index 2d2567f1c..a3ff227ed 100644 --- a/components/misc/stereo.hpp +++ b/components/misc/stereo.hpp @@ -86,7 +86,8 @@ namespace Misc //! 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); + void update(); + void updateStateset(osg::StateSet* stateset); //! Callback that updates stereo configuration during the update pass void setUpdateViewCallback(std::shared_ptr cb); diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 447f08879..561971cda 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -205,15 +205,13 @@ namespace Shader " {\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_Position = stereoViewProjections[gl_InvocationID] * vec4(vertex_passViewPos[i],1);\n" + " vec4 viewPos = stereoViewMatrices[gl_InvocationID] * vec4(vertex_passViewPos[i],1);\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" @@ -226,6 +224,17 @@ namespace Shader {"linearDepth", "linearDepth = gl_Position.z;"}, {"euclideanDepth", "euclideanDepth = length(viewPos.xyz);"}, {"passViewPos", "passViewPos = viewPos.xyz;"}, + { + "screenCoordsPassthrough", + " 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" + } }; std::stringstream ssInputDeclarations; @@ -251,38 +260,17 @@ namespace Shader identifiers.insert(declaration.identifier); } - 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()) + if (identifiers.find("passViewPos") == 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; + Log(Debug::Error) << "Vertex shader is missing 'vec3 passViewPos' on its interface. Geometry shader will NOT work."; + return ""; } - //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; } diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 02a395f95..c099e4210 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -3,12 +3,15 @@ varying vec3 screenCoordsPassthrough; varying vec4 position; varying float linearDepth; +varying vec3 passViewPos; #include "shadows_vertex.glsl" void main(void) { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + vec4 viewPos = gl_ModelViewMatrix * gl_Vertex; + passViewPos = viewPos.xyz; mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, @@ -22,5 +25,5 @@ void main(void) linearDepth = gl_Position.z; - setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz)); + setupShadowCoords(viewPos, normalize((gl_NormalMatrix * gl_Normal).xyz)); }