root/OpenSceneGraph/trunk/src/osgShadow/ShadowMap.cpp @ 7545

Revision 7545, 19.2 kB (checked in by robert, 7 years ago)

From Mahai Radu, improvements to handling of spotlights

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#include <osgShadow/ShadowMap>
15#include <osgShadow/ShadowedScene>
16#include <osg/Notify>
17#include <osg/ComputeBoundsVisitor>
18#include <osg/PolygonOffset>
19#include <osg/CullFace>
20#include <osg/io_utils>
21
22using namespace osgShadow;
23
24#include <iostream>
25//for debug
26#include <osg/LightSource>
27#include <osg/PolygonMode>
28#include <osg/Geometry>
29#include <osgDB/ReadFile>
30#include <osgText/Text>
31
32    //////////////////////////////////////////////////////////////////
33    // fragment shader
34    //
35    static const char fragmentShaderSource_noBaseTexture[] =
36        "uniform sampler2DShadow osgShadow_shadowTexture; \n"
37        "uniform vec2 osgShadow_ambientBias; \n"
38        "\n"
39        "void main(void) \n"
40        "{ \n"
41        "    gl_FragColor = gl_Color * (osgShadow_ambientBias.x + shadow2DProj( osgShadow_shadowTexture, gl_TexCoord[0] ) * osgShadow_ambientBias.y); \n"
42        "}\n";
43
44    //////////////////////////////////////////////////////////////////
45    // fragment shader
46    //
47    static const char fragmentShaderSource_withBaseTexture[] =
48        "uniform sampler2D osgShadow_baseTexture; \n"
49        "uniform sampler2DShadow osgShadow_shadowTexture; \n"
50        "uniform vec2 osgShadow_ambientBias; \n"
51        "\n"
52        "void main(void) \n"
53        "{ \n"
54        "    vec4 color = gl_Color * texture2D( osgShadow_baseTexture, gl_TexCoord[0].xy ); \n"
55        "    gl_FragColor = color * (osgShadow_ambientBias.x + shadow2DProj( osgShadow_shadowTexture, gl_TexCoord[1] ) * osgShadow_ambientBias.y); \n"
56        "}\n";
57
58    //////////////////////////////////////////////////////////////////
59    // fragment shader
60    //
61    static const char fragmentShaderSource_debugHUD_texcoord[] =
62        "uniform sampler2D osgShadow_shadowTexture; \n"
63        " \n"
64        "void main(void) \n"
65        "{ \n"
66        "   vec4 texCoord = gl_TexCoord[1].xyzw; \n"
67        "   float value = texCoord.z / texCoord.w; \n"
68        "   gl_FragColor = vec4( value, value, value, 1.0 ); \n"
69        "} \n";
70
71    static const char fragmentShaderSource_debugHUD[] =
72        "uniform sampler2D osgShadow_shadowTexture; \n"
73        " \n"
74        "void main(void) \n"
75        "{ \n"
76        "   vec4 texResult = texture2D(osgShadow_shadowTexture, gl_TexCoord[0].st ); \n"
77        "   float value = texResult.g - 0.5; \n"
78        "   gl_FragColor = vec4( value, value, value, 0.8 ); \n"
79        "} \n";
80
81    ShadowMap::ShadowMap():
82    _baseTextureUnit(0),
83        _shadowTextureUnit(1),
84        _ambientBias(0.3f,1.2f),
85        _textureSize(1024,1024)
86    {
87    }
88
89    ShadowMap::ShadowMap(const ShadowMap& copy, const osg::CopyOp& copyop):
90    ShadowTechnique(copy,copyop),
91        _baseTextureUnit(copy._baseTextureUnit),
92        _shadowTextureUnit(copy._shadowTextureUnit),
93        _ambientBias(copy._ambientBias),
94        _textureSize(copy._textureSize)
95    {
96    }
97
98    void ShadowMap::setTextureUnit(unsigned int unit)
99    {
100        _shadowTextureUnit = unit;
101    }
102
103    void ShadowMap::setAmbientBias(const osg::Vec2& ambientBias)
104    {
105        _ambientBias = ambientBias;
106    }
107
108    void ShadowMap::setTextureSize(const osg::Vec2s& textureSize)
109    {
110        _textureSize = textureSize;
111        dirty();
112    }
113
114    void ShadowMap::setLight(osg::Light* light)
115    {
116        _light = light;
117    }
118
119
120    void ShadowMap::setLight(osg::LightSource* ls)
121    {
122        _ls = ls;
123        _light = _ls->getLight();
124    }
125
126    void ShadowMap::createUniforms()
127    {
128        _uniformList.clear();
129
130        osg::Uniform* baseTextureSampler = new osg::Uniform("osgShadow_baseTexture",(int)_baseTextureUnit);
131        _uniformList.push_back(baseTextureSampler);
132
133        osg::Uniform* shadowTextureSampler = new osg::Uniform("osgShadow_shadowTexture",(int)_shadowTextureUnit);
134        _uniformList.push_back(shadowTextureSampler);
135
136        osg::Uniform* ambientBias = new osg::Uniform("osgShadow_ambientBias",_ambientBias);
137        _uniformList.push_back(ambientBias);
138
139    }
140
141    void ShadowMap::createShaders()
142    {
143        // if we are not given shaders, use the default
144        if( _shaderList.empty() )
145        {
146            if (_shadowTextureUnit==0)
147            {
148                osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture);
149                _shaderList.push_back(fragment_shader);
150            }
151            else
152            {
153                osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture);
154                _shaderList.push_back(fragment_shader);
155
156            }
157        }
158    }
159
160    void ShadowMap::init()
161    {
162        if (!_shadowedScene) return;
163
164        _texture = new osg::Texture2D;
165        _texture->setTextureSize(_textureSize.x(), _textureSize.y());
166        _texture->setInternalFormat(GL_DEPTH_COMPONENT);
167        _texture->setShadowComparison(true);
168        _texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
169        _texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
170        _texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
171
172        // the shadow comparison should fail if object is outside the texture
173        _texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
174        _texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
175        _texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
176
177        // set up the render to texture camera.
178        {
179            // create the camera
180            _camera = new osg::Camera;
181
182            _camera->setCullCallback(new CameraCullCallback(this));
183
184            _camera->setClearMask(GL_DEPTH_BUFFER_BIT);
185            //_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
186            _camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
187            _camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
188
189            // set viewport
190            _camera->setViewport(0,0,_textureSize.x(),_textureSize.y());
191
192            // set the camera to render before the main camera.
193            _camera->setRenderOrder(osg::Camera::PRE_RENDER);
194
195            // tell the camera to use OpenGL frame buffer object where supported.
196            _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
197            //_camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW);
198
199            // attach the texture and use it as the color buffer.
200            _camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());
201
202            osg::StateSet* stateset = _camera->getOrCreateStateSet();
203
204            float factor = 0.0f;
205            float units = 1.0f;
206
207            osg::ref_ptr<osg::PolygonOffset> polygon_offset = new osg::PolygonOffset;
208            polygon_offset->setFactor(factor);
209            polygon_offset->setUnits(units);
210            stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
211            stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
212
213            osg::ref_ptr<osg::CullFace> cull_face = new osg::CullFace;
214            cull_face->setMode(osg::CullFace::FRONT);
215            stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
216            stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
217
218        }
219
220        {
221            _stateset = new osg::StateSet;       
222            _stateset->setTextureAttributeAndModes(_shadowTextureUnit,_texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
223            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
224            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
225            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
226            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
227
228            _texgen = new osg::TexGen;
229
230            // add Program, when empty of Shaders then we are using fixed functionality
231            _program = new osg::Program;
232            _stateset->setAttribute(_program.get());
233
234            // create default shaders if needed
235            createShaders();
236
237            // add the shader list to the program
238            for(ShaderList::const_iterator itr=_shaderList.begin();
239                itr!=_shaderList.end();
240                ++itr)
241            {
242                _program->addShader(itr->get());
243            }
244
245            // create own uniforms
246            createUniforms();
247
248            // add the uniform list to the stateset
249            for(UniformList::const_iterator itr=_uniformList.begin();
250                itr!=_uniformList.end();
251                ++itr)
252            {
253                _stateset->addUniform(itr->get());
254            }
255
256        }
257
258        _dirty = false;
259    }
260
261
262    void ShadowMap::update(osg::NodeVisitor& nv)
263    {
264        _shadowedScene->osg::Group::traverse(nv);
265    }
266
267    void ShadowMap::cull(osgUtil::CullVisitor& cv)
268    {
269        // record the traversal mask on entry so we can reapply it later.
270        unsigned int traversalMask = cv.getTraversalMask();
271
272        osgUtil::RenderStage* orig_rs = cv.getRenderStage();
273
274        // do traversal of shadow recieving scene which does need to be decorated by the shadow map
275        {
276            cv.pushStateSet(_stateset.get());
277
278            _shadowedScene->osg::Group::traverse(cv);
279
280            cv.popStateSet();
281
282        }
283
284        // need to compute view frustum for RTT camera.
285        // 1) get the light position
286        // 2) get the center and extents of the view frustum
287
288        const osg::Light* selectLight = 0;
289        osg::Vec4 lightpos;
290        osg::Vec3 lightDir;
291
292        //MR testing giving a specific light
293        osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
294        for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
295            itr != aml.end();
296            ++itr)
297        {
298            const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
299            if (light)
300            {
301                if( _light.valid()) {
302                    if( _light.get() == light )
303                        selectLight = light;
304                    else
305                        continue;
306                }
307                else
308                    selectLight = light;
309
310                osg::RefMatrix* matrix = itr->second.get();
311                if (matrix)
312                {
313                    lightpos = light->getPosition() * (*matrix);
314                    lightDir = light->getDirection() * (*matrix);
315                }
316                else 
317                {
318                    lightpos = light->getPosition();
319                    lightDir = light->getDirection();
320                }
321
322            }
323        }
324
325        osg::Matrix eyeToWorld;
326        eyeToWorld.invert(*cv.getModelViewMatrix());
327
328        lightpos = lightpos * eyeToWorld;
329        //MR do we do this for the direction also ? preliminary Yes, but still no good result
330        lightDir = lightDir * eyeToWorld;
331        lightDir.normalize();
332
333        if (selectLight)
334        {
335
336            //std::cout<<"----- VxOSG::ShadowMap selectLight spot cutoff "<<selectLight->getSpotCutoff()<<std::endl;
337
338            if(selectLight->getSpotCutoff() < 180.0f)   // spotlight, then we don't need the bounding box
339            {
340                osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
341                float spotAngle = selectLight->getSpotCutoff();
342                _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
343                _camera->setProjectionMatrixAsPerspective(spotAngle, 1.0, 0.1, 1000.0);
344                _camera->setViewMatrixAsLookAt(position,position+lightDir,osg::Vec3(0.0f,1.0f,0.0f));
345            }
346            else
347            {
348                // get the bounds of the model.   
349                osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
350                cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask());
351
352                _shadowedScene->osg::Group::traverse(cbbv);
353
354                osg::BoundingBox bb = cbbv.getBoundingBox();
355
356                if (lightpos[3]!=0.0)   // point light
357                {
358                    osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
359
360                    float centerDistance = (position-bb.center()).length();
361
362                    float znear = centerDistance-bb.radius();
363                    float zfar  = centerDistance+bb.radius();
364                    float zNearRatio = 0.001f;
365                    if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
366
367                    float top   = (bb.radius()/centerDistance)*znear;
368                    float right = top;
369
370                    _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
371                    _camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
372                    _camera->setViewMatrixAsLookAt(position,bb.center(),osg::Vec3(0.0f,1.0f,0.0f));
373                }
374                else    // directional light
375                {
376                    // make an orthographic projection
377                    osg::Vec3 lightDir(lightpos.x(), lightpos.y(), lightpos.z());
378                    lightDir.normalize();
379
380                    // set the position far away along the light direction
381                    osg::Vec3 position = lightDir * bb.radius()  * 20;
382
383                    float centerDistance = (position-bb.center()).length();
384
385                    float znear = centerDistance-bb.radius();
386                    float zfar  = centerDistance+bb.radius();
387                    float zNearRatio = 0.001f;
388                    if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
389
390                    float top   = bb.radius();
391                    float right = top;
392
393                    _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
394                    _camera->setProjectionMatrixAsOrtho(-right, right, -top, top, znear, zfar);
395                    _camera->setViewMatrixAsLookAt(position,bb.center(),osg::Vec3(0.0f,1.0f,0.0f));
396                }
397
398
399            }
400                // compute the matrix which takes a vertex from local coords into tex coords
401                // will use this later to specify osg::TexGen..
402                osg::Matrix MVPT = _camera->getViewMatrix() *
403                    _camera->getProjectionMatrix() *
404                    osg::Matrix::translate(1.0,1.0,1.0) *
405                    osg::Matrix::scale(0.5f,0.5f,0.5f);
406
407                _texgen->setMode(osg::TexGen::EYE_LINEAR);
408                _texgen->setPlanesFromMatrix(MVPT);
409
410            cv.setTraversalMask( traversalMask &
411                getShadowedScene()->getCastsShadowTraversalMask() );
412
413            // do RTT camera traversal
414            _camera->accept(cv);
415
416            orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(_shadowTextureUnit, cv.getModelViewMatrix(), _texgen.get());
417        } // if(selectLight)
418
419
420        // reapply the original traversal mask
421        cv.setTraversalMask( traversalMask );
422    }
423
424    void ShadowMap::cleanSceneGraph()
425    {
426    }
427
428    ///////////////////// Debug Methods
429
430    osg::ref_ptr<osg::Camera> ShadowMap::makeDebugHUD()
431    {
432        osg::ref_ptr<osg::Camera> camera = new osg::Camera;
433
434        osg::Vec2 size(1280, 1024);
435        // set the projection matrix
436        camera->setProjectionMatrix(osg::Matrix::ortho2D(0,size.x(),0,size.y()));
437
438        // set the view matrix   
439        camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
440        camera->setViewMatrix(osg::Matrix::identity());
441
442        // only clear the depth buffer
443        camera->setClearMask(GL_DEPTH_BUFFER_BIT);
444        camera->setClearColor(osg::Vec4(0.2f, 0.3f, 0.5f, 0.2f));
445        //camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
446
447        // draw subgraph after main camera view.
448        camera->setRenderOrder(osg::Camera::POST_RENDER);
449
450        // we don't want the camera to grab event focus from the viewers main camera(s).
451        camera->setAllowEventFocus(false);
452
453        osg::Geode* geode = new osg::Geode;
454
455        osg::Vec3 position(10.0f,size.y()-100.0f,0.0f);
456        osg::Vec3 delta(0.0f,-120.0f,0.0f);
457        float lenght = 300.0f;
458
459        // turn the text off to avoid linking with osgText
460#if 0
461        std::string timesFont("fonts/arial.ttf");
462
463        {
464            osgText::Text* text = new  osgText::Text;
465            geode->addDrawable( text );
466
467            text->setFont(timesFont);
468            text->setPosition(position);
469            text->setText("Shadow Map HUD");
470
471            position += delta;
472        }
473#endif
474       
475        osg::Vec3 widthVec(lenght, 0.0f, 0.0f);
476        osg::Vec3 depthVec(0.0f,lenght, 0.0f);
477        osg::Vec3 centerBase( 10.0f + lenght/2, size.y()-lenght/2, 0.0f);
478        centerBase += delta;
479       
480        geode->addDrawable( osg::createTexturedQuadGeometry( centerBase-widthVec*0.5f-depthVec*0.5f,
481                                                             widthVec, depthVec) );
482
483        osg::StateSet* stateset = geode->getOrCreateStateSet();
484
485        stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
486        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
487        //stateset->setAttribute(new osg::PolygonOffset(1.0f,1.0f),osg::StateAttribute::ON);
488        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
489
490        // test with regular texture
491        //stateset->setTextureAttributeAndModes(_baseTextureUnit, new osg::Texture2D(osgDB::readImageFile("Images/lz.rgb")));
492
493        stateset->setTextureAttributeAndModes(_shadowTextureUnit,_texture.get(),osg::StateAttribute::ON);
494       
495        //test to check the texture coordinates generated during shadow pass
496#if 0
497        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
498        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
499        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
500        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
501
502        // create TexGen node
503        osg::ref_ptr<osg::TexGenNode> texGenNode = new osg::TexGenNode;
504        texGenNode->setTextureUnit(_shadowTextureUnit);
505        texGenNode->setTexGen(_texgen.get());
506        camera->addChild(texGenNode.get());
507#endif
508        //shader for correct display
509
510        osg::ref_ptr<osg::Program> program = new osg::Program;
511        stateset->setAttribute(program.get());
512       
513        osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_debugHUD);
514        program->addShader(fragment_shader);
515
516        camera->addChild(geode);
517
518        return camera;
519    }
520
521    //////////////////////// End Debug Section
Note: See TracBrowser for help on using the browser.