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

Revision 7635, 21.1 kB (checked in by robert, 6 years ago)

From Mihai Radu, "After someone asked for a fix for non-textured object appearing black
with the shader for textured objects. This works very well in cases
where there could be a mix of textured and non-textured objects in the
scene, and it makes the initialization more robust.
The idea is from PSSM, to add a 1pixel texture to the main rendering as
to provide white for any objects missing textures."

  • 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.r + 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                // fake texture for baseTexture, add a fake texture
258                // we support by default at least one texture layer
259                // without this fake texture we can not support
260                // textured and not textured scene
261
262                // TODO: at the moment the PSSM supports just one texture layer in the GLSL shader, multitexture are
263                //       not yet supported !
264
265                osg::Image* image = new osg::Image;
266                // allocate the image data, noPixels x 1 x 1 with 4 rgba floats - equivilant to a Vec4!
267                int noPixels = 1;
268                image->allocateImage(noPixels,1,1,GL_RGBA,GL_FLOAT);
269                image->setInternalTextureFormat(GL_RGBA);
270                // fill in the image data.
271                osg::Vec4* dataPtr = (osg::Vec4*)image->data();
272                osg::Vec4 color(1,1,1,1);
273                *dataPtr = color;
274                // make fake texture
275                osg::Texture2D* fakeTex = new osg::Texture2D;
276                fakeTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
277                fakeTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
278                fakeTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
279                fakeTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
280                fakeTex->setImage(image);
281                // add fake texture
282                _stateset->setTextureAttribute(_baseTextureUnit,fakeTex,osg::StateAttribute::ON);
283                _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_1D,osg::StateAttribute::OFF);
284                _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_2D,osg::StateAttribute::ON);
285                _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_3D,osg::StateAttribute::OFF);
286            }
287        }
288
289        _dirty = false;
290    }
291
292
293    void ShadowMap::update(osg::NodeVisitor& nv)
294    {
295        _shadowedScene->osg::Group::traverse(nv);
296    }
297
298    void ShadowMap::cull(osgUtil::CullVisitor& cv)
299    {
300        // record the traversal mask on entry so we can reapply it later.
301        unsigned int traversalMask = cv.getTraversalMask();
302
303        osgUtil::RenderStage* orig_rs = cv.getRenderStage();
304
305        // do traversal of shadow recieving scene which does need to be decorated by the shadow map
306        {
307            cv.pushStateSet(_stateset.get());
308
309            _shadowedScene->osg::Group::traverse(cv);
310
311            cv.popStateSet();
312
313        }
314
315        // need to compute view frustum for RTT camera.
316        // 1) get the light position
317        // 2) get the center and extents of the view frustum
318
319        const osg::Light* selectLight = 0;
320        osg::Vec4 lightpos;
321        osg::Vec3 lightDir;
322
323        //MR testing giving a specific light
324        osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
325        for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
326            itr != aml.end();
327            ++itr)
328        {
329            const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
330            if (light)
331            {
332                if( _light.valid()) {
333                    if( _light.get() == light )
334                        selectLight = light;
335                    else
336                        continue;
337                }
338                else
339                    selectLight = light;
340
341                osg::RefMatrix* matrix = itr->second.get();
342                if (matrix)
343                {
344                    lightpos = light->getPosition() * (*matrix);
345                    lightDir = light->getDirection() * (*matrix);
346                }
347                else 
348                {
349                    lightpos = light->getPosition();
350                    lightDir = light->getDirection();
351                }
352
353            }
354        }
355
356        osg::Matrix eyeToWorld;
357        eyeToWorld.invert(*cv.getModelViewMatrix());
358
359        lightpos = lightpos * eyeToWorld;
360        //MR do we do this for the direction also ? preliminary Yes, but still no good result
361        lightDir = lightDir * eyeToWorld;
362        lightDir.normalize();
363
364        if (selectLight)
365        {
366
367            //std::cout<<"----- VxOSG::ShadowMap selectLight spot cutoff "<<selectLight->getSpotCutoff()<<std::endl;
368
369            if(selectLight->getSpotCutoff() < 180.0f)   // spotlight, then we don't need the bounding box
370            {
371                osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
372                float spotAngle = selectLight->getSpotCutoff();
373                _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
374                _camera->setProjectionMatrixAsPerspective(spotAngle, 1.0, 0.1, 1000.0);
375                _camera->setViewMatrixAsLookAt(position,position+lightDir,osg::Vec3(0.0f,1.0f,0.0f));
376            }
377            else
378            {
379                // get the bounds of the model.   
380                osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
381                cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask());
382
383                _shadowedScene->osg::Group::traverse(cbbv);
384
385                osg::BoundingBox bb = cbbv.getBoundingBox();
386
387                if (lightpos[3]!=0.0)   // point light
388                {
389                    osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
390
391                    float centerDistance = (position-bb.center()).length();
392
393                    float znear = centerDistance-bb.radius();
394                    float zfar  = centerDistance+bb.radius();
395                    float zNearRatio = 0.001f;
396                    if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
397
398                    float top   = (bb.radius()/centerDistance)*znear;
399                    float right = top;
400
401                    _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
402                    _camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
403                    _camera->setViewMatrixAsLookAt(position,bb.center(),osg::Vec3(0.0f,1.0f,0.0f));
404                }
405                else    // directional light
406                {
407                    // make an orthographic projection
408                    osg::Vec3 lightDir(lightpos.x(), lightpos.y(), lightpos.z());
409                    lightDir.normalize();
410
411                    // set the position far away along the light direction
412                    osg::Vec3 position = lightDir * bb.radius()  * 20;
413
414                    float centerDistance = (position-bb.center()).length();
415
416                    float znear = centerDistance-bb.radius();
417                    float zfar  = centerDistance+bb.radius();
418                    float zNearRatio = 0.001f;
419                    if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
420
421                    float top   = bb.radius();
422                    float right = top;
423
424                    _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
425                    _camera->setProjectionMatrixAsOrtho(-right, right, -top, top, znear, zfar);
426                    _camera->setViewMatrixAsLookAt(position,bb.center(),osg::Vec3(0.0f,1.0f,0.0f));
427                }
428
429
430            }
431                // compute the matrix which takes a vertex from local coords into tex coords
432                // will use this later to specify osg::TexGen..
433                osg::Matrix MVPT = _camera->getViewMatrix() *
434                    _camera->getProjectionMatrix() *
435                    osg::Matrix::translate(1.0,1.0,1.0) *
436                    osg::Matrix::scale(0.5f,0.5f,0.5f);
437
438                _texgen->setMode(osg::TexGen::EYE_LINEAR);
439                _texgen->setPlanesFromMatrix(MVPT);
440
441            cv.setTraversalMask( traversalMask &
442                getShadowedScene()->getCastsShadowTraversalMask() );
443
444            // do RTT camera traversal
445            _camera->accept(cv);
446
447            orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(_shadowTextureUnit, cv.getModelViewMatrix(), _texgen.get());
448        } // if(selectLight)
449
450
451        // reapply the original traversal mask
452        cv.setTraversalMask( traversalMask );
453    }
454
455    void ShadowMap::cleanSceneGraph()
456    {
457    }
458
459    ///////////////////// Debug Methods
460
461    osg::ref_ptr<osg::Camera> ShadowMap::makeDebugHUD()
462    {
463        osg::ref_ptr<osg::Camera> camera = new osg::Camera;
464
465        osg::Vec2 size(1280, 1024);
466        // set the projection matrix
467        camera->setProjectionMatrix(osg::Matrix::ortho2D(0,size.x(),0,size.y()));
468
469        // set the view matrix   
470        camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
471        camera->setViewMatrix(osg::Matrix::identity());
472
473        // only clear the depth buffer
474        camera->setClearMask(GL_DEPTH_BUFFER_BIT);
475        camera->setClearColor(osg::Vec4(0.2f, 0.3f, 0.5f, 0.2f));
476        //camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
477
478        // draw subgraph after main camera view.
479        camera->setRenderOrder(osg::Camera::POST_RENDER);
480
481        // we don't want the camera to grab event focus from the viewers main camera(s).
482        camera->setAllowEventFocus(false);
483
484        osg::Geode* geode = new osg::Geode;
485
486        osg::Vec3 position(10.0f,size.y()-100.0f,0.0f);
487        osg::Vec3 delta(0.0f,-120.0f,0.0f);
488        float lenght = 300.0f;
489
490        // turn the text off to avoid linking with osgText
491#if 0
492        std::string timesFont("fonts/arial.ttf");
493
494        {
495            osgText::Text* text = new  osgText::Text;
496            geode->addDrawable( text );
497
498            text->setFont(timesFont);
499            text->setPosition(position);
500            text->setText("Shadow Map HUD");
501
502            position += delta;
503        }
504#endif
505       
506        osg::Vec3 widthVec(lenght, 0.0f, 0.0f);
507        osg::Vec3 depthVec(0.0f,lenght, 0.0f);
508        osg::Vec3 centerBase( 10.0f + lenght/2, size.y()-lenght/2, 0.0f);
509        centerBase += delta;
510       
511        geode->addDrawable( osg::createTexturedQuadGeometry( centerBase-widthVec*0.5f-depthVec*0.5f,
512                                                             widthVec, depthVec) );
513
514        osg::StateSet* stateset = geode->getOrCreateStateSet();
515
516        stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
517        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
518        //stateset->setAttribute(new osg::PolygonOffset(1.0f,1.0f),osg::StateAttribute::ON);
519        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
520
521        // test with regular texture
522        //stateset->setTextureAttributeAndModes(_baseTextureUnit, new osg::Texture2D(osgDB::readImageFile("Images/lz.rgb")));
523
524        stateset->setTextureAttributeAndModes(_shadowTextureUnit,_texture.get(),osg::StateAttribute::ON);
525       
526        //test to check the texture coordinates generated during shadow pass
527#if 0
528        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
529        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
530        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
531        stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
532
533        // create TexGen node
534        osg::ref_ptr<osg::TexGenNode> texGenNode = new osg::TexGenNode;
535        texGenNode->setTextureUnit(_shadowTextureUnit);
536        texGenNode->setTexGen(_texgen.get());
537        camera->addChild(texGenNode.get());
538#endif
539        //shader for correct display
540
541        osg::ref_ptr<osg::Program> program = new osg::Program;
542        stateset->setAttribute(program.get());
543       
544        osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_debugHUD);
545        program->addShader(fragment_shader);
546
547        camera->addChild(geode);
548
549        return camera;
550    }
551
552    //////////////////////// End Debug Section
Note: See TracBrowser for help on using the browser.