diff --git a/include/osgShadow/SoftShadowMap b/include/osgShadow/SoftShadowMap new file mode 100644 index 000000000..62999c690 --- /dev/null +++ b/include/osgShadow/SoftShadowMap @@ -0,0 +1,94 @@ +/* -*-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_SOFTSHADOWMAP +#define OSGSHADOW_SOFTSHADOWMAP 1 + +#include +#include + +#include + +namespace osgShadow { + +/** SoftShadowMap provides an implementation of soft shadows with shadow maps.*/ +class OSGSHADOW_EXPORT SoftShadowMap : public ShadowTechnique +{ + public : + SoftShadowMap(); + + SoftShadowMap(const SoftShadowMap& es, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); + + 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 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 ); + + /** 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; } + + /** Get the value used for jittering scale in the shader.*/ + const float getJitteringScale() const { return _jitteringscale; } + + /** 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 ~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; +}; + +} + +#endif diff --git a/src/osgShadow/CMakeLists.txt b/src/osgShadow/CMakeLists.txt index 031315832..8b847942d 100644 --- a/src/osgShadow/CMakeLists.txt +++ b/src/osgShadow/CMakeLists.txt @@ -1,41 +1,43 @@ IF (DYNAMIC_OPENSCENEGRAPH) - ADD_DEFINITIONS(-DOSGSHADOW_LIBRARY) + ADD_DEFINITIONS(-DOSGSHADOW_LIBRARY) ELSE (DYNAMIC_OPENSCENEGRAPH) - ADD_DEFINITIONS(-DOSG_LIBRARY_STATIC) + ADD_DEFINITIONS(-DOSG_LIBRARY_STATIC) ENDIF(DYNAMIC_OPENSCENEGRAPH) SET(LIB_NAME osgShadow) SET(HEADER_PATH ${OpenSceneGraph_SOURCE_DIR}/include/${LIB_NAME}) SET(LIB_PUBLIC_HEADERS - ${HEADER_PATH}/Export - ${HEADER_PATH}/OccluderGeometry - ${HEADER_PATH}/ShadowMap - ${HEADER_PATH}/ShadowTechnique - ${HEADER_PATH}/ShadowTexture - ${HEADER_PATH}/ShadowVolume - ${HEADER_PATH}/ShadowedScene - ${HEADER_PATH}/Version + ${HEADER_PATH}/Export + ${HEADER_PATH}/OccluderGeometry + ${HEADER_PATH}/ShadowMap + ${HEADER_PATH}/ShadowTechnique + ${HEADER_PATH}/ShadowTexture + ${HEADER_PATH}/ShadowVolume + ${HEADER_PATH}/ShadowedScene + ${HEADER_PATH}/SoftShadowMap + ${HEADER_PATH}/Version ) # FIXME: For OS X, need flag for Framework or dylib ADD_LIBRARY(${LIB_NAME} - ${OPENSCENEGRAPH_USER_DEFINED_DYNAMIC_OR_STATIC} - ${LIB_PUBLIC_HEADERS} - OccluderGeometry.cpp - ShadowMap.cpp - ShadowTechnique.cpp - ShadowTexture.cpp - ShadowVolume.cpp - ShadowedScene.cpp - Version.cpp + ${OPENSCENEGRAPH_USER_DEFINED_DYNAMIC_OR_STATIC} + ${LIB_PUBLIC_HEADERS} + OccluderGeometry.cpp + ShadowMap.cpp + ShadowTechnique.cpp + ShadowTexture.cpp + ShadowVolume.cpp + ShadowedScene.cpp + SoftShadowMap.cpp + Version.cpp ) -LINK_INTERNAL(${LIB_NAME} - osgUtil - osgDB - osg - OpenThreads +LINK_INTERNAL(${LIB_NAME} + osgUtil + osgDB + osg + OpenThreads ) LINK_CORELIB_DEFAULT(${LIB_NAME}) diff --git a/src/osgShadow/SoftShadowMap.cpp b/src/osgShadow/SoftShadowMap.cpp new file mode 100755 index 000000000..16bf17475 --- /dev/null +++ b/src/osgShadow/SoftShadowMap.cpp @@ -0,0 +1,509 @@ +/* -*-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 +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +using namespace osgShadow; + + +////////////////////////////////////////////////////////////////// +// fragment shader +// +// 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[] = + "#define SAMPLECOUNT 64 \n" + "#define SAMPLECOUNT_D2 32 \n" + "#define INV_SAMPLECOUNT (1.0f / SAMPLECOUNT) \n" + + "uniform sampler2DShadow shadowTexture; \n" + "uniform sampler3D jitterMapSampler; \n" + + "uniform vec2 ambientBias; \n" + "uniform float softwidth; \n" + "uniform float jscale; \n" + + "void main(void) \n" + "{ \n" + " vec4 sceneShadowProj = gl_TexCoord[1]; \n" + " float softFactor = softwidth * sceneShadowProj.w; \n" + " vec4 smCoord = sceneShadowProj; \n" + " vec3 jitterCoord = vec3( gl_FragCoord.xy / jscale, 0.0 ); \n" + " vec4 shadow = vec4(0.0, 0.0, 0.0, 0.0); \n" +// First "cheap" sample test + " const float pass_div = 1.0f / (2.0 * 4); \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" + " jitterCoord.z += 1.0 / SAMPLECOUNT_D2; \n" + + " smCoord.xy = sceneShadowProj.xy + (offset.xy) * softFactor; \n" + " shadow += shadow2DProj( shadowTexture, smCoord ) * pass_div; \n" + + " smCoord.xy = sceneShadowProj.xy + (offset.zw) * softFactor; \n" + " shadow += shadow2DProj( shadowTexture, smCoord ) *pass_div; \n" + " } \n" +// skip all the expensive shadow sampling if not needed + " if ( shadow * (shadow -1.0) != 0 ) \n" + " { \n" + " shadow *= pass_div; \n" + " for (int i=0; isetTextureSize(tex_width, tex_height); + _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->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; + + // 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) + { + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fShaderSource_noBaseTexture); + program->addShader(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::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 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(); + + + 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.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 SoftShadowMap::cleanSceneGraph() +{ +} + +// Implementation from Chapter 17, Efficient Soft-Edged Shadows Using Pixel Shader Branching, Yury Uralsky. +// GPU Gems 2, Matt Pharr ed. Addison-Wesley. +// +// Creates a 3D texture containing jittering data used in the shader to take samples of the shadow map. +void SoftShadowMap::initJittering(osg::StateSet *ss) +{ + // create a 3D texture with hw mipmapping + osg::Texture3D* texture3D = new osg::Texture3D; + texture3D->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::NEAREST); + texture3D->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::NEAREST); + texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::REPEAT); + texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::REPEAT); + texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::REPEAT); + texture3D->setUseHardwareMipMapGeneration(true); + + const uint size = 16; + const uint gridW = 8; + const uint gridH = 8; + uint R = (gridW * gridH / 2); + texture3D->setTextureSize(size, size, R); + + // then create the 3d image to fill with jittering data + osg::Image* image3D = new osg::Image; + unsigned char *data3D = new unsigned char[size * size * R * 4]; + + for ( uint s = 0; s < size; ++s ) + { + for ( uint t = 0; t < size; ++t ) + { + float v[4], d[4]; + + for ( uint r = 0; r < R; ++r ) + { + const int x = r % ( gridW / 2 ); + const int y = ( gridH - 1 ) - ( r / (gridW / 2) ); + + // Generate points on a regular gridW x gridH rectangular + // grid. We multiply x by 2 because, we treat 2 + // consecutive x each loop iteration. Add 0.5f to be in + // the center of the pixel. x, y belongs to [ 0.0, 1.0 ]. + v[0] = float( x * 2 + 0.5f ) / gridW; + v[1] = float( y + 0.5f ) / gridH; + v[2] = float( x * 2 + 1 + 0.5f ) / gridW; + v[3] = v[1]; + + // Jitter positions. ( 0.5f / w ) == ( 1.0f / 2*w ) + v[0] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridW ); + v[1] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridH ); + v[2] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridW ); + v[3] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridH ); + + // Warp to disk; values in [-1,1] + d[0] = sqrtf( v[1] ) * cosf( 2.f * 3.1415926f * v[0] ); + d[1] = sqrtf( v[1] ) * sinf( 2.f * 3.1415926f * v[0] ); + d[2] = sqrtf( v[3] ) * cosf( 2.f * 3.1415926f * v[2] ); + d[3] = sqrtf( v[3] ) * sinf( 2.f * 3.1415926f * v[2] ); + + // store d into unsigned values [0,255] + const uint tmp = ( (r * size * size) + (t * size) + s ) * 4; + data3D[ tmp + 0 ] = (unsigned char)( ( 1.f + d[0] ) * 127 ); + data3D[ tmp + 1 ] = (unsigned char)( ( 1.f + d[1] ) * 127 ); + data3D[ tmp + 2 ] = (unsigned char)( ( 1.f + d[2] ) * 127 ); + data3D[ tmp + 3 ] = (unsigned char)( ( 1.f + d[3] ) * 127 ); + + } + } + } + + // the GPU Gem implementation uses a NV specific internal texture format (GL_SIGNED_RGBA_NV) + // In order to make it more generic, we use GL_RGBA4 which should be cross platform. + 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); + +} +