-
-
Notifications
You must be signed in to change notification settings - Fork 981
/
OgreShadowRenderer.cpp
1925 lines (1676 loc) · 73.1 KB
/
OgreShadowRenderer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org
Copyright (c) 2000-2014 Torus Knot Software Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include <memory>
#include "OgreStableHeaders.h"
#include "OgreHardwarePixelBuffer.h"
#include "OgreRenderTexture.h"
#include "OgreViewport.h"
#include "OgreRectangle2D.h"
#include "OgreShadowCameraSetup.h"
#include "OgreShadowVolumeExtrudeProgram.h"
#include "OgreHighLevelGpuProgram.h"
namespace Ogre {
GpuProgramParametersSharedPtr SceneManager::ShadowRenderer::msInfiniteExtrusionParams;
GpuProgramParametersSharedPtr SceneManager::ShadowRenderer::msFiniteExtrusionParams;
typedef std::vector<ShadowCaster*> ShadowCasterList;
/// Inner class to use as callback for shadow caster scene query
class ShadowCasterSceneQueryListener : public SceneQueryListener
{
protected:
SceneManager* mSceneMgr;
ShadowCasterList* mCasterList;
bool mIsLightInFrustum;
const PlaneBoundedVolumeList* mLightClipVolumeList;
const Camera* mCamera;
const Light* mLight;
Real mFarDistSquared;
public:
ShadowCasterSceneQueryListener(SceneManager* sm) : mSceneMgr(sm),
mCasterList(0), mIsLightInFrustum(false), mLightClipVolumeList(0),
mCamera(0), mFarDistSquared(0) {}
// Prepare the listener for use with a set of parameters
void prepare(bool lightInFrustum, const PlaneBoundedVolumeList* lightClipVolumes, const Light* light,
const Camera* cam, ShadowCasterList* casterList, Real farDistSquared)
{
mCasterList = casterList;
mIsLightInFrustum = lightInFrustum;
mLightClipVolumeList = lightClipVolumes;
mCamera = cam;
mLight = light;
mFarDistSquared = farDistSquared;
}
bool queryResult(MovableObject* object) override;
};
SceneManager::ShadowRenderer::ShadowRenderer(SceneManager* owner) :
mSceneManager(owner),
mShadowTechnique(SHADOWTYPE_NONE),
mShadowColour(ColourValue(0.25, 0.25, 0.25)),
mShadowCasterPlainBlackPass(0),
mShadowReceiverPass(0),
mShadowModulativePass(0),
mShadowDebugPass(0),
mShadowStencilPass(0),
mShadowIndexBufferSize(51200),
mShadowIndexBufferUsedSize(0),
mShadowTextureCustomCasterPass(0),
mShadowTextureCustomReceiverPass(0),
mFullScreenQuad(0),
mShadowAdditiveLightClip(false),
mDebugShadows(false),
mShadowMaterialInitDone(false),
mShadowUseInfiniteFarPlane(true),
mShadowDirLightExtrudeDist(10000),
mDefaultShadowFarDist(0),
mDefaultShadowFarDistSquared(0),
mShadowTextureOffset(0.6),
mShadowTextureFadeStart(0.7),
mShadowTextureFadeEnd(0.9),
mShadowTextureSelfShadow(false),
mShadowTextureConfigDirty(true),
mShadowCasterRenderBackFaces(true)
{
mShadowCasterQueryListener = std::make_unique<ShadowCasterSceneQueryListener>(mSceneManager);
// set up default shadow camera setup
mDefaultShadowCameraSetup = DefaultShadowCameraSetup::create();
mCullCameraSetup = DefaultShadowCameraSetup::create();
// init shadow texture count per type.
mShadowTextureCountPerType[Light::LT_POINT] = 1;
mShadowTextureCountPerType[Light::LT_DIRECTIONAL] = 1;
mShadowTextureCountPerType[Light::LT_SPOTLIGHT] = 1;
}
SceneManager::ShadowRenderer::~ShadowRenderer() {}
void SceneManager::ShadowRenderer::setShadowColour(const ColourValue& colour)
{
mShadowColour = colour;
}
void SceneManager::ShadowRenderer::updateSplitOptions(RenderQueue* queue)
{
int shadowTechnique = mShadowTechnique;
if(!mSceneManager->getCurrentViewport()->getShadowsEnabled())
shadowTechnique = SHADOWTYPE_NONE;
bool notIntegrated = (shadowTechnique & SHADOWDETAILTYPE_INTEGRATED) == 0;
// Stencil Casters can always be receivers
queue->setShadowCastersCannotBeReceivers(!(shadowTechnique & SHADOWDETAILTYPE_STENCIL) &&
!mShadowTextureSelfShadow);
// Additive lighting, we need to split everything by illumination stage
queue->setSplitPassesByLightingType((shadowTechnique & SHADOWDETAILTYPE_ADDITIVE) && notIntegrated);
// Tell render queue to split off non-shadowable materials
queue->setSplitNoShadowPasses(shadowTechnique && notIntegrated);
}
void SceneManager::ShadowRenderer::render(RenderQueueGroup* group,
QueuedRenderableCollection::OrganisationMode om)
{
if(mShadowTechnique & SHADOWDETAILTYPE_STENCIL)
{
if(mShadowTechnique & SHADOWDETAILTYPE_ADDITIVE)
{
// Additive stencil shadows in use
renderAdditiveStencilShadowedQueueGroupObjects(group, om);
return;
}
// Modulative stencil shadows in use
renderModulativeStencilShadowedQueueGroupObjects(group, om);
return;
}
// Receiver pass(es)
if (mShadowTechnique & SHADOWDETAILTYPE_ADDITIVE)
{
// Auto-additive
renderAdditiveTextureShadowedQueueGroupObjects(group, om);
return;
}
// Modulative
renderModulativeTextureShadowedQueueGroupObjects(group, om);
}
size_t SceneManager::ShadowRenderer::getShadowTexIndex(size_t startLightIndex)
{
size_t shadowTexIndex = mShadowTextures.size();
if (mShadowTextureIndexLightList.size() > startLightIndex)
shadowTexIndex = mShadowTextureIndexLightList[startLightIndex];
return shadowTexIndex;
}
//-----------------------------------------------------------------------
void SceneManager::ShadowRenderer::renderAdditiveStencilShadowedQueueGroupObjects(
RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
LightList lightList(1);
auto visitor = mSceneManager->getQueuedRenderableVisitor();
for (const auto& pg : pGroup->getPriorityGroups())
{
RenderPriorityGroup* pPriorityGrp = pg.second;
// Sort the queue first
pPriorityGrp->sort(mSceneManager->mCameraInProgress);
// Render all the ambient passes first, no light iteration, no lights
visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false);
// Also render any objects which have receive shadows disabled
visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true, true);
// Now iterate per light
// Iterate over lights, render all volumes to stencil
for (Light* l : mSceneManager->_getLightsAffectingFrustum())
{
// Set light state
lightList[0] = l;
// set up scissor, will cover shadow vol and regular light rendering
ClipResult scissored = mSceneManager->buildAndSetScissor(lightList, mSceneManager->mCameraInProgress);
ClipResult clipped = CLIPPED_NONE;
if (mShadowAdditiveLightClip)
clipped = mSceneManager->buildAndSetLightClip(lightList);
// skip light if scissored / clipped entirely
if (scissored == CLIPPED_ALL || clipped == CLIPPED_ALL)
continue;
if (l->getCastShadows())
{
// Clear stencil
mDestRenderSystem->clearFrameBuffer(FBT_STENCIL);
renderShadowVolumesToStencil(l, mSceneManager->mCameraInProgress, false);
// NB we render where the stencil is equal to zero to render lit areas
StencilState stencilState;
stencilState.enabled = true;
stencilState.compareOp = CMPF_EQUAL;
mDestRenderSystem->setStencilState(stencilState);
}
// render lighting passes for this light
visitor->renderObjects(pPriorityGrp->getSolidsDiffuseSpecular(), om, false, false, &lightList);
// Reset stencil params
mDestRenderSystem->setStencilState(StencilState());
if (scissored == CLIPPED_SOME)
mSceneManager->resetScissor();
if (clipped == CLIPPED_SOME)
mSceneManager->resetLightClip();
}// for each light
// Now render decal passes, no need to set lights as lighting will be disabled
visitor->renderObjects(pPriorityGrp->getSolidsDecal(), om, false, false);
}// for each priority
for (const auto& pg : pGroup->getPriorityGroups())
{
visitor->renderTransparents(pg.second, om);
}
}
//-----------------------------------------------------------------------
void SceneManager::ShadowRenderer::renderModulativeStencilShadowedQueueGroupObjects(
RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
/* For each light, we need to render all the solids from each group,
then do the modulative shadows, then render the transparents from
each group.
Now, this means we are going to reorder things more, but that it required
if the shadows are to look correct. The overall order is preserved anyway,
it's just that all the transparents are at the end instead of them being
interleaved as in the normal rendering loop.
*/
// Iterate through priorities
auto visitor = mSceneManager->getQueuedRenderableVisitor();
for (const auto& pg : pGroup->getPriorityGroups())
{
RenderPriorityGroup* pPriorityGrp = pg.second;
// Sort the queue first
pPriorityGrp->sort(mSceneManager->mCameraInProgress);
// Do (shadowable) solids
visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, true, true);
}
// Override auto param ambient to force vertex programs to use shadow colour
ColourValue currAmbient = mSceneManager->getAmbientLight();
mSceneManager->setAmbientLight(mShadowColour);
// Iterate over lights, render all volumes to stencil
for (Light* l : mSceneManager->_getLightsAffectingFrustum())
{
if (l->getCastShadows())
{
// Clear stencil
mDestRenderSystem->clearFrameBuffer(FBT_STENCIL);
renderShadowVolumesToStencil(l, mSceneManager->mCameraInProgress, true);
// render full-screen shadow modulator for all lights
mSceneManager->_setPass(mShadowModulativePass);
// NB we render where the stencil is not equal to zero to render shadows, not lit areas
StencilState stencilState;
stencilState.enabled = true;
stencilState.compareOp = CMPF_NOT_EQUAL;
mDestRenderSystem->setStencilState(stencilState);
mSceneManager->renderSingleObject(mFullScreenQuad, mShadowModulativePass, false, false);
// Reset stencil params
mDestRenderSystem->setStencilState(StencilState());
}
}// for each light
// Restore ambient light
mSceneManager->setAmbientLight(currAmbient);
// Do non-shadowable solids
for (const auto& pg : pGroup->getPriorityGroups())
{
visitor->renderObjects(pg.second->getSolidsNoShadowReceive(), om, true, true);
}
for (const auto& pg : pGroup->getPriorityGroups())
{
visitor->renderTransparents(pg.second, om);
}
}
//-----------------------------------------------------------------------
void SceneManager::ShadowRenderer::renderTextureShadowCasterQueueGroupObjects(
RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
// This is like the basic group render, except we skip all transparents
// and we also render any non-shadowed objects
// Note that non-shadow casters will have already been eliminated during
// _findVisibleObjects
// Iterate through priorities
// Override auto param ambient to force vertex programs and fixed function to
ColourValue currAmbient = mSceneManager->getAmbientLight();
if (mShadowTechnique & SHADOWDETAILTYPE_ADDITIVE)
{
// Use simple black / white mask if additive
mSceneManager->setAmbientLight(ColourValue::Black);
}
else
{
// Use shadow colour as caster colour if modulative
mSceneManager->setAmbientLight(mShadowColour);
}
auto visitor = mSceneManager->getQueuedRenderableVisitor();
for (const auto& pg : pGroup->getPriorityGroups())
{
RenderPriorityGroup* pPriorityGrp = pg.second;
// Sort the queue first
pPriorityGrp->sort(mSceneManager->mCameraInProgress);
// Do solids, override light list incase any vertex programs use them
visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false);
visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, false, false);
// Do unsorted transparents that cast shadows
visitor->renderObjects(pPriorityGrp->getTransparentsUnsorted(), om, false, false, nullptr, true);
// Do transparents that cast shadows
visitor->renderObjects(pPriorityGrp->getTransparents(), QueuedRenderableCollection::OM_SORT_DESCENDING, false,
false, nullptr, true);
}// for each priority
// reset ambient light
mSceneManager->setAmbientLight(currAmbient);
}
//-----------------------------------------------------------------------
void SceneManager::ShadowRenderer::renderModulativeTextureShadowedQueueGroupObjects(
RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
/* For each light, we need to render all the solids from each group,
then do the modulative shadows, then render the transparents from
each group.
Now, this means we are going to reorder things more, but that it required
if the shadows are to look correct. The overall order is preserved anyway,
it's just that all the transparents are at the end instead of them being
interleaved as in the normal rendering loop.
*/
// Iterate through priorities
auto visitor = mSceneManager->getQueuedRenderableVisitor();
for (const auto& pg : pGroup->getPriorityGroups())
{
RenderPriorityGroup* pPriorityGrp = pg.second;
// Sort the queue first
pPriorityGrp->sort(mSceneManager->mCameraInProgress);
// Do solids
visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, true, true);
visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true, true);
}
// Iterate over lights, render received shadows
// only perform this if we're in the 'normal' render stage, to avoid
// doing it during the render to texture
if (mSceneManager->mIlluminationStage == IRS_NONE)
{
mSceneManager->mIlluminationStage = IRS_RENDER_RECEIVER_PASS;
LightList::const_iterator i, iend;
iend = mSceneManager->_getLightsAffectingFrustum().end();
size_t si = 0;
for (i = mSceneManager->_getLightsAffectingFrustum().begin();
i != iend && si < mShadowTextures.size(); ++i)
{
Light* l = *i;
if (!l->getCastShadows())
continue;
// Get camera for current shadow texture
Camera *cam = mShadowTextures[si]->getBuffer()->getRenderTarget()->getViewport(0)->getCamera();
// Hook up receiver texture
Pass* targetPass = mShadowTextureCustomReceiverPass ?
mShadowTextureCustomReceiverPass : mShadowReceiverPass;
// if this light is a spotlight, we need to add the spot fader layer
// BUT not if using a custom projection matrix, since then it will be
// inappropriately shaped most likely
if (l->getType() == Light::LT_SPOTLIGHT && !cam->isCustomProjectionMatrixEnabled())
{
// remove all TUs except 0 & 1
// (only an issue if additive shadows have been used)
while(targetPass->getNumTextureUnitStates() > 2)
targetPass->removeTextureUnitState(2);
TextureUnitState* t = NULL;
// Add spot fader if not present already
if (targetPass->getNumTextureUnitStates() == 2 &&
targetPass->getTextureUnitState(1)->_getTexturePtr() == mSpotFadeTexture)
{
// Just set
t = targetPass->getTextureUnitState(1);
}
else
{
// Remove any non-conforming spot layers
while(targetPass->getNumTextureUnitStates() > 1)
targetPass->removeTextureUnitState(1);
t = targetPass->createTextureUnitState();
t->setTexture(mSpotFadeTexture);
t->setColourOperation(LBO_ADD);
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
}
t->setProjectiveTexturing(!targetPass->hasVertexProgram(), cam);
mSceneManager->mAutoParamDataSource->setTextureProjector(cam, 1);
}
else
{
// remove all TUs except 0 including spot
while(targetPass->getNumTextureUnitStates() > 1)
targetPass->removeTextureUnitState(1);
}
// account for the RTSS
if (auto betterTechnique = targetPass->getParent()->getParent()->getBestTechnique())
{
targetPass = betterTechnique->getPass(0);
}
TextureUnitState* texUnit = targetPass->getTextureUnitState(0);
// clamp to border colour in case this is a custom material
texUnit->setSampler(mBorderSampler);
resolveShadowTexture(texUnit, si, 0);
// Set lighting / blending modes
targetPass->setSceneBlending(SBF_DEST_COLOUR, SBF_ZERO);
targetPass->setLightingEnabled(false);
targetPass->_load();
// Fire pre-receiver event
fireShadowTexturesPreReceiver(l, cam);
renderTextureShadowReceiverQueueGroupObjects(pGroup, om);
++si;
} // for each light
mSceneManager->mIlluminationStage = IRS_NONE;
}
for (const auto& pg : pGroup->getPriorityGroups())
{
visitor->renderTransparents(pg.second, om);
}
}
//-----------------------------------------------------------------------
void SceneManager::ShadowRenderer::renderAdditiveTextureShadowedQueueGroupObjects(
RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
LightList lightList(1);
auto visitor = mSceneManager->getQueuedRenderableVisitor();
for (const auto& pg : pGroup->getPriorityGroups())
{
RenderPriorityGroup* pPriorityGrp = pg.second;
// Sort the queue first
pPriorityGrp->sort(mSceneManager->mCameraInProgress);
// Render all the ambient passes first, no light iteration, no lights
visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false);
// Also render any objects which have receive shadows disabled
visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true, true);
// only perform this next part if we're in the 'normal' render stage, to avoid
// doing it during the render to texture
if (mSceneManager->mIlluminationStage == IRS_NONE)
{
// Iterate over lights, render masked
size_t si = 0;
for (Light* l : mSceneManager->_getLightsAffectingFrustum())
{
if (l->getCastShadows() && si < mShadowTextures.size())
{
// Hook up receiver texture
Pass* targetPass = mShadowTextureCustomReceiverPass ?
mShadowTextureCustomReceiverPass : mShadowReceiverPass;
// account for the RTSS
if (auto betterTechnique = targetPass->getParent()->getParent()->getBestTechnique())
{
targetPass = betterTechnique->getPass(0);
}
TextureUnitState* texUnit = targetPass->getTextureUnitState(0);
// clamp to border colour in case this is a custom material
texUnit->setSampler(mBorderSampler);
resolveShadowTexture(texUnit, si, 0);
// Remove any spot fader layer
if (targetPass->getNumTextureUnitStates() > 1 &&
targetPass->getTextureUnitState(1)->getTextureName() == "spot_shadow_fade.dds")
{
// remove spot fader layer (should only be there if
// we previously used modulative shadows)
targetPass->removeTextureUnitState(1);
}
// Set lighting / blending modes
targetPass->setSceneBlending(SBF_ONE, SBF_ONE);
targetPass->setLightingEnabled(true);
targetPass->_load();
// increment shadow texture since used
++si;
mSceneManager->mIlluminationStage = IRS_RENDER_RECEIVER_PASS;
}
else
{
mSceneManager->mIlluminationStage = IRS_NONE;
}
// render lighting passes for this light
lightList[0] = l;
// set up light scissoring, always useful in additive modes
ClipResult scissored = mSceneManager->buildAndSetScissor(lightList, mSceneManager->mCameraInProgress);
ClipResult clipped = CLIPPED_NONE;
if(mShadowAdditiveLightClip)
clipped = mSceneManager->buildAndSetLightClip(lightList);
// skip if entirely clipped
if(scissored == CLIPPED_ALL || clipped == CLIPPED_ALL)
continue;
visitor->renderObjects(pPriorityGrp->getSolidsDiffuseSpecular(), om, false, false, &lightList);
if (scissored == CLIPPED_SOME)
mSceneManager->resetScissor();
if (clipped == CLIPPED_SOME)
mSceneManager->resetLightClip();
}// for each light
mSceneManager->mIlluminationStage = IRS_NONE;
// Now render decal passes, no need to set lights as lighting will be disabled
visitor->renderObjects(pPriorityGrp->getSolidsDecal(), om, false, false);
}
}// for each priority
for (const auto& pg : pGroup->getPriorityGroups())
{
visitor->renderTransparents(pg.second, om);
}
}
//-----------------------------------------------------------------------
void SceneManager::ShadowRenderer::renderTextureShadowReceiverQueueGroupObjects(
RenderQueueGroup* pGroup,
QueuedRenderableCollection::OrganisationMode om)
{
// Iterate through priorities
// Override auto param ambient to force vertex programs to go full-bright
ColourValue currAmbient = mSceneManager->getAmbientLight();
mSceneManager->setAmbientLight(ColourValue::White);
auto visitor = mSceneManager->getQueuedRenderableVisitor();
for (const auto& pg : pGroup->getPriorityGroups())
{
RenderPriorityGroup* pPriorityGrp = pg.second;
// Do solids, override light list incase any vertex programs use them
visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false);
// Don't render transparents or passes which have shadow receipt disabled
}// for each priority
// reset ambient
mSceneManager->setAmbientLight(currAmbient);
}
//---------------------------------------------------------------------
void SceneManager::ShadowRenderer::ensureShadowTexturesCreated()
{
if(!mBorderSampler)
{
mBorderSampler = TextureManager::getSingleton().createSampler();
mBorderSampler->setAddressingMode(TAM_BORDER);
mBorderSampler->setBorderColour(ColourValue::White);
mBorderSampler->setFiltering(FT_MIP, FO_NONE); // we do not have mips. GLES2 is particularly picky here.
}
if (mShadowTextureConfigDirty)
{
destroyShadowTextures();
ShadowTextureManager::getSingleton().getShadowTextures(mShadowTextureConfigList, mShadowTextures);
// clear shadow cam - light mapping
mShadowCamLightMapping.clear();
//Used to get the depth buffer ID setting for each RTT
size_t __i = 0;
// Recreate shadow textures
for (auto& shadowTex : mShadowTextures)
{
// Camera names are local to SM
String camName = shadowTex->getName() + "Cam";
RenderTexture *shadowRTT = shadowTex->getBuffer()->getRenderTarget();
//Set appropriate depth buffer
if(!PixelUtil::isDepth(shadowRTT->suggestPixelFormat()))
shadowRTT->setDepthBufferPool( mShadowTextureConfigList[__i].depthBufferPoolId );
// Create camera for this texture, but note that we have to rebind
// in prepareShadowTextures to coexist with multiple SMs
Camera* cam = mSceneManager->createCamera(camName);
cam->setAspectRatio((Real)shadowTex->getWidth() / (Real)shadowTex->getHeight());
auto camNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
camNode->attachObject(cam);
mShadowTextureCameras.push_back(cam);
// use separate culling camera, in case a focused shadow setup is used
// in which case we want to keep the original light frustum for culling
Camera* cullCam = mSceneManager->createCamera(camName+"/Cull");
cullCam->setAspectRatio((Real)shadowTex->getWidth() / (Real)shadowTex->getHeight());
cam->setCullingFrustum(cullCam);
camNode->attachObject(cullCam);
// Create a viewport, if not there already
if (shadowRTT->getNumViewports() == 0)
{
// Note camera assignment is transient when multiple SMs
Viewport *v = shadowRTT->addViewport(cam);
v->setClearEveryFrame(true);
// remove overlays
v->setOverlaysEnabled(false);
}
// Don't update automatically - we'll do it when required
shadowRTT->setAutoUpdated(false);
// insert dummy camera-light combination
mShadowCamLightMapping[cam] = 0;
// Get null shadow texture
if (mShadowTextureConfigList.empty())
{
mNullShadowTexture.reset();
}
else
{
mNullShadowTexture = ShadowTextureManager::getSingleton().getNullShadowTexture(
mShadowTextureConfigList[0].format);
}
++__i;
}
mShadowTextureConfigDirty = false;
}
}
//---------------------------------------------------------------------
void SceneManager::ShadowRenderer::destroyShadowTextures(void)
{
for (auto cam : mShadowTextureCameras)
{
mSceneManager->getRootSceneNode()->removeAndDestroyChild(cam->getParentSceneNode());
// Always destroy camera since they are local to this SM
if(auto cullcam = dynamic_cast<Camera*>(cam->getCullingFrustum()))
mSceneManager->destroyCamera(cullcam);
mSceneManager->destroyCamera(cam);
}
mShadowTextures.clear();
mShadowTextureCameras.clear();
// set by render*TextureShadowedQueueGroupObjects
mSceneManager->mAutoParamDataSource->setTextureProjector(NULL, 0);
// Will destroy if no other scene managers referencing
ShadowTextureManager::getSingleton().clearUnused();
mShadowTextureConfigDirty = true;
}
//---------------------------------------------------------------------
void SceneManager::ShadowRenderer::prepareShadowTextures(Camera* cam, Viewport* vp, const LightList* lightList)
{
// create shadow textures if needed
ensureShadowTexturesCreated();
// Determine far shadow distance
Real shadowDist = mDefaultShadowFarDist;
if (!shadowDist)
{
// need a shadow distance, make one up
shadowDist = cam->getNearClipDistance() * 300;
}
Real shadowOffset = shadowDist * mShadowTextureOffset;
// Precalculate fading info
Real shadowEnd = shadowDist + shadowOffset;
Real fadeStart = shadowEnd * mShadowTextureFadeStart;
Real fadeEnd = shadowEnd * mShadowTextureFadeEnd;
// Additive lighting should not use fogging, since it will overbrighten; use border clamp
if ((mShadowTechnique & SHADOWDETAILTYPE_ADDITIVE) == 0)
{
// set fogging to hide the shadow edge
mShadowReceiverPass->setFog(true, FOG_LINEAR, ColourValue::White, 0, fadeStart, fadeEnd);
}
else
{
// disable fogging explicitly
mShadowReceiverPass->setFog(true, FOG_NONE);
}
// Iterate over the lights we've found, max out at the limit of light textures
// Note that the light sorting must now place shadow casting lights at the
// start of the light list, therefore we do not need to deal with potential
// mismatches in the light<->shadow texture list any more
LightList::const_iterator i, iend;
ShadowTextureList::iterator si, siend;
CameraList::iterator ci;
iend = lightList->end();
siend = mShadowTextures.end();
ci = mShadowTextureCameras.begin();
mShadowTextureIndexLightList.clear();
size_t shadowTextureIndex = 0;
for (i = lightList->begin(), si = mShadowTextures.begin(); i != iend && si != siend; ++i)
{
Light* light = *i;
// skip light if shadows are disabled
if (!light->getCastShadows())
continue;
mDestRenderSystem->_setDepthClamp(light->getType() == Light::LT_DIRECTIONAL);
// texture iteration per light.
size_t textureCountPerLight = mShadowTextureCountPerType[light->getType()];
for (size_t j = 0; j < textureCountPerLight && si != siend; ++j)
{
TexturePtr &shadowTex = *si;
RenderTarget *shadowRTT = shadowTex->getBuffer()->getRenderTarget();
Viewport *shadowView = shadowRTT->getViewport(0);
Camera *texCam = *ci;
// rebind camera, incase another SM in use which has switched to its cam
shadowView->setCamera(texCam);
// Associate main view camera as LOD camera
texCam->setLodCamera(cam);
// set base
if (light->getType() != Light::LT_POINT)
{
#ifdef OGRE_NODELESS_POSITIONING
texCam->getParentSceneNode()->setDirection(light->getDerivedDirection(), Node::TS_WORLD);
#else
texCam->getParentSceneNode()->setOrientation(light->getParentNode()->_getDerivedOrientation());
#endif
}
if (light->getType() != Light::LT_DIRECTIONAL)
texCam->getParentSceneNode()->setPosition(light->getDerivedPosition());
// also update culling camera
auto cullCam = dynamic_cast<Camera*>(texCam->getCullingFrustum());
cullCam->_notifyViewport(shadowView);
mCullCameraSetup->getShadowCamera(mSceneManager, cam, vp, light, cullCam, j);
// Use the material scheme of the main viewport
// This is required to pick up the correct shadow_caster_material and similar properties.
shadowView->setMaterialScheme(vp->getMaterialScheme());
// Set the viewport visibility flags
shadowView->setVisibilityMask(light->getLightMask() & vp->getVisibilityMask());
// update shadow cam - light mapping
ShadowCamLightMapping::iterator camLightIt = mShadowCamLightMapping.find( texCam );
assert(camLightIt != mShadowCamLightMapping.end());
camLightIt->second = light;
if (!light->getCustomShadowCameraSetup())
mDefaultShadowCameraSetup->getShadowCamera(mSceneManager, cam, vp, light, texCam, j);
else
light->getCustomShadowCameraSetup()->getShadowCamera(mSceneManager, cam, vp, light, texCam, j);
// Setup background colour
shadowView->setBackgroundColour(ColourValue::White);
// Fire shadow caster update, callee can alter camera settings
fireShadowTexturesPreCaster(light, texCam, j);
// Update target
shadowRTT->update();
++si; // next shadow texture
++ci; // next camera
}
mDestRenderSystem->_setDepthClamp(false);
// set the first shadow texture index for this light.
mShadowTextureIndexLightList.push_back(shadowTextureIndex);
shadowTextureIndex += textureCountPerLight;
}
fireShadowTexturesUpdated(std::min(lightList->size(), mShadowTextures.size()));
ShadowTextureManager::getSingleton().clearUnused();
}
//---------------------------------------------------------------------
void SceneManager::ShadowRenderer::renderShadowVolumesToStencil(const Light* light,
const Camera* camera, bool calcScissor)
{
// Get the shadow caster list
const ShadowCasterList& casters = findShadowCastersForLight(light, camera);
// Check there are some shadow casters to render
if (casters.empty())
{
// No casters, just do nothing
return;
}
// Add light to internal list for use in render call
LightList lightList;
// const_cast is forgiveable here since we pass this const
lightList.push_back(const_cast<Light*>(light));
// Set up scissor test (point & spot lights only)
ClipResult scissored = CLIPPED_NONE;
if (calcScissor)
{
scissored = mSceneManager->buildAndSetScissor(lightList, camera);
if (scissored == CLIPPED_ALL)
return; // nothing to do
}
mDestRenderSystem->unbindGpuProgram(GPT_FRAGMENT_PROGRAM);
// Can we do a 2-sided stencil?
bool stencil2sided = false;
if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_TWO_SIDED_STENCIL) &&
mDestRenderSystem->getCapabilities()->hasCapability(RSC_STENCIL_WRAP))
{
// enable
stencil2sided = true;
}
// Do we have access to vertex programs?
bool extrudeInSoftware = true;
bool finiteExtrude = !mShadowUseInfiniteFarPlane;
if (const auto& fprog = mShadowStencilPass->getFragmentProgram())
{
extrudeInSoftware = false;
// attach the appropriate extrusion vertex program
// Note we never unset it because support for vertex programs is constant
mShadowStencilPass->setGpuProgram(GPT_VERTEX_PROGRAM,
ShadowVolumeExtrudeProgram::get(light->getType(), finiteExtrude), false);
// Set params
if (finiteExtrude)
{
mShadowStencilPass->setVertexProgramParameters(msFiniteExtrusionParams);
}
else
{
mShadowStencilPass->setVertexProgramParameters(msInfiniteExtrusionParams);
}
if (mDebugShadows)
{
mShadowDebugPass->setGpuProgram(GPT_VERTEX_PROGRAM,
ShadowVolumeExtrudeProgram::get(light->getType(), finiteExtrude), false);
// Set params
if (finiteExtrude)
{
mShadowDebugPass->setVertexProgramParameters(msFiniteExtrusionParams);
}
else
{
mShadowDebugPass->setVertexProgramParameters(msInfiniteExtrusionParams);
}
}
mSceneManager->bindGpuProgram(mShadowStencilPass->getVertexProgram()->_getBindingDelegate());
mSceneManager->bindGpuProgram(fprog->_getBindingDelegate());
}
else
{
mDestRenderSystem->unbindGpuProgram(GPT_VERTEX_PROGRAM);
}
if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_GEOMETRY_PROGRAM))
{
mDestRenderSystem->unbindGpuProgram(GPT_GEOMETRY_PROGRAM);
}
mDestRenderSystem->_setAlphaRejectSettings(mShadowStencilPass->getAlphaRejectFunction(),
mShadowStencilPass->getAlphaRejectValue(), mShadowStencilPass->isAlphaToCoverageEnabled());
// Turn off colour writing and depth writing
ColourBlendState disabled;
disabled.writeR = disabled.writeG = disabled.writeB = disabled.writeA = false;
mDestRenderSystem->setColourBlendState(disabled);
mDestRenderSystem->_disableTextureUnitsFrom(0);
mDestRenderSystem->_setDepthBufferParams(true, false, CMPF_LESS);
// Figure out the near clip volume
const PlaneBoundedVolume& nearClipVol =
light->_getNearClipVolume(camera);
// Now iterate over the casters and render
for (auto *caster : casters)
{
bool zfailAlgo = camera->isCustomNearClipPlaneEnabled();
unsigned long flags = 0;
// Calculate extrusion distance
Real extrudeDist = mShadowDirLightExtrudeDist;
if (light->getType() != Light::LT_DIRECTIONAL)
{
// we have to limit shadow extrusion to avoid cliping by far clip plane
extrudeDist = std::min(caster->getPointExtrusionDistance(light), mShadowDirLightExtrudeDist);
// Set autoparams for finite point light extrusion
mSceneManager->mAutoParamDataSource->setShadowPointLightExtrusionDistance(extrudeDist);
}
Real darkCapExtrudeDist = extrudeDist;
if (!extrudeInSoftware && !finiteExtrude)
{
// hardware extrusion, to infinity (and beyond!)
flags |= SRF_EXTRUDE_TO_INFINITY;
darkCapExtrudeDist = mShadowDirLightExtrudeDist;
}
// Determine whether zfail is required
if (zfailAlgo || nearClipVol.intersects(caster->getWorldBoundingBox()))
{
// We use zfail for this object only because zfail
// compatible with zpass algorithm
zfailAlgo = true;
// We need to include the light and / or dark cap
// But only if they will be visible
if(camera->isVisible(caster->getLightCapBounds()))
{
flags |= SRF_INCLUDE_LIGHT_CAP;
}
// zfail needs dark cap
// UNLESS directional lights using hardware extrusion to infinity
// since that extrudes to a single point
if(!((flags & SRF_EXTRUDE_TO_INFINITY) &&
light->getType() == Light::LT_DIRECTIONAL) &&
camera->isVisible(caster->getDarkCapBounds(*light, darkCapExtrudeDist)))
{