diff --git a/include/osgShadow/ShadowMap b/include/osgShadow/ShadowMap index 987354812..531db3029 100644 --- a/include/osgShadow/ShadowMap +++ b/include/osgShadow/ShadowMap @@ -15,6 +15,7 @@ #define OSGSHADOW_SHADOWEMAP 1 #include +#include #include @@ -30,9 +31,35 @@ class OSGSHADOW_EXPORT ShadowMap : public ShadowTechnique META_Object(osgShadow, ShadowMap); + /** Set the texture unit that the shadow texture will be applied on.*/ + void setTextureUnit(unsigned int unit); + + /** Get the texture unit that the shadow texture will be applied on.*/ + unsigned int getTextureUnit() const { return _textureUnit; } + + + /** initialize the ShadowedScene and local cached data structures.*/ + virtual void init(); + + /** run the update traversal of the ShadowedScene and update any loca chached data structures.*/ + virtual void update(osg::NodeVisitor& nv); + + /** run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/ + virtual void cull(osgUtil::CullVisitor& cv); + + /** Clean scene graph from any shadow technique specific nodes, state and drawables.*/ + virtual void cleanSceneGraph(); + + protected : virtual ~ShadowMap() {} + + osg::ref_ptr _camera; + osg::ref_ptr _texgen; + osg::ref_ptr _texture; + osg::ref_ptr _stateset; + unsigned int _textureUnit; }; diff --git a/include/osgShadow/ShadowedScene b/include/osgShadow/ShadowedScene index 0cb146d5b..220a69aba 100644 --- a/include/osgShadow/ShadowedScene +++ b/include/osgShadow/ShadowedScene @@ -28,7 +28,7 @@ class OSGSHADOW_EXPORT ShadowedScene : public osg::Group { public: - ShadowedScene(); + ShadowedScene(ShadowTechnique* st=0); ShadowedScene(const ShadowedScene& es, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); diff --git a/src/osgShadow/ShadowMap.cpp b/src/osgShadow/ShadowMap.cpp index b5a759251..f78cddfac 100644 --- a/src/osgShadow/ShadowMap.cpp +++ b/src/osgShadow/ShadowMap.cpp @@ -12,16 +12,267 @@ */ #include +#include #include +#include +#include +#include +#include using namespace osgShadow; -ShadowMap::ShadowMap() + +////////////////////////////////////////////////////////////////// +// fragment shader +// +char fragmentShaderSource_noBaseTexture[] = + "uniform sampler2DShadow shadowTexture; \n" + "uniform vec2 ambientBias; \n" + "\n" + "void main(void) \n" + "{ \n" + " gl_FragColor = gl_Color * (ambientBias.x + shadow2DProj( shadowTexture, gl_TexCoord[0] ) * ambientBias.y); \n" + "}\n"; + +////////////////////////////////////////////////////////////////// +// fragment shader +// +char fragmentShaderSource_withBaseTexture[] = + "uniform sampler2D baseTexture; \n" + "uniform sampler2DShadow shadowTexture; \n" + "uniform vec2 ambientBias; \n" + "\n" + "void main(void) \n" + "{ \n" + " vec4 color = gl_Color * texture2D( baseTexture, gl_TexCoord[0].xy ); \n" + " gl_FragColor = color * (ambientBias.x + shadow2DProj( shadowTexture, gl_TexCoord[1] ) * ambientBias.y); \n" + "}\n"; + +ShadowMap::ShadowMap(): + _textureUnit(1) { - osg::notify(osg::NOTICE)<<"Warning: osgShadow::ShadowMap technique not implemented yet."<setTextureSize(tex_width, tex_height); + _texture->setInternalFormat(GL_DEPTH_COMPONENT); + _texture->setShadowComparison(true); + _texture->setShadowTextureMode(osg::Texture2D::LUMINANCE); + _texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); + _texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); + + // set up the render to texture camera. + { + // create the camera + _camera = new osg::Camera; + + _camera->setCullCallback(new CameraCullCallback(this)); + + _camera->setClearMask(GL_DEPTH_BUFFER_BIT); + //_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + _camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); + _camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); + + // set viewport + _camera->setViewport(0,0,tex_width,tex_height); + + // set the camera to render before the main camera. + _camera->setRenderOrder(osg::Camera::PRE_RENDER); + + // tell the camera to use OpenGL frame buffer object where supported. + _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + //_camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW); + + // attach the texture and use it as the color buffer. + _camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get()); + + osg::StateSet* stateset = _camera->getOrCreateStateSet(); + + float factor = 0.0f; + float units = 1.0f; + + osg::ref_ptr polygon_offset = new osg::PolygonOffset; + polygon_offset->setFactor(factor); + polygon_offset->setUnits(units); + stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + + osg::ref_ptr cull_face = new osg::CullFace; + cull_face->setMode(osg::CullFace::FRONT); + stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + + } + + { + _stateset = new osg::StateSet; + _stateset->setTextureAttributeAndModes(_textureUnit,_texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON); + _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON); + _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON); + _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON); + + _texgen = new osg::TexGen; + +#if 1 + osg::Program* program = new osg::Program; + _stateset->setAttribute(program); + + if (_textureUnit==0) + { + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture); + program->addShader(fragment_shader); + + osg::Uniform* shadowTextureSampler = new osg::Uniform("shadowTexture",(int)_textureUnit); + _stateset->addUniform(shadowTextureSampler); + } + else + { + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture); + program->addShader(fragment_shader); + + osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0); + _stateset->addUniform(baseTextureSampler); + + osg::Uniform* shadowTextureSampler = new osg::Uniform("shadowTexture",(int)_textureUnit); + _stateset->addUniform(shadowTextureSampler); + } + + osg::Uniform* ambientBias = new osg::Uniform("ambientBias",osg::Vec2(0.3f,1.2f)); + _stateset->addUniform(ambientBias); + +#endif + } + + _dirty = false; +} + + +void ShadowMap::update(osg::NodeVisitor& nv) +{ + _shadowedScene->osg::Group::traverse(nv); +} + +void ShadowMap::cull(osgUtil::CullVisitor& cv) +{ + // record the traversal mask on entry so we can reapply it later. + unsigned int traversalMask = cv.getTraversalMask(); + + osgUtil::RenderStage* orig_rs = cv.getRenderStage(); + + // do traversal of shadow recieving scene which does need to be decorated by the shadow map + { + cv.pushStateSet(_stateset.get()); + + _shadowedScene->osg::Group::traverse(cv); + + cv.popStateSet(); + + } + + // need to compute view frustum for RTT camera. + // 1) get the light position + // 2) get the center and extents of the view frustum + + const osg::Light* selectLight = 0; + osg::Vec4 lightpos; + + osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList(); + for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin(); + itr != aml.end(); + ++itr) + { + const osg::Light* light = dynamic_cast(itr->first.get()); + if (light) + { + osg::RefMatrix* matrix = itr->second.get(); + if (matrix) lightpos = light->getPosition() * (*matrix); + else lightpos = light->getPosition(); + + selectLight = light; + } + } + + osg::Matrix eyeToWorld; + eyeToWorld.invert(cv.getModelViewMatrix()); + + lightpos = lightpos * eyeToWorld; + + if (selectLight) + { + + // get the bounds of the model. + osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); + cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask()); + + _shadowedScene->osg::Group::traverse(cbbv); + + osg::BoundingBox bb = cbbv.getBoundingBox(); + + if (lightpos[3]!=0.0) + { + osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z()); + + float centerDistance = (position-bb.center()).length(); + + float znear = centerDistance-bb.radius(); + float zfar = centerDistance+bb.radius(); + float zNearRatio = 0.001f; + if (znearsetReferenceFrame(osg::Camera::ABSOLUTE_RF); + _camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar); + _camera->setViewMatrixAsLookAt(position,bb.center(),osg::Vec3(0.0f,1.0f,0.0f)); + + + // compute the matrix which takes a vertex from local coords into tex coords + // will use this later to specify osg::TexGen.. + osg::Matrix MVPT = _camera->getViewMatrix() * + _camera->getProjectionMatrix() * + osg::Matrix::translate(1.0,1.0,1.0) * + osg::Matrix::scale(0.5f,0.5f,0.5f); + + _texgen->setMode(osg::TexGen::EYE_LINEAR); + _texgen->setPlanesFromMatrix(MVPT); + } + + + cv.setTraversalMask( traversalMask & + getShadowedScene()->getCastsShadowTraversalMask() ); + + // do RTT camera traversal + _camera->accept(cv); + + orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(_textureUnit, &cv.getModelViewMatrix(), _texgen.get()); + } + + + // reapply the original traversal mask + cv.setTraversalMask( traversalMask ); +} + +void ShadowMap::cleanSceneGraph() { } diff --git a/src/osgShadow/ShadowTexture.cpp b/src/osgShadow/ShadowTexture.cpp index ff5705e0f..d932ad8e3 100644 --- a/src/osgShadow/ShadowTexture.cpp +++ b/src/osgShadow/ShadowTexture.cpp @@ -31,6 +31,11 @@ ShadowTexture::ShadowTexture(const ShadowTexture& copy, const osg::CopyOp& copyo { } +void ShadowTexture::setTextureUnit(unsigned int unit) +{ + _textureUnit = unit; +} + void ShadowTexture::init() { if (!_shadowedScene) return; @@ -64,7 +69,7 @@ void ShadowTexture::init() // tell the camera to use OpenGL frame buffer object where supported. _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); - //_camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW); + _camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW); // attach the texture and use it as the color buffer. _camera->attach(osg::Camera::COLOR_BUFFER, _texture.get()); diff --git a/src/osgShadow/ShadowedScene.cpp b/src/osgShadow/ShadowedScene.cpp index 87e2db21a..28b9bd46b 100644 --- a/src/osgShadow/ShadowedScene.cpp +++ b/src/osgShadow/ShadowedScene.cpp @@ -21,15 +21,27 @@ using namespace osgShadow; -ShadowedScene::ShadowedScene() +ShadowedScene::ShadowedScene(ShadowTechnique* st): + _recievesShadowTraversalMask(0xffffffff), + _castsShadowTraversalMask(0xffffffff) { setNumChildrenRequiringUpdateTraversal(1); + + if (st) setShadowTechnique(st); } ShadowedScene::ShadowedScene(const ShadowedScene& copy, const osg::CopyOp& copyop): - osg::Group(copy,copyop) + osg::Group(copy,copyop), + _recievesShadowTraversalMask(copy._recievesShadowTraversalMask), + _castsShadowTraversalMask(copy._castsShadowTraversalMask) { - setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1); + setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1); + + if (copy._shadowTechnique.valid()) + { + setShadowTechnique( dynamic_cast(copy._shadowTechnique->clone(copyop)) ); + } + } ShadowedScene::~ShadowedScene()