[osg-users] osgShadow::ShadowMap adding some polish

Robert Osfield robert.osfield at gmail.com
Fri Sep 14 02:10:54 PDT 2007


Hi Mihai,

I'm currently very busy, so only skimming osg-users, and as a
consequence finding it difficult to dive into complex topics.

General comments:

  osgShadow is still in its infancy so comments/suggestions for improvements
  are very welcome.

  Support for perspective shadow maps is really needed, and the
  osgSim::OverlayNode provides some of the required code and a proof of
  concept on the OSG side.  Perspective shadow maps might diminish the need
  to manually specifying the center of shadow.

  W.r.t tracking of lights, it was my intention that the lights in the
subgraph would
  be tracked by allowing the cull traversal to go collect them, then as a post
  process in the ShadowTechnique use these settings.  This keeps things loose
  coupled and means that you don't need to add any extra mechanism to do
  this tracking.

  When posting files, making sure they aren't inlined, such as by
zipping them or
  adjusting your mail tool as its not practical to review inline source files.

Robert.



On 9/13/07, Mihai Radu <radu at cm-labs.com> wrote:
> Hi Robert,
>
> I'm working now on integrating the new osgViewer and osg 2.x into the
> simulator of our company, and as a result I am moving to using
> osgShadow::ShadowMap in place of the old code that I refactored to use
> with osg 1.2, before the first iterations of the NodeKit.
>
> I will extend the osg class to integrate with the company's physics and
> viewer, and I am trying to keep to a minimum the overlap between the osg
> code and the derived class.
> Therefore there are a couple of things that seem simpler to change or
> add-in to osgShadow::ShadowMap, and this way the class gets some
> additions, if they are warranted.
>
> As an example I added functions to get/set a Vec2 for the x/y size of
> the depth texture used. (I attached the changed files)
>
> There are three other points that I will need to work with:
>
> 1. updating the camera & TexGen during cull()
>
>     One part here is to change the way the light is selected, one option
> is to keep a reference to the intended osg::Light, the current code will
> use the last light in the list of the RenderStage.
>     Another is to set a different ways to update, the current way is to
> use the bounding box of the entire scene. Another style that I found
> useful is to use a given reference point and radius, for example so that
> the shadows can follow a vehicle without losing resolution as it moves
> about a terrain, or use the parameters of a given SpotLight.
> I see that ShadowTechnique defines ShadowTechnique::CameraCullCallback,
> but I did not see if it is ever used, is it intended for this kind of
> usage ? Then what would be the best way of using it ?
>
> 2. custom GLSL shaders
>
>     Need to be able to set / get custom programs for rendering the
> scene. I can see this done by either passing an std::string for
> vertex/fragment shaders, or by passing osg::Shader/Program.
>
> 3. Uniform variables for controlling rendering
>
>     I suggest to have a set of standard uniforms ( a standard name like
> for the other ones used by osg ) to help with implementation of custom
> shaders. The values that are immediately needed are baseTexture,
> shadowTexture, ambientBias, and the texture unit used for shadowing
> coordinates, to access the appropriate gl_TexCoord[].
>     When using the nodekit on complex scenes with multiple lights, that
> can be enabled/disabled at run-time, I found that I needed to also use a
> uniform to disable shadow application when the shadow casting light is
> turned off, this can also be used on a per-object basis to turn off
> application of shadows on some parts of the scene. The GL number of the
> shadow-casting light is another important value when a different light
> is used to cast shadows.
>
> I'm eager to get your opinion on this.
>
> Cheers
> Mihai Radu
>
>
> /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
>  *
>  * This library is open source and may be redistributed and/or modified under
>  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
>  * (at your option) any later version.  The full license is in LICENSE file
>  * included with this distribution, and on the openscenegraph.org website.
>  *
>  * This library is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * OpenSceneGraph Public License for more details.
> */
>
> #include <osgShadow/ShadowMap>
> #include <osgShadow/ShadowedScene>
> #include <osg/Notify>
> #include <osg/ComputeBoundsVisitor>
> #include <osg/PolygonOffset>
> #include <osg/CullFace>
> #include <osg/io_utils>
>
> using namespace osgShadow;
>
>
> //////////////////////////////////////////////////////////////////
> // fragment shader
> //
> static const 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
> //
> static const 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),
>     _ambientBias(0.3f,1.2f),
>     _textureSize(1024,1024)
> {
> }
>
> ShadowMap::ShadowMap(const ShadowMap& copy, const osg::CopyOp& copyop):
>     ShadowTechnique(copy,copyop),
>     _textureUnit(copy._textureUnit),
>     _ambientBias(copy._ambientBias),
>     _textureSize(copy._textureSize)
> {
> }
>
> void ShadowMap::setTextureUnit(unsigned int unit)
> {
>     _textureUnit = unit;
> }
>
> void ShadowMap::setAmbientBias(const osg::Vec2& ambientBias)
> {
>     _ambientBias = ambientBias;
> }
>
> void ShadowMap::setTextureSize(const osg::Vec2& textureSize)
> {
>     if(_textureSize != textureSize)
>     {
>         _textureSize = textureSize;
>         dirty();
>     }
> }
>
> void ShadowMap::init()
> {
>     if (!_shadowedScene) return;
>
>     unsigned int tex_width = _textureSize.x();
>     unsigned int tex_height = _textureSize.y();
>
>     _texture = new osg::Texture2D;
>     _texture->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<osg::PolygonOffset> 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<osg::CullFace> 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",_ambientBias);
>         _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<const osg::Light*>(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 (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
>
>             float top   = (bb.radius()/centerDistance)*znear;
>             float right = top;
>
>             _camera->setReferenceFrame(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);
>         }
>         else
>         {
>             // make an orthographic projection
>             osg::Vec3 lightDir(lightpos.x(), lightpos.y(), lightpos.z());
>             lightDir.normalize();
>
>             // set the position far away along the light direction
>             osg::Vec3 position = lightDir * bb.radius()  * 20;
>
>             float centerDistance = (position-bb.center()).length();
>
>             float znear = centerDistance-bb.radius();
>             float zfar  = centerDistance+bb.radius();
>             float zNearRatio = 0.001f;
>             if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
>
>             float top   = bb.radius();
>             float right = top;
>
>             _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
>             _camera->setProjectionMatrixAsOrtho(-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()
> {
> }
>
> /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
>  *
>  * This library is open source and may be redistributed and/or modified under
>  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
>  * (at your option) any later version.  The full license is in LICENSE file
>  * included with this distribution, and on the openscenegraph.org website.
>  *
>  * This library is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * OpenSceneGraph Public License for more details.
> */
>
> #ifndef OSGSHADOW_SHADOWEMAP
> #define OSGSHADOW_SHADOWEMAP 1
>
> #include <osg/Camera>
> #include <osg/Material>
>
> #include <osgShadow/ShadowTechnique>
>
> namespace osgShadow {
>
> /** ShadowedTexture provides an implementation of shadow textures.*/
> class OSGSHADOW_EXPORT ShadowMap : public ShadowTechnique
> {
>     public :
>         ShadowMap();
>
>         ShadowMap(const ShadowMap& es, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
>
>         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; }
>
>         /** Set the values for the ambient bias the shader will use.*/
>         void setAmbientBias(const osg::Vec2& ambientBias );
>
>         /** Get the values that are used for the ambient bias in the shader.*/
>         const osg::Vec2& getAmbientBias() const { return _ambientBias; }
>
>         /** Set the values for the texture size, default at constructions 1024x1024.*/
>         void setTextureSize(const osg::Vec2& textureSize );
>
>         /** Get the values that are used for the depth texture size.*/
>         const osg::Vec2& getTextureSize() const { return _textureSize; }
>
>         /** 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<osg::Camera>       _camera;
>         osg::ref_ptr<osg::TexGen>       _texgen;
>         osg::ref_ptr<osg::Texture2D>    _texture;
>         osg::ref_ptr<osg::StateSet>     _stateset;
>         unsigned int                    _textureUnit;
>         osg::Vec2                       _ambientBias;
>                 osg::Vec2                       _textureSize;
>
> };
>
> }
>
> #endif
>
> _______________________________________________
> osg-users mailing list
> osg-users at lists.openscenegraph.org
> http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
>
>
>



More information about the osg-users mailing list