From 1d328ba0d4477d637c5d64a39db91ef216caf8c1 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 18 Sep 2008 14:48:28 +0000 Subject: [PATCH] From Christopher Blaesius, "Soft shadow mapping is basically the same as hard shadow mapping beside that it uses a different fragment shader. So for me it makes sense that osgShadow::SoftShadowMap is derived from osgShadow::ShadowMap, this makes it easier to maintain the two classes. Additional SoftShadowMap also provides the same Debug methods as ShadowMap." --- include/osgShadow/ShadowMap | 4 +- include/osgShadow/SoftShadowMap | 102 +++----- src/osgShadow/SoftShadowMap.cpp | 402 ++++++++------------------------ 3 files changed, 139 insertions(+), 369 deletions(-) diff --git a/include/osgShadow/ShadowMap b/include/osgShadow/ShadowMap index 5400998ac..e23084bd8 100644 --- a/include/osgShadow/ShadowMap +++ b/include/osgShadow/ShadowMap @@ -91,9 +91,9 @@ class OSGSHADOW_EXPORT ShadowMap : public ShadowTechnique virtual ~ShadowMap(void) {}; /** Create the managed Uniforms */ - void createUniforms(); + virtual void createUniforms(); - void createShaders(); + virtual void createShaders(); osg::ref_ptr _camera; osg::ref_ptr _texgen; diff --git a/include/osgShadow/SoftShadowMap b/include/osgShadow/SoftShadowMap index 1556f7e52..6e7e8fd58 100644 --- a/include/osgShadow/SoftShadowMap +++ b/include/osgShadow/SoftShadowMap @@ -16,13 +16,15 @@ #include #include +#include +#include -#include +#include namespace osgShadow { /** SoftShadowMap provides an implementation of soft shadows with shadow maps.*/ -class OSGSHADOW_EXPORT SoftShadowMap : public ShadowTechnique +class OSGSHADOW_EXPORT SoftShadowMap : public ShadowMap { public : SoftShadowMap(); @@ -31,85 +33,57 @@ class OSGSHADOW_EXPORT SoftShadowMap : public ShadowTechnique META_Object(osgShadow, SoftShadowMap); - /** 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 ); - - - /** Set the resolution of the rendertarget texture used for shadow generation */ - void setTextureSize(int width, int height) { setTextureSize(osg::Vec2s(width, height)); } - - /** Set the resolution of the rendertarget texture used for shadow generation */ - void setTextureSize(const osg::Vec2s&); - - /** Get the resolution of the rendertarget texture used for shadow generation */ - const osg::Vec2s& getTextureSize() const { return _textureSize; } - - - /** Add a small bias to the z-value when calculating the MVPT matrix, this can reduce - * shadow acne problem. - * Suitable values are 0-0.005 - * Default is 0. */ - void setBias(float bias) { _bias = bias; } - - /** Return the bias value set used when calculating the MVPT matrix */ - float getBias() const { return _bias; } - - /** Set the values for width of the soft penumbra the shader will use. * Zero is for hard shadow (no penumbra). 0.01 is already very soft penumbra. * Default is 0.005.*/ - void setSoftnessWidth(const float softnesswidth ); + void setSoftnessWidth(const float softnessWidth); + + /** Get the value used for width of the soft penumbra in the shader.*/ + float getSoftnessWidth() const { return _softnessWidth; } /** Set the values for jittering scale the shader will use. * Zero is no jittering (i.e. see the banding in penumbra) * High values (>64) cause 'pixelization' of the penumbra. * Usually but not necessarily power of two number. * Default is 32. */ - void setJitteringScale(const float jitteringscale ); - - /** Get the values that are used for the ambient bias in the shader.*/ - const osg::Vec2& getAmbientBias() const { return _ambientBias; } - - /** Get the value used for width of the soft penumbra in the shader.*/ - const float getSoftnessWidth() const { return _softnesswidth; } + void setJitteringScale(const float jitteringScale); /** Get the value used for jittering scale in the shader.*/ - const float getJitteringScale() const { return _jitteringscale; } + float getJitteringScale() const { return _jitteringScale; } - /** initialize the ShadowedScene and local cached data structures.*/ - virtual void init(); + /** Set the texture unit that the jitter texture will be applied on.*/ + void setJitterTextureUnit(unsigned int jitterTextureUnit); - /** 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(); + /** Get the texture unit that the jitter texture will be applied on.*/ + unsigned int getJitterTextureUnit() const { return _jitterTextureUnit; } - protected : + /** Add a small bias to the z-value, this can reduce + * shadow acne problem. + * This is the same as calling setPolygonOffset(osg::Vec2(bias,0)); + * Suitable values are 0-0.005 + * Default is 0. */ + void setBias(float bias) { setPolygonOffset(osg::Vec2(bias,0)); } + + /** Return the bias value */ + float getBias() const { return getPolygonOffset().x(); } - virtual ~SoftShadowMap() {} - void initJittering(osg::StateSet *); - osg::ref_ptr _camera; - osg::ref_ptr _texgen; - osg::ref_ptr _texture; - osg::ref_ptr _stateset; - unsigned int _textureUnit; - osg::Vec2 _ambientBias; - float _softnesswidth; - float _jitteringscale; - float _bias; - osg::Vec2s _textureSize; + protected: + virtual ~SoftShadowMap(void) {}; + + /** Create the managed Uniforms */ + void createUniforms(); + void createShaders(); + void initJittering(osg::StateSet *ss); + + osg::ref_ptr _softnessWidthUniform; + osg::ref_ptr _jitteringScaleUniform; + float _softnessWidth; + float _jitteringScale; + unsigned int _jitterTextureUnit; + + }; diff --git a/src/osgShadow/SoftShadowMap.cpp b/src/osgShadow/SoftShadowMap.cpp index 4bc1457db..b987160e8 100755 --- a/src/osgShadow/SoftShadowMap.cpp +++ b/src/osgShadow/SoftShadowMap.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -21,13 +22,19 @@ #include #include - -#include -#include -#include - using namespace osgShadow; +#include +//for debug +#include +#include +#include +#include +#include +#include +#include + +#define IMPROVE_TEXGEN_PRECISION 1 ////////////////////////////////////////////////////////////////// // fragment shader @@ -35,58 +42,58 @@ using namespace osgShadow; // Implementation from Chapter 17, Efficient Soft-Edged Shadows Using Pixel Shader Branching, Yury Uralsky. // GPU Gems 2, Matt Pharr ed. Addison-Wesley. // -static const char fShaderSource_noBaseTexture[] = +static const char fragmentSoftShaderSource_noBaseTexture[] = "#define SAMPLECOUNT 64 \n" "#define SAMPLECOUNT_FLOAT 64.0 \n" "#define SAMPLECOUNT_D2 32 \n" "#define SAMPLECOUNT_D2_FLOAT 32.0 \n" "#define INV_SAMPLECOUNT (1.0 / SAMPLECOUNT_FLOAT) \n" - "uniform sampler2DShadow shadowTexture; \n" - "uniform sampler3D jitterMapSampler; \n" + "uniform sampler2DShadow osgShadow_shadowTexture; \n" + "uniform sampler3D osgShadow_jitterTexture; \n" - "uniform vec2 ambientBias; \n" - "uniform float softwidth; \n" - "uniform float jscale; \n" + "uniform vec2 osgShadow_ambientBias; \n" + "uniform float osgShadow_softnessWidth; \n" + "uniform float osgShadow_jitteringScale; \n" "void main(void) \n" "{ \n" " vec4 sceneShadowProj = gl_TexCoord[1]; \n" - " float softFactor = softwidth * sceneShadowProj.w; \n" + " float softFactor = osgShadow_softnessWidth * sceneShadowProj.w; \n" " vec4 smCoord = sceneShadowProj; \n" - " vec3 jitterCoord = vec3( gl_FragCoord.xy / jscale, 0.0 ); \n" + " vec3 jitterCoord = vec3( gl_FragCoord.xy / osgShadow_jitteringScale, 0.0 ); \n" " vec4 shadow = vec4(0.0, 0.0, 0.0, 0.0); \n" // First "cheap" sample test " const float pass_div = 1.0 / (2.0 * 4.0); \n" " for ( int i = 0; i < 4; ++i ) \n" " { \n" // Get jitter values in [0,1]; adjust to have values in [-1,1] - " vec4 offset = 2.0 * texture3D( jitterMapSampler, jitterCoord ) -1.0; \n" + " vec4 offset = 2.0 * texture3D( osgShadow_jitterTexture, jitterCoord ) -1.0; \n" " jitterCoord.z += 1.0 / SAMPLECOUNT_D2_FLOAT; \n" " smCoord.xy = sceneShadowProj.xy + (offset.xy) * softFactor; \n" - " shadow += shadow2DProj( shadowTexture, smCoord ) * pass_div; \n" + " shadow += shadow2DProj( osgShadow_shadowTexture, smCoord ) * pass_div; \n" " smCoord.xy = sceneShadowProj.xy + (offset.zw) * softFactor; \n" - " shadow += shadow2DProj( shadowTexture, smCoord ) *pass_div; \n" + " shadow += shadow2DProj( osgShadow_shadowTexture, smCoord ) *pass_div; \n" " } \n" // skip all the expensive shadow sampling if not needed " if ( shadow * (shadow -1.0) != 0.0 ) \n" " { \n" " shadow *= pass_div; \n" " for (int i=0; iset(_jitteringScale); } -void SoftShadowMap::setAmbientBias(const osg::Vec2& ambientBias) +void SoftShadowMap::setSoftnessWidth(const float softnessWidth) { - _ambientBias = ambientBias; + _softnessWidth = softnessWidth; + if (_softnessWidthUniform.valid()) _softnessWidthUniform->set(_softnessWidth); } -void SoftShadowMap::setSoftnessWidth(const float softnesswidth ) +void SoftShadowMap::setJitterTextureUnit(unsigned int jitterTextureUnit) { - _softnesswidth = softnesswidth; + _jitterTextureUnit=jitterTextureUnit; } -void SoftShadowMap::setJitteringScale(const float jitteringscale ) +void SoftShadowMap::createUniforms() { - _jitteringscale = jitteringscale; + _uniformList.clear(); + + osg::Uniform* baseTextureSampler = new osg::Uniform("osgShadow_baseTexture",(int)_baseTextureUnit); + _uniformList.push_back(baseTextureSampler); + + osg::Uniform* shadowTextureSampler = new osg::Uniform("osgShadow_shadowTexture",(int)_shadowTextureUnit); + _uniformList.push_back(shadowTextureSampler); + + _ambientBiasUniform = new osg::Uniform("osgShadow_ambientBias",_ambientBias); + _uniformList.push_back(_ambientBiasUniform.get()); + + _softnessWidthUniform = new osg::Uniform("osgShadow_softnessWidth",_softnessWidth); + _uniformList.push_back(_softnessWidthUniform.get()); + + _jitteringScaleUniform = new osg::Uniform("osgShadow_jitteringScale",_jitteringScale); + _uniformList.push_back(_jitteringScaleUniform.get()); + + _jitterTextureUnit=_shadowTextureUnit+1; + initJittering(_stateset.get()); + + osg::Uniform* jitterTextureSampler = new osg::Uniform("osgShadow_jitterTexture",(int)_jitterTextureUnit); + _uniformList.push_back(jitterTextureSampler); + + } -void SoftShadowMap::setTextureSize(const osg::Vec2s& texsize) -{ - _textureSize = texsize; - - if (_texture.valid()) +void SoftShadowMap::createShaders() +{ + // if we are not given shaders, use the default + if( _shaderList.empty() ) { - _texture->setTextureSize(_textureSize[0], _textureSize[1]); - _camera->setViewport(0,0,_textureSize[0], _textureSize[1]); - } -} - - -void SoftShadowMap::init() -{ - if (!_shadowedScene) return; - - _texture = new osg::Texture2D; - _texture->setTextureSize(_textureSize[0], _textureSize[1]); - _texture->setInternalFormat(GL_DEPTH_COMPONENT); - _texture->setSourceType(GL_UNSIGNED_INT); - - // Sets GL_TEXTURE_COMPARE_MODE_ARB to GL_COMPARE_R_TO_TEXTURE_ARB - _texture->setShadowComparison(true); - _texture->setShadowCompareFunc(osg::Texture::LEQUAL); - - _texture->setShadowTextureMode(osg::Texture::LUMINANCE); - _texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR); - _texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR); - _texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); - _texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - - - - // set up the render to texture camera. - { - // create the camera - _camera = new osg::Camera; - - _camera->setInheritanceMask(0x0); - _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,_textureSize[0],_textureSize[1]); - - // 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; - - // set culling to BACK facing (cf message from Wojtek Lewandowski in osg Mailing list) - 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; - - - osg::Program* program = new osg::Program; - _stateset->setAttribute(program); - - if ( _textureUnit==0) + if (_shadowTextureUnit==0) { - osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fShaderSource_noBaseTexture); - program->addShader(fragment_shader); + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentSoftShaderSource_noBaseTexture); + _shaderList.push_back(fragment_shader); } else { - osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fShaderSource_withBaseTexture); - program->addShader(fragment_shader); - - osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0); - _stateset->addUniform(baseTextureSampler); + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentSoftShaderSource_withBaseTexture); + _shaderList.push_back(fragment_shader); } - - osg::Uniform* shadowTextureSampler = new osg::Uniform("shadowTexture",(int)_textureUnit); - _stateset->addUniform(shadowTextureSampler); - - osg::Uniform* ambientBias = new osg::Uniform("ambientBias",_ambientBias); - _stateset->addUniform(ambientBias); - - // bhbn - // Initialisation of jittering texture - initJittering(_stateset.get()); - - osg::Uniform* jitterMapSampler = new osg::Uniform("jitterMapSampler",(int)_textureUnit + 1); - _stateset->addUniform(jitterMapSampler); - - osg::Uniform* softwidth = new osg::Uniform("softwidth",_softnesswidth); - _stateset->addUniform(softwidth); - - osg::Uniform* jscale = new osg::Uniform("jscale",_jitteringscale); - _stateset->addUniform(jscale); - } - - _dirty = false; } -void SoftShadowMap::update(osg::NodeVisitor& nv) -{ - _shadowedScene->osg::Group::traverse(nv); -} - -void SoftShadowMap::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 receiving 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(); - - - osg::Vec3 position; - - if (lightpos[3]!=0.0) - { - position.set(lightpos.x(), lightpos.y(), lightpos.z()); - } - 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 - 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 (znearsetReferenceFrame(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.5,0.5,0.5+_bias); - - _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 SoftShadowMap::cleanSceneGraph() -{ -} - // Implementation from Chapter 17, Efficient Soft-Edged Shadows Using Pixel Shader Branching, Yury Uralsky. // GPU Gems 2, Matt Pharr ed. Addison-Wesley. // @@ -519,10 +315,10 @@ void SoftShadowMap::initJittering(osg::StateSet *ss) image3D->setImage(size, size, R, GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE, data3D, osg::Image::USE_NEW_DELETE); texture3D->setImage(image3D); - ss->setTextureAttributeAndModes((int)_textureUnit + 1, texture3D, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - ss->setTextureMode((int)_textureUnit + 1,GL_TEXTURE_GEN_S,osg::StateAttribute::ON); - ss->setTextureMode((int)_textureUnit + 1,GL_TEXTURE_GEN_T,osg::StateAttribute::ON); - ss->setTextureMode((int)_textureUnit + 1,GL_TEXTURE_GEN_R,osg::StateAttribute::ON); + ss->setTextureAttributeAndModes(_jitterTextureUnit, texture3D, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + ss->setTextureMode(_jitterTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON); + ss->setTextureMode(_jitterTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON); + ss->setTextureMode(_jitterTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON); }