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

Revision 7539, 17.3 kB (checked in by robert, 7 years ago)

From Mihair Radu, "Most of the additions are small utility methods:
- set the resolution of the shadow map; it calls dirty() to
re-initialize at next update
- keep a list of Shader objects to use instead of the default ones, if
the list is empty, the default shaders are used
- explicitly create the Uniform variables, so that subsequent additions
that require more Uniforms can put them in a central place
- set a Light or LightSource? to use explicitly for shadow casting,
allows multiple lights in the scene, with one casting shadows

There are two additions that do not ( yet ) function correctly, but in
the present usage they do not interfere with the regular usage of the
techique:
- support for using spotlights, it's using Light.spotCutoff to determine
if it's a spot-light and not point-light,

there is an error in the setup of either the shadow camera or the

texgen, most likely due to the direction of the spotlight, since the
position is being used just like in point or directional lights.
- creation of a debugHUD

the hud is created properly, ( the example included shows it ), but

it displays only white, there has been some discussion of displaying the
shadow map, but I could not find it, the addition of a simple fragment
shader with the appropriate color transform should get this going."

  • 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    ShadowMap::ShadowMap():
59    _baseTextureUnit(0),
60        _shadowTextureUnit(1),
61        _ambientBias(0.3f,1.2f),
62        _textureSize(1024,1024)
63    {
64    }
65
66    ShadowMap::ShadowMap(const ShadowMap& copy, const osg::CopyOp& copyop):
67    ShadowTechnique(copy,copyop),
68        _baseTextureUnit(copy._baseTextureUnit),
69        _shadowTextureUnit(copy._shadowTextureUnit),
70        _ambientBias(copy._ambientBias),
71        _textureSize(copy._textureSize)
72    {
73    }
74
75    void ShadowMap::setTextureUnit(unsigned int unit)
76    {
77        _shadowTextureUnit = unit;
78    }
79
80    void ShadowMap::setAmbientBias(const osg::Vec2& ambientBias)
81    {
82        _ambientBias = ambientBias;
83    }
84
85    void ShadowMap::setTextureSize(const osg::Vec2s& textureSize)
86    {
87        _textureSize = textureSize;
88        dirty();
89    }
90
91    void ShadowMap::setLight(osg::Light* light)
92    {
93        _light = light;
94    }
95
96
97    void ShadowMap::setLight(osg::LightSource* ls)
98    {
99        _ls = ls;
100        _light = _ls->getLight();
101    }
102
103    void ShadowMap::createUniforms()
104    {
105        _uniformList.clear();
106
107        osg::Uniform* baseTextureSampler = new osg::Uniform("osgShadow_baseTexture",(int)_baseTextureUnit);
108        _uniformList.push_back(baseTextureSampler);
109
110        osg::Uniform* shadowTextureSampler = new osg::Uniform("osgShadow_shadowTexture",(int)_shadowTextureUnit);
111        _uniformList.push_back(shadowTextureSampler);
112
113        osg::Uniform* ambientBias = new osg::Uniform("osgShadow_ambientBias",_ambientBias);
114        _uniformList.push_back(ambientBias);
115
116    }
117
118    void ShadowMap::createShaders()
119    {
120        // if we are not given shaders, use the default
121        if( _shaderList.empty() )
122        {
123            if (_shadowTextureUnit==0)
124            {
125                osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture);
126                _shaderList.push_back(fragment_shader);
127            }
128            else
129            {
130                osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture);
131                _shaderList.push_back(fragment_shader);
132
133            }
134        }
135    }
136
137    void ShadowMap::init()
138    {
139        if (!_shadowedScene) return;
140
141        _texture = new osg::Texture2D;
142        _texture->setTextureSize(_textureSize.x(), _textureSize.y());
143        _texture->setInternalFormat(GL_DEPTH_COMPONENT);
144        _texture->setShadowComparison(true);
145        _texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
146        _texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
147        _texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
148        _texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_EDGE);
149        _texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_EDGE);
150
151        // set up the render to texture camera.
152        {
153            // create the camera
154            _camera = new osg::Camera;
155
156            _camera->setCullCallback(new CameraCullCallback(this));
157
158            _camera->setClearMask(GL_DEPTH_BUFFER_BIT);
159            //_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
160            _camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
161            _camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
162
163            // set viewport
164            _camera->setViewport(0,0,_textureSize.x(),_textureSize.y());
165
166            // set the camera to render before the main camera.
167            _camera->setRenderOrder(osg::Camera::PRE_RENDER);
168
169            // tell the camera to use OpenGL frame buffer object where supported.
170            _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
171            //_camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW);
172
173            // attach the texture and use it as the color buffer.
174            _camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());
175
176            osg::StateSet* stateset = _camera->getOrCreateStateSet();
177
178            float factor = 0.0f;
179            float units = 1.0f;
180
181            osg::ref_ptr<osg::PolygonOffset> polygon_offset = new osg::PolygonOffset;
182            polygon_offset->setFactor(factor);
183            polygon_offset->setUnits(units);
184            stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
185            stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
186
187            osg::ref_ptr<osg::CullFace> cull_face = new osg::CullFace;
188            cull_face->setMode(osg::CullFace::FRONT);
189            stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
190            stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
191
192        }
193
194        {
195            _stateset = new osg::StateSet;       
196            _stateset->setTextureAttributeAndModes(_shadowTextureUnit,_texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
197            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
198            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
199            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
200            _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
201
202            _texgen = new osg::TexGen;
203
204            // add Program, when empty of Shaders then we are using fixed functionality
205            _program = new osg::Program;
206            _stateset->setAttribute(_program.get());
207
208            // create default shaders if needed
209            createShaders();
210
211            // add the shader list to the program
212            for(ShaderList::const_iterator itr=_shaderList.begin();
213                itr!=_shaderList.end();
214                ++itr)
215            {
216                _program->addShader(itr->get());
217            }
218
219            // create own uniforms
220            createUniforms();
221
222            // add the uniform list to the stateset
223            for(UniformList::const_iterator itr=_uniformList.begin();
224                itr!=_uniformList.end();
225                ++itr)
226            {
227                _stateset->addUniform(itr->get());
228            }
229
230        }
231
232        _dirty = false;
233    }
234
235
236    void ShadowMap::update(osg::NodeVisitor& nv)
237    {
238        _shadowedScene->osg::Group::traverse(nv);
239    }
240
241    void ShadowMap::cull(osgUtil::CullVisitor& cv)
242    {
243        // record the traversal mask on entry so we can reapply it later.
244        unsigned int traversalMask = cv.getTraversalMask();
245
246        osgUtil::RenderStage* orig_rs = cv.getRenderStage();
247
248        // do traversal of shadow recieving scene which does need to be decorated by the shadow map
249        {
250            cv.pushStateSet(_stateset.get());
251
252            _shadowedScene->osg::Group::traverse(cv);
253
254            cv.popStateSet();
255
256        }
257
258        // need to compute view frustum for RTT camera.
259        // 1) get the light position
260        // 2) get the center and extents of the view frustum
261
262        const osg::Light* selectLight = 0;
263        osg::Vec4 lightpos;
264        osg::Vec3 lightDir;
265
266        //MR testing giving a specific light
267        osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
268        for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
269            itr != aml.end();
270            ++itr)
271        {
272            const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
273            if (light)
274            {
275                if( _light.valid()) {
276                    if( _light.get() == light )
277                        selectLight = light;
278                    else
279                        continue;
280                }
281                else
282                    selectLight = light;
283
284                osg::RefMatrix* matrix = itr->second.get();
285                if (matrix)
286                {
287                    lightpos = light->getPosition() * (*matrix);
288                    lightDir = light->getDirection() * (*matrix);
289                }
290                else 
291                {
292                    lightpos = light->getPosition();
293                    lightDir = light->getDirection();
294                }
295
296            }
297        }
298
299        osg::Matrix eyeToWorld;
300        eyeToWorld.invert(*cv.getModelViewMatrix());
301
302        lightpos = lightpos * eyeToWorld;
303        //MR do we do this for the direction also ? preliminary Yes, but still no good result
304        lightDir = lightDir * eyeToWorld;
305        lightDir.normalize();
306
307        if (selectLight)
308        {
309
310            //std::cout<<"----- VxOSG::ShadowMap selectLight spot cutoff "<<selectLight->getSpotCutoff()<<std::endl;
311
312            if(selectLight->getSpotCutoff() < 180.0f)   // spotlight, then we don't need the bounding box
313            {
314                osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
315                float spotAngle = selectLight->getSpotCutoff();
316                _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
317                _camera->setProjectionMatrixAsPerspective(spotAngle, 1.0, 0.1, 1000.0);
318                _camera->setViewMatrixAsLookAt(position,position+lightDir,osg::Vec3(0.0f,1.0f,0.0f));
319            }
320            else
321            {
322                // get the bounds of the model.   
323                osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
324                cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask());
325
326                _shadowedScene->osg::Group::traverse(cbbv);
327
328                osg::BoundingBox bb = cbbv.getBoundingBox();
329
330                if (lightpos[3]!=0.0)   // point light
331                {
332                    osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
333
334                    float centerDistance = (position-bb.center()).length();
335
336                    float znear = centerDistance-bb.radius();
337                    float zfar  = centerDistance+bb.radius();
338                    float zNearRatio = 0.001f;
339                    if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
340
341                    float top   = (bb.radius()/centerDistance)*znear;
342                    float right = top;
343
344                    _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
345                    _camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
346                    _camera->setViewMatrixAsLookAt(position,bb.center(),osg::Vec3(0.0f,1.0f,0.0f));
347                }
348                else    // directional light
349                {
350                    // make an orthographic projection
351                    osg::Vec3 lightDir(lightpos.x(), lightpos.y(), lightpos.z());
352                    lightDir.normalize();
353
354                    // set the position far away along the light direction
355                    osg::Vec3 position = lightDir * bb.radius()  * 20;
356
357                    float centerDistance = (position-bb.center()).length();
358
359                    float znear = centerDistance-bb.radius();
360                    float zfar  = centerDistance+bb.radius();
361                    float zNearRatio = 0.001f;
362                    if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
363
364                    float top   = bb.radius();
365                    float right = top;
366
367                    _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
368                    _camera->setProjectionMatrixAsOrtho(-right, right, -top, top, znear, zfar);
369                    _camera->setViewMatrixAsLookAt(position,bb.center(),osg::Vec3(0.0f,1.0f,0.0f));
370                }
371
372                // compute the matrix which takes a vertex from local coords into tex coords
373                // will use this later to specify osg::TexGen..
374                osg::Matrix MVPT = _camera->getViewMatrix() *
375                    _camera->getProjectionMatrix() *
376                    osg::Matrix::translate(1.0,1.0,1.0) *
377                    osg::Matrix::scale(0.5f,0.5f,0.5f);
378
379                _texgen->setMode(osg::TexGen::EYE_LINEAR);
380                _texgen->setPlanesFromMatrix(MVPT);
381            }
382
383
384            cv.setTraversalMask( traversalMask &
385                getShadowedScene()->getCastsShadowTraversalMask() );
386
387            // do RTT camera traversal
388            _camera->accept(cv);
389
390            orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(_shadowTextureUnit, cv.getModelViewMatrix(), _texgen.get());
391        } // if(selectLight)
392
393
394        // reapply the original traversal mask
395        cv.setTraversalMask( traversalMask );
396    }
397
398    void ShadowMap::cleanSceneGraph()
399    {
400    }
401
402    ///////////////////// Debug Methods
403
404    osg::ref_ptr<osg::Camera> ShadowMap::makeDebugHUD()
405    {
406        osg::ref_ptr<osg::Camera> camera = new osg::Camera;
407
408        osg::Vec2 size(1280, 1024);
409        // set the projection matrix
410        camera->setProjectionMatrix(osg::Matrix::ortho2D(0,size.x(),0,size.y()));
411
412        // set the view matrix   
413        camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
414        camera->setViewMatrix(osg::Matrix::identity());
415
416        // only clear the depth buffer
417        camera->setClearMask(GL_DEPTH_BUFFER_BIT);
418        camera->setClearColor(osg::Vec4(0.2f, 0.3f, 0.5f, 0.2f));
419        //camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
420
421        // draw subgraph after main camera view.
422        camera->setRenderOrder(osg::Camera::POST_RENDER);
423
424        // we don't want the camera to grab event focus from the viewers main camera(s).
425        camera->setAllowEventFocus(false);
426
427        osg::Geode* geode = new osg::Geode;
428
429        osg::Vec3 position(10.0f,size.y()-100.0f,0.0f);
430        osg::Vec3 delta(0.0f,-120.0f,0.0f);
431        float lenght = 300.0f;
432
433        // turn the text off to avoid linking with osgText
434#if 0
435        std::string timesFont("fonts/arial.ttf");
436
437        {
438            osgText::Text* text = new  osgText::Text;
439            geode->addDrawable( text );
440
441            text->setFont(timesFont);
442            text->setPosition(position);
443            text->setText("Shadow Map HUD");
444
445            position += delta;
446        }
447#endif
448       
449        osg::Vec3 widthVec(lenght, 0.0f, 0.0f);
450        osg::Vec3 depthVec(0.0f,lenght, 0.0f);
451        osg::Vec3 centerBase( 10.0f + lenght/2, size.y()-lenght/2, 0.0f);
452        centerBase += delta;
453       
454        geode->addDrawable( osg::createTexturedQuadGeometry( centerBase-widthVec*0.5f-depthVec*0.5f,
455                                                             widthVec, depthVec) );
456
457        osg::StateSet* stateset = geode->getOrCreateStateSet();
458
459        stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
460        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
461        //stateset->setAttribute(new osg::PolygonOffset(1.0f,1.0f),osg::StateAttribute::ON);
462        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
463
464        //stateset->setTextureAttributeAndModes(_baseTextureUnit, new osg::Texture2D(osgDB::readImageFile("Images/lz.rgb")));
465        stateset->setTextureAttributeAndModes(_baseTextureUnit,_texture.get(),osg::StateAttribute::ON);
466
467        //TODO might need to have a shader for correct display
468
469        camera->addChild(geode);
470
471        return camera;
472    }
473
474    //////////////////////// End Debug Section
Note: See TracBrowser for help on using the browser.