From 26a7b48d692c27baf353bd748fb6db40348184cb Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 11 Oct 2017 20:47:19 +0100 Subject: [PATCH] Attempt to make a VDSM work, and also to set up a debug HUD, but without success. --- apps/openmw/mwrender/renderingmanager.cpp | 6 +- apps/openmw/mwrender/shadow.cpp | 698 +++++++++++++++++++++- apps/openmw/mwrender/shadow.hpp | 28 +- files/shaders/objects_fragment.glsl | 4 +- files/shaders/terrain_fragment.glsl | 4 +- 5 files changed, 714 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e670eaebc0..172ff34a85 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -210,7 +210,7 @@ namespace MWRender settings->setReceivesShadowTraversalMask(~0u); //settings->setShadowMapProjectionHint(osgShadow::ShadowSettings::PERSPECTIVE_SHADOW_MAP); - //settings->setBaseShadowTextureUnit(1); + settings->setBaseShadowTextureUnit(1); //settings->setMinimumShadowMapNearFarRatio(0); //settings->setNumShadowMapsPerLight(1); //settings->setShadowMapProjectionHint(osgShadow::ShadowSettings::ORTHOGRAPHIC_SHADOW_MAP); @@ -227,12 +227,12 @@ namespace MWRender MWShadow* tech = new MWShadow(); shadowedScene->setShadowTechnique(tech); - tech->setMaxFarPlane(0); + /*tech->setMaxFarPlane(0); tech->setTextureSize(osg::Vec2s(mapres, mapres)); tech->setShadowTextureCoordIndex(1); tech->setShadowTextureUnit(1); tech->setBaseTextureCoordIndex(0); - tech->setBaseTextureUnit(0); + tech->setBaseTextureUnit(0);*/ //mRootNode->addChild(sceneRoot); shadowedScene->addChild(sceneRoot); diff --git a/apps/openmw/mwrender/shadow.cpp b/apps/openmw/mwrender/shadow.cpp index 17cc5156bb..c58334a51a 100644 --- a/apps/openmw/mwrender/shadow.cpp +++ b/apps/openmw/mwrender/shadow.cpp @@ -1,11 +1,701 @@ #include "shadow.hpp" +#include +#include +#include +#include +#include +#include + namespace MWRender { - void MWShadow::ViewData::init(MWShadow * st, osgUtil::CullVisitor * cv) + using namespace osgShadow; + + std::string debugVertexShaderSource = "void main(void){gl_Position = gl_Vertex; gl_TexCoord[0]=gl_MultiTexCoord0;}"; + std::string debugFragmentShaderSource = + "uniform sampler2D texture; \n" + " \n" + "void main(void) \n" + "{ \n" +#if 0 + " float f = texture2D( texture, gl_TexCoord[0] ).r; \n" + " \n" + " f = 256.0 * f; \n" + " float fC = floor( f ) / 256.0; \n" + " \n" + " f = 256.0 * fract( f ); \n" + " float fS = floor( f ) / 256.0; \n" + " \n" + " f = 256.0 * fract( f ); \n" + " float fH = floor( f ) / 256.0; \n" + " \n" + " fS *= 0.5; \n" + " fH = ( fH * 0.34 + 0.66 ) * ( 1.0 - fS ); \n" + " \n" + " vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ), \n" + " abs( fC - 0.333333 ), \n" + " abs( fC - 0.666667 ) ); \n" + " \n" + " rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb ); \n" + " \n" + " float fMax = max( max( rgb.r, rgb.g ), rgb.b ); \n" + " fMax = 1.0 / fMax; \n" + " \n" + " vec3 color = fMax * rgb; \n" + " \n" + " gl_FragColor = vec4( fS + fH * color, 1 ); \n" +#else + " gl_FragColor = texture2D(texture, gl_TexCoord[0]); \n" + " //gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0); \n" +#endif + "} \n"; + + + MWShadow::MWShadow() : debugCamera(new osg::Camera), debugProgram(new osg::Program), testTex(new osg::Texture2D) { - LightSpacePerspectiveShadowMapDB::ViewData::init(st, cv); - osg::StateSet * stateset = _camera->getOrCreateStateSet(); - stateset->removeAttribute(osg::StateAttribute::CULLFACE); + debugCamera->setViewport(0, 0, 200, 200); + debugCamera->setRenderOrder(osg::Camera::POST_RENDER); + debugCamera->setClearColor(osg::Vec4(1.0, 1.0, 0.0, 1.0)); + + osg::ref_ptr vertexShader = new osg::Shader(osg::Shader::VERTEX, debugVertexShaderSource); + debugProgram->addShader(vertexShader); + osg::ref_ptr fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, debugFragmentShaderSource); + debugProgram->addShader(fragmentShader); + + debugGeometry = osg::createTexturedQuadGeometry(osg::Vec3(-1, -1, 0), osg::Vec3(2, 0, 0), osg::Vec3(0, 2, 0));- + debugCamera->addChild(debugGeometry); + osg::ref_ptr stateSet = debugGeometry->getOrCreateStateSet(); + stateSet->setAttributeAndModes(debugProgram, osg::StateAttribute::ON); + osg::ref_ptr textureUniform = new osg::Uniform("texture", 0); + //textureUniform->setType(osg::Uniform::SAMPLER_2D); + stateSet->addUniform(textureUniform.get()); + + testTex->setDataVariance(osg::Object::DYNAMIC); + osg::ref_ptr testImage = osgDB::readRefImageFile("path/to/an/image/file.png"); + testTex->setImage(testImage); + } + + class VDSMCameraCullCallback : public osg::NodeCallback + { + public: + + VDSMCameraCullCallback(ViewDependentShadowMap* vdsm, osg::Polytope& polytope); + + virtual void operator()(osg::Node*, osg::NodeVisitor* nv); + + osg::RefMatrix* getProjectionMatrix() { return _projectionMatrix.get(); } + osgUtil::RenderStage* getRenderStage() { return _renderStage.get(); } + + protected: + + ViewDependentShadowMap* _vdsm; + osg::ref_ptr _projectionMatrix; + osg::ref_ptr _renderStage; + osg::Polytope _polytope; + }; + + VDSMCameraCullCallback::VDSMCameraCullCallback(ViewDependentShadowMap* vdsm, osg::Polytope& polytope) : + _vdsm(vdsm), + _polytope(polytope) + { + } + + void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osgUtil::CullVisitor* cv = dynamic_cast(nv); + osg::Camera* camera = dynamic_cast(node); + OSG_INFO << "VDSMCameraCullCallback::operator()(osg::Node* " << camera << ", osg::NodeVisitor* " << cv << ")" << std::endl; + +#if 1 + if (!_polytope.empty()) + { + OSG_INFO << "Pushing custom Polytope" << std::endl; + + osg::CullingSet& cs = cv->getProjectionCullingStack().back(); + + cs.setFrustum(_polytope); + + cv->pushCullingSet(); + } +#endif + if (_vdsm->getShadowedScene()) + { + _vdsm->getShadowedScene()->osg::Group::traverse(*nv); + } +#if 1 + if (!_polytope.empty()) + { + OSG_INFO << "Popping custom Polytope" << std::endl; + cv->popCullingSet(); + } +#endif + + _renderStage = cv->getCurrentRenderBin()->getStage(); + + OSG_INFO << "VDSM second : _renderStage = " << _renderStage << std::endl; + + if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + { + // make sure that the near plane is computed correctly. + cv->computeNearPlane(); + + osg::Matrixd projection = *(cv->getProjectionMatrix()); + + OSG_INFO << "RTT Projection matrix " << projection << std::endl; + + osg::Matrix::value_type left, right, bottom, top, zNear, zFar; + osg::Matrix::value_type epsilon = 1e-6; + if (fabs(projection(0, 3))setProjectionMatrix(projection); + + _projectionMatrix = cv->getProjectionMatrix(); + } + } + + class ComputeLightSpaceBounds : public osg::NodeVisitor, public osg::CullStack + { + public: + ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) + { + setCullingMode(osg::CullSettings::VIEW_FRUSTUM_CULLING); + + pushViewport(viewport); + pushProjectionMatrix(new osg::RefMatrix(projectionMatrix)); + pushModelViewMatrix(new osg::RefMatrix(viewMatrix), osg::Transform::ABSOLUTE_RF); + } + + void apply(osg::Node& node) + { + if (isCulled(node)) return; + + // push the culling mode. + pushCurrentMask(); + + traverse(node); + + // pop the culling mode. + popCurrentMask(); + } + + void apply(osg::Geode& node) + { + if (isCulled(node)) return; + + // push the culling mode. + pushCurrentMask(); + + for (unsigned int i = 0; igetBoundingBox()); + } + } + + // pop the culling mode. + popCurrentMask(); + } + + void apply(osg::Billboard&) + { + OSG_INFO << "Warning Billboards not yet supported" << std::endl; + return; + } + + void apply(osg::Projection&) + { + // projection nodes won't affect a shadow map so their subgraphs should be ignored + return; + } + + void apply(osg::Transform& transform) + { + if (isCulled(transform)) return; + + // push the culling mode. + pushCurrentMask(); + + // absolute transforms won't affect a shadow map so their subgraphs should be ignored. + if (transform.getReferenceFrame() == osg::Transform::RELATIVE_RF) + { + osg::ref_ptr matrix = new osg::RefMatrix(*getModelViewMatrix()); + transform.computeLocalToWorldMatrix(*matrix, this); + pushModelViewMatrix(matrix.get(), transform.getReferenceFrame()); + + traverse(transform); + + popModelViewMatrix(); + } + + // pop the culling mode. + popCurrentMask(); + + } + + void apply(osg::Camera&) + { + // camera nodes won't affect a shadow map so their subgraphs should be ignored + return; + } + + void updateBound(const osg::BoundingBox& bb) + { + if (!bb.valid()) return; + + const osg::Matrix& matrix = *getModelViewMatrix() * *getProjectionMatrix(); + + update(bb.corner(0) * matrix); + update(bb.corner(1) * matrix); + update(bb.corner(2) * matrix); + update(bb.corner(3) * matrix); + update(bb.corner(4) * matrix); + update(bb.corner(5) * matrix); + update(bb.corner(6) * matrix); + update(bb.corner(7) * matrix); + } + + void update(const osg::Vec3& v) + { + if (v.z()<-1.0f) + { + //OSG_NOTICE<<"discarding("<1.0f) x = 1.0f; + float y = v.y(); + if (y<-1.0f) y = -1.0f; + if (y>1.0f) y = 1.0f; + _bb.expandBy(osg::Vec3(x, y, v.z())); + } + + osg::BoundingBox _bb; + }; + + void MWShadow::cull(osgUtil::CullVisitor& cv) + { + OSG_INFO << std::endl << std::endl << "ViewDependentShadowMap::cull(osg::CullVisitor&" << &cv << ")" << std::endl; + + if (!_shadowCastingStateSet) + { + OSG_INFO << "Warning, init() has not yet been called so ShadowCastingStateSet has not been setup yet, unable to create shadows." << std::endl; + _shadowedScene->osg::Group::traverse(cv); + return; + } + + ViewDependentData* vdd = getViewDependentData(&cv); + + if (!vdd) + { + OSG_INFO << "Warning, now ViewDependentData created, unable to create shadows." << std::endl; + _shadowedScene->osg::Group::traverse(cv); + return; + } + + ShadowSettings* settings = getShadowedScene()->getShadowSettings(); + + OSG_INFO << "cv->getProjectionMatrix()=" << *cv.getProjectionMatrix() << std::endl; + + osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode(); + + osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix(); + + // check whether this main views projection is perspective or orthographic + bool orthographicViewFrustum = viewProjectionMatrix(0, 3) == 0.0 && + viewProjectionMatrix(1, 3) == 0.0 && + viewProjectionMatrix(2, 3) == 0.0; + + double minZNear = 0.0; + double maxZFar = DBL_MAX; + + if (cachedNearFarMode == osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + { + double left, right, top, bottom; + if (orthographicViewFrustum) + { + viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar); + } + else + { + viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar); + } + OSG_INFO << "minZNear=" << minZNear << ", maxZFar=" << maxZFar << std::endl; + } + + // set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible + if (settings->getComputeNearFarModeOverride() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + { + cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride()); + } + + // 1. Traverse main scene graph + cv.pushStateSet(_shadowRecievingPlaceholderStateSet.get()); + + osg::ref_ptr decoratorStateGraph = cv.getCurrentStateGraph(); + + cullShadowReceivingScene(&cv); + + cv.popStateSet(); + + if (cv.getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + { + OSG_INFO << "Just done main subgraph traversak" << std::endl; + // make sure that the near plane is computed correctly so that any projection matrix computations + // are all done correctly. + cv.computeNearPlane(); + } + + // clamp the minZNear and maxZFar to those provided by ShadowSettings + maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(), maxZFar); + if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio(); + + //OSG_NOTICE<<"maxZFar "<getLightDataList(); + for (LightDataList::iterator itr = pll.begin(); + itr != pll.end(); + ++itr) + { + // 3. create per light/per shadow map division of lightspace/frustum + // create a list of light/shadow map data structures + + LightData& pl = **itr; + + // 3.1 compute light space polytope + // + osg::Polytope polytope = computeLightViewFrustumPolytope(frustum, pl); + + // if polytope is empty then no rendering. + if (polytope.empty()) + { + OSG_NOTICE << "Polytope empty no shadow to render" << std::endl; + continue; + } + + // 3.2 compute RTT camera view+projection matrix settings + // + osg::Matrixd projectionMatrix; + osg::Matrixd viewMatrix; + if (!computeShadowCameraSettings(frustum, pl, projectionMatrix, viewMatrix)) + { + OSG_NOTICE << "No valid Camera settings, no shadow to render" << std::endl; + continue; + } + + // if we are using multiple shadow maps and CastShadowTraversalMask is being used + // traverse the scene to compute the extents of the objects + if (/*numShadowMapsPerLight>1 &&*/ _shadowedScene->getCastsShadowTraversalMask() != 0xffffffff) + { + // osg::ElapsedTime timer; + + osg::ref_ptr viewport = new osg::Viewport(0, 0, 2048, 2048); + ComputeLightSpaceBounds clsb(viewport.get(), projectionMatrix, viewMatrix); + clsb.setTraversalMask(_shadowedScene->getCastsShadowTraversalMask()); + + osg::Matrixd invertModelView; + invertModelView.invert(viewMatrix); + osg::Polytope local_polytope(polytope); + local_polytope.transformProvidingInverse(invertModelView); + + osg::CullingSet& cs = clsb.getProjectionCullingStack().back(); + cs.setFrustum(local_polytope); + clsb.pushCullingSet(); + + _shadowedScene->accept(clsb); + + // OSG_NOTICE<<"Extents of LightSpace "< texture = sd->_texture; + osg::ref_ptr stateSet = debugGeometry->getOrCreateStateSet(); + if (false) + stateSet->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); + else + stateSet->setTextureAttributeAndModes(0, testTex, osg::StateAttribute::ON); + + unsigned int traversalMask = cv.getTraversalMask(); + cv.setTraversalMask(debugGeometry->getNodeMask()); + cv.pushStateSet(stateSet); + debugCamera->accept(cv); + cv.popStateSet(); + cv.setTraversalMask(traversalMask); + + cv.getState()->setCheckForGLErrors(osg::State::ONCE_PER_ATTRIBUTE); + } + + osg::ref_ptr camera = sd->_camera; + + camera->setProjectionMatrix(projectionMatrix); + camera->setViewMatrix(viewMatrix); + + if (settings->getDebugDraw()) + { + camera->getViewport()->x() = pos_x; + pos_x += static_cast(camera->getViewport()->width()) + 40; + } + + // transform polytope in model coords into light spaces eye coords. + osg::Matrixd invertModelView; + invertModelView.invert(camera->getViewMatrix()); + + osg::Polytope local_polytope(polytope); + local_polytope.transformProvidingInverse(invertModelView); + + + if (numShadowMapsPerLight>1) + { + // compute the start and end range in non-dimensional coords +#if 0 + double r_start = (sm_i == 0) ? -1.0 : (double(sm_i) / double(numShadowMapsPerLight)*2.0 - 1.0); + double r_end = (sm_i + 1 == numShadowMapsPerLight) ? 1.0 : (double(sm_i + 1) / double(numShadowMapsPerLight)*2.0 - 1.0); +#endif + + // hardwired for 2 splits + double r_start = (sm_i == 0) ? -1.0 : splitPoint; + double r_end = (sm_i + 1 == numShadowMapsPerLight) ? 1.0 : splitPoint; + + // for all by the last shadowmap shift the r_end so that it overlaps slightly with the next shadowmap + // to prevent a seam showing through between the shadowmaps + if (sm_i + 10) + { + // not the first shadowmap so insert a polytope to clip the scene from before r_start + + // plane in clip space coords + osg::Plane plane(0.0, 1.0, 0.0, -r_start); + + // transform into eye coords + plane.transformProvidingInverse(projectionMatrix); + local_polytope.getPlaneList().push_back(plane); + + //OSG_NOTICE<<"Adding r_start plane "<0) + { + decoratorStateGraph->setStateSet(selectStateSetForRenderingShadow(*vdd)); + } + + // OSG_NOTICE<<"End of shadow setup Projection matrix "<<*cv.getProjectionMatrix()< +#include namespace MWRender { - class MWShadow : public osgShadow::LightSpacePerspectiveShadowMapDB + class MWShadow : public osgShadow::ViewDependentShadowMap { - protected: - struct ViewData : public LightSpacePerspectiveShadowMapDB::ViewData - { - virtual void init(MWShadow * st, osgUtil::CullVisitor * cv); - }; + public: + MWShadow(); - virtual ViewDependentShadowTechnique::ViewData * initViewDependentData(osgUtil::CullVisitor *cv, ViewDependentShadowTechnique::ViewData * vd) - { - MWShadow::ViewData* td = dynamic_cast(vd); - if (!td) - td = new MWShadow::ViewData; - td->init(this, cv); - return td; - } + virtual void cull(osgUtil::CullVisitor& cv); + protected: + osg::ref_ptr debugCamera; + + osg::ref_ptr debugProgram; + + osg::ref_ptr debugGeometry; + + osg::ref_ptr testTex; }; } diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index b3bfa600b5..4dd282dc43 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -55,7 +55,7 @@ varying vec4 passColor; varying vec3 passViewPos; varying vec3 passNormal; -uniform sampler2DShadow shadowTexture; +uniform sampler2DShadow shadowTexture0; varying vec4 shadowSpaceCoords; #include "lighting.glsl" @@ -116,7 +116,7 @@ void main() gl_FragData[0].xyz = mix(gl_FragData[0].xyz, decalTex.xyz, decalTex.a); #endif - float shadowing = shadow2DProj(shadowTexture, shadowSpaceCoords).r; + float shadowing = shadow2DProj(shadowTexture0, shadowSpaceCoords).r; #if !PER_PIXEL_LIGHTING gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0); diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 9da9dfad5d..e27dd384ea 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -25,7 +25,7 @@ varying vec4 passColor; varying vec3 passViewPos; varying vec3 passNormal; -uniform sampler2DShadow shadowTexture; +uniform sampler2DShadow shadowTexture0; varying vec4 shadowSpaceCoords; #include "lighting.glsl" @@ -68,7 +68,7 @@ void main() gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a; #endif - float shadowing = shadow2DProj(shadowTexture, shadowSpaceCoords).r; + float shadowing = shadow2DProj(shadowTexture0, shadowSpaceCoords).r; #if !PER_PIXEL_LIGHTING gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0);