root/OpenSceneGraph/trunk/src/osgShadow/ParallelSplitShadowMap.cpp @ 13041

Revision 13041, 42.7 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 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/* ##################################################################################################### */
15/* ParallelSplitShadowMap written by Adrian Egli (3dhelp (at) gmail.com)                                 */
16/* ##################################################################################################### */
17/*                                                                                                       */
18/* the pssm main idea is based on:                                                                       */
19/*                                                                                                       */
20/* Parallel-Split Shadow Maps for Large-scale Virtual Environments                                       */
21/*    Fan Zhang     Hanqiu Sun    Leilei Xu    Lee Kit Lun                                               */
22/*    The Chinese University of Hong Kong                                                                */
23/*                                                                                                       */
24/* Refer to our latest project webpage for "Parallel-Split Shadow Maps on Programmable GPUs" in GPU Gems */
25/*                                                                                                       */
26/* ##################################################################################################### */
27
28#include <osgShadow/ParallelSplitShadowMap>
29
30#include <osgShadow/ShadowedScene>
31#include <osg/Notify>
32#include <osg/ComputeBoundsVisitor>
33#include <osg/PolygonOffset>
34#include <osg/CullFace>
35#include <osg/io_utils>
36#include <iostream>
37#include <sstream>
38#include <osg/Geode>
39#include <osg/Geometry>
40#include <osgDB/ReadFile>
41#include <osg/Texture1D>
42#include <osg/Depth>
43#include <osg/ShadeModel>
44
45using namespace osgShadow;
46
47// split scheme
48#define TEXTURE_RESOLUTION  1024
49
50
51
52
53#define ZNEAR_MIN_FROM_LIGHT_SOURCE 5.0
54#define MOVE_VIRTUAL_CAMERA_BEHIND_REAL_CAMERA_FACTOR 0.0
55
56//#define SHOW_SHADOW_TEXTURE_DEBUG    // DEPTH instead of color for debug information texture display in a rectangle
57//#define SHADOW_TEXTURE_DEBUG         // COLOR instead of DEPTH
58
59#ifndef SHADOW_TEXTURE_DEBUG
60#define SHADOW_TEXTURE_GLSL
61#endif
62
63//////////////////////////////////////////////////////////////////////////
64// FragmentShaderGenerator
65std::string ParallelSplitShadowMap::FragmentShaderGenerator::generateGLSL_FragmentShader_BaseTex(
66    bool debug,
67    unsigned int splitCount,
68    double textureRes,
69    bool filtered,
70    unsigned int nbrSplits,
71    unsigned int textureOffset
72    ) {
73        std::stringstream sstr;
74
75        /// base texture
76        sstr << "uniform sampler2D baseTexture; "      << std::endl;
77        sstr << "uniform float enableBaseTexture; "     << std::endl;
78        sstr << "uniform vec2 ambientBias;"    << std::endl;
79
80        for (unsigned int i=0;i<nbrSplits;i++)    {
81            sstr << "uniform sampler2DShadow shadowTexture"    <<    i    <<"; "    << std::endl;
82            sstr << "uniform float zShadow"                    <<    i    <<"; "    << std::endl;
83        }
84
85
86
87
88        sstr << "void main(void)"    << std::endl;
89        sstr << "{"    << std::endl;
90
91
92        /// select the shadow map : split
93        sstr << "float testZ = gl_FragCoord.z*2.0-1.0;" <<std::endl;
94        sstr << "float map0 = step(testZ, zShadow0);"<< std::endl;//DEBUG
95        for (unsigned int i=1;i<nbrSplits;i++)    {
96            sstr << "float map" << i << "  = step(zShadow"<<i-1<<",testZ)*step(testZ, zShadow"<<i<<");"<< std::endl;//DEBUG
97        }
98
99        if (filtered) {
100            sstr << "          float fTexelSize="<< (1.41 / textureRes ) <<";" << std::endl;
101            sstr << "          float fZOffSet  = -0.001954;" << std::endl; // 2^-9 good value for ATI / NVidia
102
103        }
104        for (unsigned int i=0;i<nbrSplits;i++)    {
105            if (!filtered) {
106                sstr << "    float shadow"    <<    i    <<" = shadow2D( shadowTexture"    <<    i    <<",gl_TexCoord["    <<    (i+textureOffset)    <<"].xyz).r;"   << std::endl;
107                sstr << " shadow"    <<    i    <<" = step(0.25,shadow"    <<    i    <<");" << std::endl; // reduce shadow artefacts
108            } else {
109
110
111                // filter the shadow (look up) 3x3
112                //
113                // 1 0 1
114                // 0 2 0
115                // 1 0 1
116                //
117                // / 6
118
119                sstr << "    float shadowOrg"    <<    i    <<" = shadow2D( shadowTexture"  <<    i    <<",gl_TexCoord["    <<    (i+textureOffset)    <<"].xyz+vec3(0.0,0.0,fZOffSet) ).r;"   << std::endl;
120                sstr << "    float shadow0"    <<    i    <<" = shadow2D( shadowTexture"    <<    i    <<",gl_TexCoord["    <<    (i+textureOffset)    <<"].xyz+vec3(-fTexelSize,-fTexelSize,fZOffSet) ).r;"   << std::endl;
121                sstr << "    float shadow1"    <<    i    <<" = shadow2D( shadowTexture"    <<    i    <<",gl_TexCoord["    <<    (i+textureOffset)    <<"].xyz+vec3( fTexelSize,-fTexelSize,fZOffSet) ).r;"   << std::endl;
122                sstr << "    float shadow2"    <<    i    <<" = shadow2D( shadowTexture"    <<    i    <<",gl_TexCoord["    <<    (i+textureOffset)    <<"].xyz+vec3( fTexelSize, fTexelSize,fZOffSet) ).r;"   << std::endl;
123                sstr << "    float shadow3"    <<    i    <<" = shadow2D( shadowTexture"    <<    i    <<",gl_TexCoord["    <<    (i+textureOffset)    <<"].xyz+vec3(-fTexelSize, fTexelSize,fZOffSet) ).r;"   << std::endl;
124
125                sstr << "    float shadow"    <<    i    <<" = ( 2.0*shadowOrg"    <<    i
126                    <<" + shadow0"    <<    i
127                    <<" + shadow1"    <<    i
128                    <<" + shadow2"    <<    i
129                    <<" + shadow3"    <<    i
130                    << ")/6.0;"<< std::endl;
131
132                //sstr << " shadow"    <<    i    <<" = shadow"    <<    i    <<" * step(0.025,shadow"    <<    i    <<");" << std::endl; // reduce shadow artefacts
133
134                //sstr << "    float shadow02"    <<    i    <<" = (shadow0"    <<    i    <<"+shadow2"    <<    i    <<")*0.5;"<< std::endl;
135                //sstr << "    float shadow13"    <<    i    <<" = (shadow1"    <<    i    <<"+shadow3"    <<    i    <<")*0.5;"<< std::endl;
136                //sstr << "    float shadowSoft"    <<    i    <<" = (shadow02"    <<    i    <<"+shadow13"    <<    i    <<")*0.5;"<< std::endl;
137                //sstr << "    float shadow"    <<    i    <<" = (shadowSoft"    <<    i    <<"+shadowOrg"    <<    i    <<")*0.5;"<< std::endl;
138                //sstr << " shadow"    <<    i    <<" = step(0.25,shadow"    <<    i    <<");" << std::endl; // reduce shadow artefacts
139            }
140        }
141
142
143        sstr << "    float term0 = (1.0-shadow0)*map0; "    << std::endl;
144        for (unsigned int i=1;i<nbrSplits;i++)    {
145            sstr << "    float term" << i << " = map"<< i << "*(1.0-shadow"<<i<<");"<< std::endl;
146        }
147
148
149
150        /// build shadow factor value v
151        sstr << "    float v = clamp(";
152        for (unsigned int i=0;i<nbrSplits;i++)    {
153            sstr << "term"    <<    i;
154            if ( i+1 < nbrSplits ){
155                sstr << "+";
156            }
157        }
158        sstr << ",0.0,1.0);"    << std::endl;
159
160
161
162
163        if ( debug ) {
164
165
166            sstr << "    float c0=0.0;" << std::endl;
167            sstr << "    float c1=0.0;" << std::endl;
168            sstr << "    float c2=0.0;" << std::endl;
169
170            sstr << "    float sumTerm=0.0;" << std::endl;
171
172            for (unsigned int i=0;i<nbrSplits;i++)    {
173                if ( i < 3 ) sstr << "    c" << i << "=term" << i << ";" << std::endl;
174                sstr << "    sumTerm=sumTerm+term" << i << ";" << std::endl;
175            }
176
177            sstr << "    vec4 color    = gl_Color*( 1.0 - sumTerm ) + (sumTerm)* gl_Color*vec4(c0,(1.0-c0)*c1,(1.0-c0)*(1.0-c1)*c2,1.0); "    << std::endl;
178
179
180            switch(nbrSplits){
181            case 1: sstr << "    color    =  color*0.75 + vec4(map0,0,0,1.0)*0.25; "    << std::endl;break;
182            case 2: sstr << "    color    =  color*0.75 + vec4(map0,map1,0,1.0)*0.25; "    << std::endl;break;
183            case 3: sstr << "    color    =  color*0.75 + vec4(map0,map1,map2,1.0)*0.25; "    << std::endl; break;
184            case 4: sstr << "    color    =  color*0.75 + vec4(map0+map3,map1+map3,map2,1.0)*0.25; "    << std::endl; break;
185            case 5: sstr << "    color    =  color*0.75 + vec4(map0+map3,map1+map3+map4,map2+map4,1.0)*0.25; "    << std::endl;break;
186            case 6: sstr << "    color    =  color*0.75 + vec4(map0+map3+map5,map1+map3+map4,map2+map4+map5,1.0)*0.25; "    << std::endl;    break;
187            default: break;
188            }
189
190
191        } else {
192            sstr << "    vec4 color    = gl_Color; "<< std::endl;
193        }
194
195
196
197
198
199
200
201        sstr << "    vec4 texcolor = texture2D(baseTexture,gl_TexCoord[0].st); "    << std::endl;
202
203
204        sstr << "    float enableBaseTextureFilter = enableBaseTexture*(1.0 - step(texcolor.x+texcolor.y+texcolor.z+texcolor.a,0.0)); "    << std::endl;                                                //18
205        sstr << "    vec4 colorTex = color*texcolor;" << std::endl;
206        sstr << "    gl_FragColor.rgb = (((color*(ambientBias.x+1.0)*(1.0-enableBaseTextureFilter)) + colorTex*(1.0+ambientBias.x)*enableBaseTextureFilter)*(1.0-ambientBias.y*v)).rgb; "<< std::endl;
207        sstr << "    gl_FragColor.a = (color*(1.0-enableBaseTextureFilter) + colorTex*enableBaseTextureFilter).a; "<< std::endl;
208
209
210
211        sstr << "}"<< std::endl;
212
213        //std::cout << sstr.str() << std::endl;
214        if ( splitCount == nbrSplits-1 ) { OSG_INFO << std::endl << "ParallelSplitShadowMap: GLSL shader code:" << std::endl << "-------------------------------------------------------------------"  << std::endl << sstr.str() << std::endl; }
215
216        return sstr.str();
217}
218
219//////////////////////////////////////////////////////////////////////////
220// clamp variables of any type
221template<class Type> inline Type Clamp(Type A, Type Min, Type Max) {
222    if(A<Min) return Min;
223    if(A>Max) return Max;
224    return A;
225}
226
227#define min(a,b)            (((a) < (b)) ? (a) : (b))
228#define max(a,b)            (((a) > (b)) ? (a) : (b))
229
230//////////////////////////////////////////////////////////////////////////
231ParallelSplitShadowMap::ParallelSplitShadowMap(osg::Geode** gr, int icountplanes) :
232    _textureUnitOffset(1),
233    _debug_color_in_GLSL(false),
234    _user_polgyonOffset_set(false),
235    _resolution(TEXTURE_RESOLUTION),
236    _setMaxFarDistance(1000.0),
237    _isSetMaxFarDistance(false),
238    _split_min_near_dist(ZNEAR_MIN_FROM_LIGHT_SOURCE),
239    _move_vcam_behind_rcam_factor(MOVE_VIRTUAL_CAMERA_BEHIND_REAL_CAMERA_FACTOR),
240    _userLight(NULL),
241    _GLSL_shadow_filtered(true),
242    _ambientBiasUniform(NULL),
243    _ambientBias(0.1f,0.3f)
244{
245    _displayTexturesGroupingNode = gr;
246    _number_of_splits = icountplanes;
247
248    _polgyonOffset.set(0.0f,0.0f);
249    setFragmentShaderGenerator(new FragmentShaderGenerator());
250    setSplitCalculationMode(SPLIT_EXP);
251}
252
253ParallelSplitShadowMap::ParallelSplitShadowMap(const ParallelSplitShadowMap& copy, const osg::CopyOp& copyop):
254    ShadowTechnique(copy,copyop),
255    _displayTexturesGroupingNode(0),
256    _textureUnitOffset(copy._textureUnitOffset),
257    _number_of_splits(copy._number_of_splits),
258    _debug_color_in_GLSL(copy._debug_color_in_GLSL),
259    _polgyonOffset(copy._polgyonOffset),
260    _user_polgyonOffset_set(copy._user_polgyonOffset_set),
261    _resolution(copy._resolution),
262    _setMaxFarDistance(copy._setMaxFarDistance),
263    _isSetMaxFarDistance(copy._isSetMaxFarDistance),
264    _split_min_near_dist(copy._split_min_near_dist),
265    _move_vcam_behind_rcam_factor(copy._move_vcam_behind_rcam_factor),
266    _userLight(copy._userLight),
267    _FragmentShaderGenerator(copy._FragmentShaderGenerator),
268    _GLSL_shadow_filtered(copy._GLSL_shadow_filtered),
269    _SplitCalcMode(copy._SplitCalcMode),
270    _ambientBiasUniform(NULL),
271    _ambientBias(copy._ambientBias)
272{
273}
274
275void ParallelSplitShadowMap::setAmbientBias(const osg::Vec2& ambientBias)
276{
277    _ambientBias = ambientBias;
278    if (_ambientBiasUniform ) _ambientBiasUniform->set(osg::Vec2f(_ambientBias.x(), _ambientBias.y()));
279}
280
281void ParallelSplitShadowMap::init()
282{
283    if (!_shadowedScene) return;
284
285    osg::ref_ptr<osg::StateSet> sharedStateSet = new osg::StateSet;
286    sharedStateSet->setDataVariance(osg::Object::DYNAMIC);
287
288    unsigned int iCamerasMax=_number_of_splits;
289    for (unsigned int iCameras=0;iCameras<iCamerasMax;iCameras++)
290    {
291        PSSMShadowSplitTexture pssmShadowSplitTexture;
292        pssmShadowSplitTexture._splitID = iCameras;
293        pssmShadowSplitTexture._textureUnit = iCameras+_textureUnitOffset;
294
295        pssmShadowSplitTexture._resolution = _resolution;
296
297        OSG_DEBUG << "ParallelSplitShadowMap : Texture ID=" << iCameras << " Resolution=" << pssmShadowSplitTexture._resolution << std::endl;
298        // set up the texture to render into
299        {
300            pssmShadowSplitTexture._texture = new osg::Texture2D;
301            pssmShadowSplitTexture._texture->setTextureSize(pssmShadowSplitTexture._resolution, pssmShadowSplitTexture._resolution);
302#ifndef SHADOW_TEXTURE_DEBUG
303            pssmShadowSplitTexture._texture->setInternalFormat(GL_DEPTH_COMPONENT);
304            pssmShadowSplitTexture._texture->setShadowComparison(true);
305            pssmShadowSplitTexture._texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
306#else
307            pssmShadowSplitTexture._texture->setInternalFormat(GL_RGBA);
308#endif
309            pssmShadowSplitTexture._texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
310            pssmShadowSplitTexture._texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
311            pssmShadowSplitTexture._texture->setBorderColor(osg::Vec4(1.0,1.0,1.0,1.0));
312            pssmShadowSplitTexture._texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_BORDER);
313            pssmShadowSplitTexture._texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_BORDER);
314        }
315        // set up the render to texture camera.
316        {
317            // create the camera
318            pssmShadowSplitTexture._camera = new osg::Camera;
319            pssmShadowSplitTexture._camera->setCullCallback(new CameraCullCallback(this));
320
321
322#ifndef SHADOW_TEXTURE_DEBUG
323            pssmShadowSplitTexture._camera->setClearMask(GL_DEPTH_BUFFER_BIT);
324            pssmShadowSplitTexture._camera->setClearColor(osg::Vec4(1.0,1.0,1.0,1.0));
325#else
326            pssmShadowSplitTexture._camera->setClearMask(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
327            switch(iCameras)
328            {
329            case 0:
330                pssmShadowSplitTexture._camera->setClearColor(osg::Vec4(1.0,0.0,0.0,1.0));
331                break;
332            case 1:
333                pssmShadowSplitTexture._camera->setClearColor(osg::Vec4(0.0,1.0,0.0,1.0));
334                break;
335            case 2:
336                pssmShadowSplitTexture._camera->setClearColor(osg::Vec4(0.0,0.0,1.0,1.0));
337                break;
338            default:
339                pssmShadowSplitTexture._camera->setClearColor(osg::Vec4(1.0,1.0,1.0,1.0));
340                break;
341            }
342#endif
343            pssmShadowSplitTexture._camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
344            pssmShadowSplitTexture._camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
345
346            // set viewport
347            pssmShadowSplitTexture._camera->setViewport(0,0,pssmShadowSplitTexture._resolution,pssmShadowSplitTexture._resolution);
348
349            // set the camera to render before the main camera.
350            pssmShadowSplitTexture._camera->setRenderOrder(osg::Camera::PRE_RENDER);
351
352            // tell the camera to use OpenGL frame buffer object where supported.
353            pssmShadowSplitTexture._camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
354
355            // attach the texture and use it as the color buffer.
356#ifndef SHADOW_TEXTURE_DEBUG
357            pssmShadowSplitTexture._camera->attach(osg::Camera::DEPTH_BUFFER, pssmShadowSplitTexture._texture.get());
358#else
359            pssmShadowSplitTexture._camera->attach(osg::Camera::COLOR_BUFFER, pssmShadowSplitTexture._texture.get());
360#endif
361            osg::StateSet* stateset = pssmShadowSplitTexture._camera->getOrCreateStateSet();
362
363
364
365            //////////////////////////////////////////////////////////////////////////
366            if ( _user_polgyonOffset_set ) {
367                float factor = _polgyonOffset.x();
368                float units  = _polgyonOffset.y();
369                osg::ref_ptr<osg::PolygonOffset> polygon_offset = new osg::PolygonOffset;
370                polygon_offset->setFactor(factor);
371                polygon_offset->setUnits(units);
372                stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
373                stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
374            }
375
376
377            //////////////////////////////////////////////////////////////////////////
378            if ( ! _GLSL_shadow_filtered ) {
379                // if not glsl filtering enabled then we should force front face culling to reduce the number of shadow artefacts.
380                osg::ref_ptr<osg::CullFace> cull_face = new osg::CullFace;
381                cull_face->setMode(osg::CullFace::FRONT);
382                stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
383                stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
384            }
385
386            //////////////////////////////////////////////////////////////////////////
387            osg::ShadeModel* shademodel = dynamic_cast<osg::ShadeModel*>(stateset->getAttribute(osg::StateAttribute::SHADEMODEL));
388            if (!shademodel){shademodel = new osg::ShadeModel;stateset->setAttribute(shademodel);}
389            shademodel->setMode( osg::ShadeModel::FLAT );
390            stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
391        }
392
393        //////////////////////////////////////////////////////////////////////////
394        // set up stateset and append texture, texGen ,...
395        {
396            pssmShadowSplitTexture._stateset = sharedStateSet.get();//new osg::StateSet;
397            pssmShadowSplitTexture._stateset->setTextureAttributeAndModes(pssmShadowSplitTexture._textureUnit,pssmShadowSplitTexture._texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
398            pssmShadowSplitTexture._stateset->setTextureMode(pssmShadowSplitTexture._textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
399            pssmShadowSplitTexture._stateset->setTextureMode(pssmShadowSplitTexture._textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
400            pssmShadowSplitTexture._stateset->setTextureMode(pssmShadowSplitTexture._textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
401            pssmShadowSplitTexture._stateset->setTextureMode(pssmShadowSplitTexture._textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
402
403
404            /// generate a TexGen object
405            pssmShadowSplitTexture._texgen = new osg::TexGen;
406
407        }
408
409        //////////////////////////////////////////////////////////////////////////
410        // set up shader (GLSL)
411#ifdef SHADOW_TEXTURE_GLSL
412
413        osg::Program* program = new osg::Program;
414        pssmShadowSplitTexture._stateset->setAttribute(program);
415
416        //////////////////////////////////////////////////////////////////////////
417        // GLSL PROGRAMS
418        osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT,
419            _FragmentShaderGenerator->generateGLSL_FragmentShader_BaseTex(
420            _debug_color_in_GLSL,
421            iCameras,
422            pssmShadowSplitTexture._resolution,
423            _GLSL_shadow_filtered,
424            _number_of_splits,
425            _textureUnitOffset
426            ).c_str());
427        program->addShader(fragment_shader);
428
429
430        //////////////////////////////////////////////////////////////////////////
431        // UNIFORMS
432        std::stringstream strST; strST << "shadowTexture" << (pssmShadowSplitTexture._textureUnit-_textureUnitOffset);
433        osg::Uniform* shadowTextureSampler = new osg::Uniform(strST.str().c_str(),(int)(pssmShadowSplitTexture._textureUnit));
434        pssmShadowSplitTexture._stateset->addUniform(shadowTextureSampler);
435
436        //TODO: NOT YET SUPPORTED in the current version of the shader
437        if ( ! _ambientBiasUniform ) {
438            _ambientBiasUniform = new osg::Uniform("ambientBias",_ambientBias);
439            pssmShadowSplitTexture._stateset->addUniform(_ambientBiasUniform);
440        }
441
442
443        std::stringstream strzShadow; strzShadow << "zShadow" << (pssmShadowSplitTexture._textureUnit-_textureUnitOffset);
444        pssmShadowSplitTexture._farDistanceSplit = new osg::Uniform(strzShadow.str().c_str(),1.0f);
445        pssmShadowSplitTexture._stateset->addUniform(pssmShadowSplitTexture._farDistanceSplit);
446
447        osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
448        pssmShadowSplitTexture._stateset->addUniform(baseTextureSampler);
449
450        osg::Uniform* randomTextureSampler = new osg::Uniform("randomTexture",(int)(_textureUnitOffset+_number_of_splits));
451        pssmShadowSplitTexture._stateset->addUniform(randomTextureSampler);
452
453        if ( _textureUnitOffset > 0 ) {
454            osg::Uniform* enableBaseTexture = new osg::Uniform("enableBaseTexture",1.0f);
455            pssmShadowSplitTexture._stateset->addUniform(enableBaseTexture);
456        } else {
457            osg::Uniform* enableBaseTexture = new osg::Uniform("enableBaseTexture",0.0f);
458            pssmShadowSplitTexture._stateset->addUniform(enableBaseTexture);
459        }
460
461        for (unsigned int textLoop(0);textLoop<_textureUnitOffset;textLoop++)
462        {
463            // fake texture for baseTexture, add a fake texture
464            // we support by default at least one texture layer
465            // without this fake texture we can not support
466            // textured and not textured scene
467
468            // TODO: at the moment the PSSM supports just one texture layer in the GLSL shader, multitexture are
469            //       not yet supported !
470
471            osg::Image* image = new osg::Image;
472            // allocate the image data, noPixels x 1 x 1 with 4 rgba floats - equivalent to a Vec4!
473            int noPixels = 1;
474            image->allocateImage(noPixels,1,1,GL_RGBA,GL_FLOAT);
475            image->setInternalTextureFormat(GL_RGBA);
476            // fill in the image data.
477            osg::Vec4* dataPtr = (osg::Vec4*)image->data();
478            osg::Vec4f color(1.0f,1.0f,1.0f,0.0f);
479            *dataPtr = color;
480            // make fake texture
481            osg::Texture2D* texture = new osg::Texture2D;
482            texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
483            texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
484            texture->setBorderColor(osg::Vec4(1.0,1.0,1.0,1.0));
485            texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
486            texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
487            texture->setImage(image);
488            // add fake texture
489            pssmShadowSplitTexture._stateset->setTextureAttribute(textLoop,texture,osg::StateAttribute::ON);
490            pssmShadowSplitTexture._stateset->setTextureMode(textLoop,GL_TEXTURE_1D,osg::StateAttribute::OFF);
491            pssmShadowSplitTexture._stateset->setTextureMode(textLoop,GL_TEXTURE_2D,osg::StateAttribute::ON);
492            pssmShadowSplitTexture._stateset->setTextureMode(textLoop,GL_TEXTURE_3D,osg::StateAttribute::OFF);
493        }
494#endif
495
496
497        //////////////////////////////////////////////////////////////////////////
498        // DEBUG
499        if ( _displayTexturesGroupingNode ) {
500            {
501                pssmShadowSplitTexture._debug_textureUnit = 1;
502                pssmShadowSplitTexture._debug_texture = new osg::Texture2D;
503                pssmShadowSplitTexture._debug_texture->setTextureSize(TEXTURE_RESOLUTION, TEXTURE_RESOLUTION);
504#ifdef SHOW_SHADOW_TEXTURE_DEBUG
505                pssmShadowSplitTexture._debug_texture->setInternalFormat(GL_DEPTH_COMPONENT);
506                pssmShadowSplitTexture._debug_texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
507#else
508                pssmShadowSplitTexture._debug_texture->setInternalFormat(GL_RGBA);
509#endif
510                pssmShadowSplitTexture._debug_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
511                pssmShadowSplitTexture._debug_texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
512                // create the camera
513                pssmShadowSplitTexture._debug_camera = new osg::Camera;
514                pssmShadowSplitTexture._debug_camera->setCullCallback(new CameraCullCallback(this));
515                pssmShadowSplitTexture._debug_camera->setClearMask(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
516                pssmShadowSplitTexture._debug_camera->setClearColor(osg::Vec4(1.0,1.0,1.0,1.0));
517                pssmShadowSplitTexture._debug_camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
518
519                // set viewport
520                pssmShadowSplitTexture._debug_camera->setViewport(0,0,TEXTURE_RESOLUTION,TEXTURE_RESOLUTION);
521                // set the camera to render before the main camera.
522                pssmShadowSplitTexture._debug_camera->setRenderOrder(osg::Camera::PRE_RENDER);
523                // tell the camera to use OpenGL frame buffer object where supported.
524                pssmShadowSplitTexture._debug_camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
525                // attach the texture and use it as the color buffer.
526#ifdef SHOW_SHADOW_TEXTURE_DEBUG
527                pssmShadowSplitTexture._debug_camera->attach(osg::Camera::DEPTH_BUFFER, pssmShadowSplitTexture._debug_texture.get());
528#else
529                pssmShadowSplitTexture._debug_camera->attach(osg::Camera::COLOR_BUFFER, pssmShadowSplitTexture._debug_texture.get());
530#endif
531                // osg::StateSet* stateset = pssmShadowSplitTexture._debug_camera->getOrCreateStateSet();
532
533                pssmShadowSplitTexture._debug_stateset = new osg::StateSet;
534                pssmShadowSplitTexture._debug_stateset->setTextureAttributeAndModes(pssmShadowSplitTexture._debug_textureUnit,pssmShadowSplitTexture._debug_texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
535                pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
536                pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
537                pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
538                pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
539            }
540
541            osg::Geode* geode = _displayTexturesGroupingNode[iCameras];
542            geode->getOrCreateStateSet()->setTextureAttributeAndModes(0,pssmShadowSplitTexture._debug_texture.get(),osg::StateAttribute::ON);
543
544        }
545        //////////////////////////////////////////////////////////////////////////
546
547        _PSSMShadowSplitTextureMap.insert(PSSMShadowSplitTextureMap::value_type(iCameras,pssmShadowSplitTexture));
548
549
550    }
551
552
553    _dirty = false;
554}
555
556void ParallelSplitShadowMap::update(osg::NodeVisitor& nv){
557    getShadowedScene()->osg::Group::traverse(nv);
558}
559
560void ParallelSplitShadowMap::cull(osgUtil::CullVisitor& cv){
561    // record the traversal mask on entry so we can reapply it later.
562    unsigned int traversalMask = cv.getTraversalMask();
563    osgUtil::RenderStage* orig_rs = cv.getRenderStage();
564
565#ifdef SHADOW_TEXTURE_GLSL
566    PSSMShadowSplitTextureMap::iterator it=_PSSMShadowSplitTextureMap.begin();
567#else
568    // do traversal of shadow receiving scene which does need to be decorated by the shadow map
569    for (PSSMShadowSplitTextureMap::iterator it=_PSSMShadowSplitTextureMap.begin();it!=_PSSMShadowSplitTextureMap.end();it++)
570#endif
571    {
572        PSSMShadowSplitTexture pssmShadowSplitTexture = it->second;
573        cv.pushStateSet(pssmShadowSplitTexture._stateset.get());
574
575        //////////////////////////////////////////////////////////////////////////
576        // DEBUG
577        if ( _displayTexturesGroupingNode ) {
578            cv.pushStateSet(pssmShadowSplitTexture._debug_stateset.get());
579        }
580        //////////////////////////////////////////////////////////////////////////
581
582        _shadowedScene->osg::Group::traverse(cv);
583
584        cv.popStateSet();
585
586    }
587
588    // need to compute view frustum for RTT camera.
589    // get the bounds of the model.
590    osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
591    cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask());
592    _shadowedScene->osg::Group::traverse(cbbv);
593
594    //////////////////////////////////////////////////////////////////////////
595    const osg::Light* selectLight = 0;
596
597    /// light pos and light direction
598    osg::Vec4 lightpos;
599    osg::Vec3 lightDirection;
600
601    if ( ! _userLight ) {
602        // try to find a light in the scene
603        osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
604        for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
605            itr != aml.end();
606            ++itr)
607        {
608            const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
609            if (light)
610            {
611                osg::RefMatrix* matrix = itr->second.get();
612                if (matrix) lightpos = light->getPosition() * (*matrix);
613                else lightpos = light->getPosition();
614                if (matrix) lightDirection = light->getDirection() * (*matrix);
615                else lightDirection = light->getDirection();
616
617                selectLight = light;
618            }
619        }
620
621        osg::Matrix eyeToWorld;
622        eyeToWorld.invert(*cv.getModelViewMatrix());
623
624        lightpos = lightpos * eyeToWorld;
625        lightDirection = lightDirection * eyeToWorld;
626    }else{
627        // take the user light as light source
628        lightpos = _userLight->getPosition();
629        lightDirection = _userLight->getDirection();
630        selectLight = _userLight.get();
631    }
632
633    if (selectLight)
634    {
635
636        // do traversal of shadow receiving scene which does need to be decorated by the shadow map
637        //unsigned int iMaxSplit = _PSSMShadowSplitTextureMap.size();
638
639        for (PSSMShadowSplitTextureMap::iterator it=_PSSMShadowSplitTextureMap.begin();it!=_PSSMShadowSplitTextureMap.end();it++)
640        {
641            PSSMShadowSplitTexture pssmShadowSplitTexture = it->second;
642
643
644            //////////////////////////////////////////////////////////////////////////
645            // SETUP pssmShadowSplitTexture for rendering
646            //
647            lightDirection.normalize();
648            pssmShadowSplitTexture._lightDirection = lightDirection;
649            pssmShadowSplitTexture._cameraView    = cv.getRenderInfo().getView()->getCamera()->getViewMatrix();
650            pssmShadowSplitTexture._cameraProj    = cv.getRenderInfo().getView()->getCamera()->getProjectionMatrix();
651
652            //////////////////////////////////////////////////////////////////////////
653            // CALCULATE
654
655
656
657            // Calculate corner points of frustum split
658            //
659            // To avoid edge problems, scale the frustum so
660            // that it's at least a few pixels larger
661            //
662            osg::Vec3d pCorners[8];
663            calculateFrustumCorners(pssmShadowSplitTexture,pCorners);
664
665            // Init Light (Directional Light)
666            //
667            calculateLightInitialPosition(pssmShadowSplitTexture,pCorners);
668
669            // Calculate near and far for light view
670            //
671            calculateLightNearFarFormFrustum(pssmShadowSplitTexture,pCorners);
672
673            // Calculate view and projection matrices
674            //
675            calculateLightViewProjectionFormFrustum(pssmShadowSplitTexture,pCorners);
676
677            //////////////////////////////////////////////////////////////////////////
678            // set up shadow rendering camera
679            pssmShadowSplitTexture._camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
680
681            //////////////////////////////////////////////////////////////////////////
682            // DEBUG
683            if ( _displayTexturesGroupingNode ) {
684                pssmShadowSplitTexture._debug_camera->setViewMatrix(pssmShadowSplitTexture._camera->getViewMatrix());
685                pssmShadowSplitTexture._debug_camera->setProjectionMatrix(pssmShadowSplitTexture._camera->getProjectionMatrix());
686                pssmShadowSplitTexture._debug_camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
687            }
688
689            //////////////////////////////////////////////////////////////////////////
690            // compute the matrix which takes a vertex from local coords into tex coords
691            // will use this later to specify osg::TexGen..
692
693            osg::Matrix MVPT = pssmShadowSplitTexture._camera->getViewMatrix() *
694                pssmShadowSplitTexture._camera->getProjectionMatrix() *
695                osg::Matrix::translate(1.0,1.0,1.0) *
696                osg::Matrix::scale(0.5,0.5,0.5);
697
698            pssmShadowSplitTexture._texgen->setMode(osg::TexGen::EYE_LINEAR);
699            pssmShadowSplitTexture._texgen->setPlanesFromMatrix(MVPT);
700            //////////////////////////////////////////////////////////////////////////
701
702
703            //////////////////////////////////////////////////////////////////////////
704            cv.setTraversalMask( traversalMask & getShadowedScene()->getCastsShadowTraversalMask() );
705
706            // do RTT camera traversal
707            pssmShadowSplitTexture._camera->accept(cv);
708
709            //////////////////////////////////////////////////////////////////////////
710            // DEBUG
711            if ( _displayTexturesGroupingNode ) {
712                pssmShadowSplitTexture._debug_camera->accept(cv);
713            }
714
715
716            orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(pssmShadowSplitTexture._textureUnit, cv.getModelViewMatrix(), pssmShadowSplitTexture._texgen.get());
717
718
719        }
720    } // if light
721
722
723
724    // reapply the original traversal mask
725    cv.setTraversalMask( traversalMask );
726}
727
728void ParallelSplitShadowMap::cleanSceneGraph(){
729
730}
731
732
733//////////////////////////////////////////////////////////////////////////
734// Computes corner points of a frustum
735//
736//
737//unit box representing frustum in clip space
738const osg::Vec3d const_pointFarTR(   1.0,  1.0,  1.0);
739const osg::Vec3d const_pointFarBR(   1.0, -1.0,  1.0);
740const osg::Vec3d const_pointFarTL(  -1.0,  1.0,  1.0);
741const osg::Vec3d const_pointFarBL(  -1.0, -1.0,  1.0);
742const osg::Vec3d const_pointNearTR(  1.0,  1.0, -1.0);
743const osg::Vec3d const_pointNearBR(  1.0, -1.0, -1.0);
744const osg::Vec3d const_pointNearTL( -1.0,  1.0, -1.0);
745const osg::Vec3d const_pointNearBL( -1.0, -1.0, -1.0);
746//////////////////////////////////////////////////////////////////////////
747
748
749void ParallelSplitShadowMap::calculateFrustumCorners(PSSMShadowSplitTexture &pssmShadowSplitTexture, osg::Vec3d *frustumCorners)
750{
751    // get user cameras
752    double fovy,aspectRatio,camNear,camFar;
753    pssmShadowSplitTexture._cameraProj.getPerspective(fovy,aspectRatio,camNear,camFar);
754
755
756    // force to max far distance to show shadow, for some scene it can be solve performance problems.
757    if ((_isSetMaxFarDistance) && (_setMaxFarDistance < camFar))
758        camFar = _setMaxFarDistance;
759
760
761    // build camera matrix with some offsets (the user view camera)
762    osg::Matrixd viewMat;
763    osg::Vec3d camEye,camCenter,camUp;
764    pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp);
765    osg::Vec3d viewDir = camCenter - camEye;
766    //viewDir.normalize(); //we can assume that viewDir is still normalized in the viewMatrix
767    camEye = camEye  - viewDir * _move_vcam_behind_rcam_factor;
768    camFar += _move_vcam_behind_rcam_factor * viewDir.length();
769    viewMat.makeLookAt(camEye,camCenter,camUp);
770
771
772
773    //////////////////////////////////////////////////////////////////////////
774    /// CALCULATE SPLIT
775    double maxFar = camFar;
776    // double minNear = camNear;
777    double camNearFar_Dist = maxFar - camNear;
778    if ( _SplitCalcMode == SPLIT_LINEAR )
779    {
780        camFar  = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID+1))/((double)(_number_of_splits));
781        camNear = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID))/((double)(_number_of_splits));
782    }
783    else
784    {
785        // Exponential split scheme:
786        //
787        // Ci = (n - f)*(i/numsplits)^(bias+1) + n;
788        //
789        static double fSplitSchemeBias[2]={0.25f,0.66f};
790        fSplitSchemeBias[1]=Clamp(fSplitSchemeBias[1],0.0,3.0);
791        double* pSplitDistances =new double[_number_of_splits+1];
792
793        for(int i=0;i<(int)_number_of_splits;i++)
794        {
795            double fIDM=(double)(i)/(double)(_number_of_splits);
796            pSplitDistances[i]=camNearFar_Dist*(pow(fIDM,fSplitSchemeBias[1]+1))+camNear;
797        }
798        // make sure border values are right
799        pSplitDistances[0]=camNear;
800        pSplitDistances[_number_of_splits]=camFar;
801
802        camNear = pSplitDistances[pssmShadowSplitTexture._splitID];
803        camFar  = pSplitDistances[pssmShadowSplitTexture._splitID+1];
804
805        delete[] pSplitDistances;
806    }
807
808
809    pssmShadowSplitTexture._split_far = camFar;
810
811
812
813    //////////////////////////////////////////////////////////////////////////
814    /// TRANSFORM frustum corners (Optimized for Orthogonal)
815
816
817    osg::Matrixd projMat;
818    projMat.makePerspective(fovy,aspectRatio,camNear,camFar);
819    osg::Matrixd projViewMat(viewMat*projMat);
820    osg::Matrixd invProjViewMat;
821    invProjViewMat.invert(projViewMat);
822
823    //transform frustum vertices to world space
824    frustumCorners[0] = const_pointFarBR * invProjViewMat;
825    frustumCorners[1] = const_pointNearBR* invProjViewMat;
826    frustumCorners[2] = const_pointNearTR* invProjViewMat;
827    frustumCorners[3] = const_pointFarTR * invProjViewMat;
828    frustumCorners[4] = const_pointFarTL * invProjViewMat;
829    frustumCorners[5] = const_pointFarBL * invProjViewMat;
830    frustumCorners[6] = const_pointNearBL* invProjViewMat;
831    frustumCorners[7] = const_pointNearTL* invProjViewMat;
832
833    //std::cout << "camFar : "<<pssmShadowSplitTexture._splitID << " / " << camNear << "," << camFar << std::endl;
834}
835
836//////////////////////////////////////////////////////////////////////////
837//
838// compute directional light initial position;
839void ParallelSplitShadowMap::calculateLightInitialPosition(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners)
840{
841    pssmShadowSplitTexture._frustumSplitCenter = frustumCorners[0];
842    for(int i=1;i<8;i++)
843    {
844        pssmShadowSplitTexture._frustumSplitCenter +=frustumCorners[i];
845    }
846    //    pssmShadowSplitTexture._frustumSplitCenter /= 8.0;
847    pssmShadowSplitTexture._frustumSplitCenter *= 0.125;
848}
849
850void ParallelSplitShadowMap::calculateLightNearFarFormFrustum(
851    PSSMShadowSplitTexture &pssmShadowSplitTexture,
852    osg::Vec3d *frustumCorners
853    ) {
854
855        //calculate near, far
856        double zFar(-DBL_MAX);
857
858        // calculate zFar (as longest distance)
859        for(int i=0;i<8;i++) {
860            double dist_z_from_light = fabs(pssmShadowSplitTexture._lightDirection*(frustumCorners[i] -  pssmShadowSplitTexture._frustumSplitCenter));
861            if ( zFar  < dist_z_from_light ) zFar  = dist_z_from_light;
862        }
863
864        // update camera position and look at center
865        pssmShadowSplitTexture._lightCameraSource = pssmShadowSplitTexture._frustumSplitCenter - pssmShadowSplitTexture._lightDirection*(zFar+_split_min_near_dist);
866        pssmShadowSplitTexture._lightCameraTarget = pssmShadowSplitTexture._frustumSplitCenter + pssmShadowSplitTexture._lightDirection*(zFar);
867
868        // calculate [zNear,zFar]
869        zFar = (-DBL_MAX);
870        double zNear(DBL_MAX);
871        for(int i=0;i<8;i++) {
872            double dist_z_from_light = fabs(pssmShadowSplitTexture._lightDirection*(frustumCorners[i] -  pssmShadowSplitTexture._lightCameraSource));
873            if ( zFar  < dist_z_from_light ) zFar  = dist_z_from_light;
874            if ( zNear > dist_z_from_light ) zNear  = dist_z_from_light;
875        }
876        // update near - far plane
877        pssmShadowSplitTexture._lightNear = max(zNear - _split_min_near_dist - 0.01,0.01);
878        pssmShadowSplitTexture._lightFar  = zFar;
879}
880
881
882
883
884void ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners)
885{
886
887    // calculate the camera's coordinate system
888    osg::Vec3d camEye,camCenter,camUp;
889    pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp);
890    osg::Vec3d viewDir(camCenter-camEye);
891    osg::Vec3d camRight(viewDir^camUp);
892
893    // we force to have normalized vectors (camera's view)
894    camUp.normalize();
895    viewDir.normalize();
896    camRight.normalize();
897
898    // use quaternion -> numerical more robust
899    osg::Quat qRot;
900    qRot.makeRotate(viewDir,pssmShadowSplitTexture._lightDirection);
901    osg::Vec3d top =  qRot * camUp;
902    osg::Vec3d right = qRot * camRight;
903
904    // calculate the camera's frustum right,right,bottom,top parameters
905    double maxRight(-DBL_MAX),maxTop(-DBL_MAX);
906    double minRight(DBL_MAX),minTop(DBL_MAX);
907
908    for(int i(0); i < 8; i++)
909    {
910
911        osg::Vec3d diffCorner(frustumCorners[i] - pssmShadowSplitTexture._frustumSplitCenter);
912        double lright(diffCorner*right);
913        double lTop(diffCorner*top);
914
915        if ( lright > maxRight ) maxRight  =  lright;
916        if ( lTop  > maxTop  ) maxTop   =  lTop;
917
918        if ( lright < minRight ) minRight  =  lright;
919        if ( lTop  < minTop  ) minTop   =  lTop;
920    }
921
922    // make the camera view matrix
923    pssmShadowSplitTexture._camera->setViewMatrixAsLookAt(pssmShadowSplitTexture._lightCameraSource,pssmShadowSplitTexture._lightCameraTarget,top);
924
925    // use ortho projection for light (directional light only supported)
926    pssmShadowSplitTexture._camera->setProjectionMatrixAsOrtho(minRight,maxRight,minTop,maxTop,pssmShadowSplitTexture._lightNear,pssmShadowSplitTexture._lightFar);
927
928
929#ifdef SHADOW_TEXTURE_GLSL
930    // get user cameras
931    osg::Vec3d vProjCamFraValue = (camEye + viewDir * pssmShadowSplitTexture._split_far) * (pssmShadowSplitTexture._cameraView * pssmShadowSplitTexture._cameraProj);
932    pssmShadowSplitTexture._farDistanceSplit->set((float)vProjCamFraValue.z());
933#endif
934
935
936}
937
938
Note: See TracBrowser for help on using the browser.