1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "nel/3d/shadow_map_manager.h"
22 #include "nel/misc/aabbox.h"
23 #include "nel/3d/driver.h"
24 #include "nel/3d/scene.h"
25 #include "nel/3d/viewport.h"
26 #include "nel/3d/scissor.h"
27 #include "nel/3d/dru.h"
28 #include "nel/3d/texture_mem.h"
29 #include "nel/3d/visual_collision_manager.h"
30 #include "nel/misc/hierarchical_timer.h"
31 #include "nel/misc/fast_floor.h"
34 using namespace NLMISC
;
44 // ***************************************************************************
46 static inline float easeInEaseOut(float x
)
49 // cubic such that f(0)=0, f'(0)=0, f(1)=1, f'(1)=0.
57 // ***************************************************************************
58 CShadowMapManager::CShadowMapManager()
62 // For Texture profiling
63 _TextureCategory
= new ITexture::CTextureCategory("SHADOW MANAGER");
65 setQuadGridSize(NL3D_SMM_QUADGRID_SIZE
, NL3D_SMM_QUADCELL_SIZE
);
66 _ShadowCasters
.reserve(256);
67 _GenerateShadowCasters
.reserve(256);
71 _FillQuads
.setVertexFormat(CVertexBuffer::PositionFlag
);
72 _FillMaterial
.initUnlit();
73 _FillMaterial
.setColor(CRGBA(0,0,0,0));
74 _FillMaterial
.setZWrite(false);
75 _FillMaterial
.setZFunc(CMaterial::always
);
76 _FillMaterial
.setDoubleSided(true);
77 _FillQuads
.setPreferredMemory(CVertexBuffer::RAMVolatile
, true);
80 _BlurQuads
.setVertexFormat(CVertexBuffer::PositionFlag
|
81 CVertexBuffer::TexCoord0Flag
|
82 CVertexBuffer::TexCoord1Flag
|
83 CVertexBuffer::TexCoord2Flag
|
84 CVertexBuffer::TexCoord3Flag
);
85 _BlurQuads
.setPreferredMemory(CVertexBuffer::RAMVolatile
, true);
87 // Only 2 quads are used to blur
88 _BlurQuads
.setNumVertices(8);
91 _BlurMaterial
[i
].initUnlit();
92 _BlurMaterial
[i
].setColor(CRGBA::White
);
93 _BlurMaterial
[i
].setZWrite(false);
94 _BlurMaterial
[i
].setZFunc(CMaterial::always
);
95 _BlurMaterial
[i
].setDoubleSided(true);
96 // Setup The Blur. NB: it will take advantage of Max 4 texture driver support, but will still
97 // work with 2 or 3 (less beautifull).
101 _BlurMaterial
[i
].texEnvOpRGB(j
, CMaterial::InterpolateConstant
);
102 _BlurMaterial
[i
].texEnvArg0RGB(j
, CMaterial::Texture
, CMaterial::SrcColor
);
103 _BlurMaterial
[i
].texEnvArg1RGB(j
, CMaterial::Previous
, CMaterial::SrcColor
);
104 _BlurMaterial
[i
].texEnvOpAlpha(j
, CMaterial::InterpolateConstant
);
105 _BlurMaterial
[i
].texEnvArg0Alpha(j
, CMaterial::Texture
, CMaterial::SrcAlpha
);
106 _BlurMaterial
[i
].texEnvArg1Alpha(j
, CMaterial::Previous
, CMaterial::SrcAlpha
);
108 // Factor for Stage so the sum is 1.
109 _BlurMaterial
[i
].texConstantColor(1, CRGBA(128,128,128,128)); // factor= 1/2
110 _BlurMaterial
[i
].texConstantColor(2, CRGBA(85,85,85,85)); // factor= 1/3
111 _BlurMaterial
[i
].texConstantColor(3, CRGBA(64,64,64,64)); // factor= 1/4
118 _CopyQuads
.setVertexFormat (CVertexBuffer::PositionFlag
| CVertexBuffer::TexCoord0Flag
);
119 _CopyQuads
.setNumVertices(4);
120 _CopyQuads
.setPreferredMemory(CVertexBuffer::RAMVolatile
, true);
121 CVertexBufferReadWrite vba
;
122 _CopyQuads
.lock (vba
);
123 vba
.setVertexCoord (0, CVector (0, 0, 0));
124 vba
.setVertexCoord (1, CVector (1, 0, 0));
125 vba
.setVertexCoord (2, CVector (1, 0, 1));
126 vba
.setVertexCoord (3, CVector (0, 0, 1));
129 _CopyMaterial
.initUnlit();
130 _CopyMaterial
.setColor(CRGBA::White
);
131 _CopyMaterial
.setZWrite(false);
132 _CopyMaterial
.setZFunc(CMaterial::always
);
133 _CopyMaterial
.setDoubleSided(true);
134 _CopyMaterial
.setBlend (false);
135 _CopyMaterial
.setAlphaTest (false);
136 _CopyMaterial
.setBlendFunc (CMaterial::one
, CMaterial::zero
);
138 // **** Setup Receiving
140 // Setup the clamp texture.
141 const uint clampTextSize
= 512;
142 const uint clampNearFadeSize
= 32;
143 const uint clampFarFadeSize
= 128;
144 uint textMemSize
= 4*clampTextSize
*1;
146 uint8
*tmpMem
= new uint8
[textMemSize
];
147 memset(tmpMem
, 255, textMemSize
);
148 for(i
=0;i
<clampNearFadeSize
;++i
)
150 float f
= (float)i
/clampNearFadeSize
;
152 tmpMem
[4*i
+3]= uint8(255.f
*f
);
154 for(i
=0;i
<clampFarFadeSize
;++i
)
156 float f
= (float)i
/clampFarFadeSize
;
158 tmpMem
[4*(clampTextSize
-i
-1)+3]= uint8(255.f
*f
);
161 _ClampTexture
= new CTextureMem (tmpMem
, 4*clampTextSize
*1, true, false, clampTextSize
, 1);
162 _ClampTexture
->setWrapS (ITexture::Clamp
);
163 _ClampTexture
->setWrapT (ITexture::Clamp
);
164 _ClampTexture
->setFilterMode (ITexture::Linear
, ITexture::LinearMipMapOff
);
165 _ClampTexture
->generate();
166 _ClampTexture
->setReleasable (false);
167 // For Texture Profiling
168 _ClampTexture
->setTextureCategory(_TextureCategory
);
171 _ReceiveShadowMaterial
.initUnlit();
172 _ReceiveShadowMaterial
.setBlend(true);
173 _ReceiveShadowMaterial
.setBlendFunc(CMaterial::zero
, CMaterial::invsrccolor
);
174 _ReceiveShadowMaterial
.setZWrite(false);
176 _ReceiveShadowMaterial
.setAlphaTest(true);
177 _ReceiveShadowMaterial
.setAlphaTestThreshold(0.01f
);
179 // ---- Stage 0. Project the ShadowMap. Blend the color between ShadowColor and White.
180 // setup texture coord gen
181 _ReceiveShadowMaterial
.enableUserTexMat(0, true);
182 _ReceiveShadowMaterial
.setTexCoordGen(0, true);
183 _ReceiveShadowMaterial
.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace
);
184 // Setup the stage so we interpolate ShadowColor and White (according to shadowmap alpha)
185 // nico : with D3D driver, limitation of the number of per stage constant (Only 1 if diffuse is used), so do a blend between inv diffuse & black (instead of diffuse & white), which resolve to a modulate between
186 // source alpha & inverse diffuse. then invert result at subsequent stage
187 _ReceiveShadowMaterial
.texEnvOpRGB(0, CMaterial::Modulate
);
188 _ReceiveShadowMaterial
.texEnvArg0RGB(0, CMaterial::Diffuse
, CMaterial::InvSrcColor
);
189 _ReceiveShadowMaterial
.texEnvArg1RGB(0, CMaterial::Texture
, CMaterial::SrcAlpha
);
190 // Take Alpha for AlphaTest only.
191 _ReceiveShadowMaterial
.texEnvOpAlpha(0, CMaterial::Replace
);
192 _ReceiveShadowMaterial
.texEnvArg0Alpha(0, CMaterial::Texture
, CMaterial::SrcAlpha
);
194 // ---- Stage 1. "Modulate" by Clamp Texture. Blend the color between stage0 color and White.
195 // setup texture coord gen
196 _ReceiveShadowMaterial
.enableUserTexMat(1, true);
197 _ReceiveShadowMaterial
.setTexCoordGen(1, true);
198 _ReceiveShadowMaterial
.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace
);
199 _ReceiveShadowMaterial
.setTexture(1, _ClampTexture
);
200 // Setup the stage so we interpolate Shadow and White (according to clamp alpha)
201 _ReceiveShadowMaterial
.texEnvOpRGB(1, CMaterial::Modulate
);
202 _ReceiveShadowMaterial
.texEnvArg0RGB(1, CMaterial::Previous
, CMaterial::SrcColor
); // Color is inverted before the blend
203 _ReceiveShadowMaterial
.texEnvArg1RGB(1, CMaterial::Texture
, CMaterial::SrcAlpha
);
204 // Take Alpha for AlphaTest only. (take 1st texture alpha...)
205 _ReceiveShadowMaterial
.texEnvOpAlpha(0, CMaterial::Replace
);
206 _ReceiveShadowMaterial
.texEnvArg0Alpha(0, CMaterial::Previous
, CMaterial::SrcAlpha
);
208 // **** Setup Casting
209 _CasterShadowMaterial
.initUnlit();
210 _CasterShadowMaterial
.setColor(CRGBA::White
);
211 _CasterShadowMaterial
.setZWrite(false);
212 _CasterShadowMaterial
.setZFunc(CMaterial::always
);
213 _CasterShadowMaterial
.setDoubleSided(true);
214 // Alpha Polygon coverage accumulate, for polygon smoothing
215 _CasterShadowMaterial
.setBlend(true);
216 _CasterShadowMaterial
.setBlendFunc(CMaterial::one
, CMaterial::one
);
218 _BlurQuads
.setName("CShadowMapManager::_BlurQuads");
219 _FillQuads
.setName("CShadowMapManager::_FillQuads");
220 _CopyQuads
.setName("CShadowMapManager::_CopyQuads");
223 // ***************************************************************************
224 CShadowMapManager::~CShadowMapManager()
226 clearAllShadowCasters();
229 // ***************************************************************************
230 void CShadowMapManager::setQuadGridSize(uint size
, float cellSize
)
232 _ShadowReceiverGrid
.create(size
, cellSize
);
235 // ***************************************************************************
236 void CShadowMapManager::addShadowCaster(CTransform
*model
)
238 _ShadowCasters
.push_back(model
);
241 // ***************************************************************************
242 void CShadowMapManager::addShadowReceiver(CTransform
*model
)
245 model
->getReceiverBBox(bb
);
247 _ShadowReceiverGrid
.insert(bb
.getMin(), bb
.getMax(), model
);
250 // ***************************************************************************
251 void CShadowMapManager::renderGenerate(CScene
*scene
)
253 H_AUTO( NL3D_ShadowManager_Generate
);
255 // Each frame, do a small garbage collector for unused free textures.
256 garbageShadowTextures(scene
);
258 IDriver
*driverForShadowGeneration
= scene
->getRenderTrav().getAuxDriver();
259 nlassert(driverForShadowGeneration
);
260 CSmartPtr
<NL3D::ITexture
> previousRenderTarget
= driverForShadowGeneration
->getRenderTarget();
264 uint32 wndW
= _BlurTextureW
, wndH
= _BlurTextureH
;
265 // get some text/screen size.
266 driverForShadowGeneration
->getWindowSize(wndW
, wndH
);
267 uint baseTextureSize
= scene
->getShadowMapTextureSize();
268 // Minimize the Dest Texture size, so the blurTexture don't get too heavy in VRAM.
269 uint32 textDestW
= min(wndW
, (uint32
)NL3D_SMM_MAX_TEXTDEST_SIZE
);
270 uint32 textDestH
= min(wndH
, (uint32
)NL3D_SMM_MAX_TEXTDEST_SIZE
);
272 // if not needed or if not possible, exit. test for wndSize is also important when window is minimized
273 if( _ShadowCasters
.empty() ||
274 textDestW
<baseTextureSize
|| textDestH
<baseTextureSize
)
276 clearAllShadowCasters();
280 // If Needed to project some ShadowCaster, but none to compute this frame, quit.
281 if( _GenerateShadowCasters
.empty() )
283 // But here don't reset since the renderProject() will do job
287 // get the number of shadowMap compute we can do in one screen.
288 uint numTextW
= textDestW
/baseTextureSize
;
289 uint numTextH
= textDestH
/baseTextureSize
;
290 if(!isPowerOf2(numTextW
))
291 numTextW
= raiseToNextPowerOf2(numTextW
)/2;
292 if(!isPowerOf2(numTextH
))
293 numTextH
= raiseToNextPowerOf2(numTextH
)/2;
294 // the max shadow casters we can do in 1 screen pass.
295 uint maxSCPerPass
= numTextW
* numTextH
;
297 // compute vp float size.
298 float vpWidth
= (float)baseTextureSize
/ (float)(numTextW
*baseTextureSize
);
299 float vpHeight
= (float)baseTextureSize
/ (float)(numTextH
*baseTextureSize
);
302 // Create / Update the Blur Texture
303 updateBlurTexture(*driverForShadowGeneration
, numTextW
* baseTextureSize
, numTextH
* baseTextureSize
);
306 // Do NPass if a screen is not sufficient to render all shadow maps...
310 CViewport bkupViewport
;
311 driverForShadowGeneration
->getViewport(bkupViewport
);
312 bool bkupFog
= driverForShadowGeneration
->fogEnabled();
315 driverForShadowGeneration
->enableFog(false);
316 // Allow Writing on alpha only. => don't write on RGB objects!
317 driverForShadowGeneration
->setColorMask(false, false, false, true);
319 uint numSC
= (uint
)_GenerateShadowCasters
.size();
323 uint numPassSC
= min(maxSCPerPass
, numSC
);
324 // number of line including the last line if not empty
325 uint numTotalLine
= (numPassSC
+numTextW
-1)/numTextW
;
327 uint numTotalCol
= (numPassSC
<numTextW
)?numPassSC
:numTextW
;
329 // Render to the blur texture
330 driverForShadowGeneration
->setRenderTarget (_BlurTexture
[0], 0, 0, numTotalCol
*baseTextureSize
, numTotalLine
*baseTextureSize
);
335 // Render All Shadow Map
338 // Render the polygons with Smooth Anti-Alias. Less jittering for little performance overcost
340 driverForShadowGeneration
->enablePolygonSmoothing(true);
344 for(i
=0;i
<numPassSC
;i
++)
346 // get the transform to compute shadow map.
347 CTransform
*sc
= _GenerateShadowCasters
[baseSC
+i
];
349 // select the shadow direction
351 computeShadowDirection(scene
, sc
, lightDir
);
353 // setup viewport to render to
355 vp
.init(textX
*baseTextureSize
/(float)_BlurTextureW
, textY
*baseTextureSize
/(float)_BlurTextureH
, vpWidth
, vpHeight
);
356 driverForShadowGeneration
->setupViewport(vp
);
358 // TODO_SHADOW: optim: one big erase per pass, but just bbox needed (according to number of SC to render)
359 // do a siccor or prefer do a polygon clear?
361 sic
.init(textX
*baseTextureSize
/(float)_BlurTextureW
, textY
*baseTextureSize
/(float)_BlurTextureH
, vpWidth
, vpHeight
);
362 driverForShadowGeneration
->setupScissor(sic
);
363 driverForShadowGeneration
->clear2D(CRGBA(0,0,0,0));
366 sc
->generateShadowMap(lightDir
);
379 driverForShadowGeneration
->enablePolygonSmoothing(false);
381 // For Subsequent operations, setup a full viewport and a "Screen Frustum"
383 sic
.initFullScreen();
384 // TODO_SHADOW: optim: need scissor?
385 driverForShadowGeneration
->setupScissor(sic
);
388 driverForShadowGeneration
->setupViewport(vp
);
389 driverForShadowGeneration
->setFrustum(0, (float)_BlurTextureW
, 0, (float)_BlurTextureH
, -1,1,false);
390 driverForShadowGeneration
->setupViewMatrix(CMatrix::Identity
);
391 driverForShadowGeneration
->setupModelMatrix(CMatrix::Identity
);
393 // Ensure the One pixel black security on texture border
394 fillBlackBorder(driverForShadowGeneration
, numPassSC
, numTextW
, numTextH
, baseTextureSize
);
398 uint numBlur
= scene
->getShadowMapBlurSize();
399 clamp(numBlur
, 0U, 3U);
401 for(i
=0;i
<numBlur
;i
++)
403 // Set the blur texture target
404 blurTarget
= (i
+1)&1;
405 const uint blurSource
= i
&1;
406 driverForShadowGeneration
->setRenderTarget (_BlurTexture
[blurTarget
], 0, 0, numTotalCol
*baseTextureSize
, numTotalLine
*baseTextureSize
);
409 applyFakeGaussianBlur(driverForShadowGeneration
, numPassSC
, numTextW
, numTextH
, baseTextureSize
, blurSource
);
411 // Ensure the One pixel black security on texture border
412 fillBlackBorder(driverForShadowGeneration
, numPassSC
, numTextW
, numTextH
, baseTextureSize
);
415 // Copy the last blur texture
416 _CopyMaterial
.setTexture(0, _BlurTexture
[blurTarget
]);
418 // Store Screen in ShadowMaps
422 for(i
=0;i
<numPassSC
;i
++)
424 // get the transform to compute shadow map.
425 CTransform
*sc
= _GenerateShadowCasters
[baseSC
+i
];
426 CShadowMap
*sm
= sc
->getShadowMap();
429 ITexture
*text
= sm
->getTexture();
432 uint bts
= baseTextureSize
;
434 // todo hulud : Try the temporary buffer trick (openGL)
435 //if (!driverForShadowGeneration->copyTargetToTexture (text, 0, 0, textX*bts, textY*bts, bts, bts))
438 driverForShadowGeneration
->setRenderTarget (text
, 0, 0, bts
, bts
);
439 driverForShadowGeneration
->clear2D (CRGBA(0,0,0,0));
441 // Viewport is already fullscreen, set the frustum
442 driverForShadowGeneration
->setFrustum(0, 1, 0, 1, -1,1,false);
444 // Set the vertex buffer UV
446 CVertexBufferReadWrite vba
;
447 _CopyQuads
.lock (vba
);
448 const float u
= (float)(textX
*bts
)*_BlurTextureOOW
;
449 const float v
= (float)(textY
*bts
)*_BlurTextureOOH
;
450 const float width
= (float)bts
*_BlurTextureOOW
;
451 const float height
= (float)bts
*_BlurTextureOOH
;
452 vba
.setTexCoord (0, 0, u
, v
);
453 vba
.setTexCoord (1, 0, u
+width
, v
);
454 vba
.setTexCoord (2, 0, u
+width
, v
+height
);
455 vba
.setTexCoord (3, 0, u
, v
+height
);
459 driverForShadowGeneration
->activeVertexBuffer (_CopyQuads
);
462 sic
.initFullScreen();
463 // TODO_SHADOW: optim: need scissor?
464 driverForShadowGeneration
->setupScissor(sic
);
466 driverForShadowGeneration
->setupViewMatrix(CMatrix::Identity
);
467 driverForShadowGeneration
->setupModelMatrix(CMatrix::Identity
);
469 // Render the shadow in the final shadow texture
470 vp
.init (0, 0, 1, 1);
471 driverForShadowGeneration
->setupViewport(vp
);
472 driverForShadowGeneration
->renderRawQuads (_CopyMaterial
, 0, 1);
474 // Set default render target
475 driverForShadowGeneration
->setRenderTarget (NULL
);
478 // Indicate to the ShadowMap that we have updated his Texture
479 sm
->LastGenerationFrame
= scene
->getNumRender();
498 // Set default render target
499 driverForShadowGeneration
->setRenderTarget (previousRenderTarget
);
501 // Allow Writing on all.
502 driverForShadowGeneration
->setColorMask(true, true, true, true);
503 // Restore driver state. (do it here because driverForShadowGeneration may be the main screen).
504 driverForShadowGeneration
->setupViewport(bkupViewport
);
505 driverForShadowGeneration
->enableFog(bkupFog
);
506 // TODO_SHADOW: optim need scissor?
508 sic
.initFullScreen();
509 driverForShadowGeneration
->setupScissor(sic
);
511 // ensure the Scene Driver has correct matrix setup (in common case where AuxDriver == Std Driver)
512 scene
->getRenderTrav().setupDriverCamera();
515 // Clear ShadowCaster Generation
516 clearGenerateShadowCasters();
519 // ***************************************************************************
520 void CShadowMapManager::renderProject(CScene
*scene
)
522 // if not needed exit. NB renderGenerate() must have been called before.
523 if( _ShadowCasters
.empty() )
529 // Project ShadowMap on receivers.
532 H_AUTO( NL3D_ShadowManager_Project
);
535 /* Fog Case: Since we do a modulate, we don't want to modulate the fog color with himself.
536 Instead, if the shadowed pixel is in full fog, we have to modulate him with Blac (modulate with INVERSE-source color, actually ...)
537 => replace fog color with black temporarily
539 IDriver
*driver
= scene
->getRenderTrav().getDriver();
540 CRGBA bkupFogColor
= driver
->getFogColor();
543 driver
->setupFog(driver
->getFogStart(), driver
->getFogEnd(), CRGBA::Black
);
545 /* Light case: CVisualCollisionManager use a fakeLight to avoid ShadowMapping on backFaces of meshs
546 Hence must clean all lights, and enable only the Light0 in driver
548 // Use CRenderTrav::resetLightSetup() to do so, to reset its Cache information
549 scene
->getRenderTrav().resetLightSetup();
550 driver
->enableLight(0, true);
553 // For each ShadowMap
554 for(uint i
=0;i
<_ShadowCasters
.size();i
++)
556 CTransform
*caster
= _ShadowCasters
[i
];
557 CShadowMap
*sm
= caster
->getShadowMap();
559 // NB: the ShadowCaster may not have a texture yet, for example because of Generate selection...
560 // If the final fade is 1, don't render!
561 if( sm
->getTexture() && sm
->getFinalFade()<1 )
563 CVector casterPos
= caster
->getWorldMatrix().getPos();
565 // Compute the World bbox (for quadGrid intersection)
566 CAABBox worldBB
= sm
->LocalBoundingBox
;
567 worldBB
.setCenter(worldBB
.getCenter() + casterPos
);
569 // compute the world matrix of the projection.
570 CMatrix worldProjMat
= sm
->LocalProjectionMatrix
;
571 worldProjMat
.setPos(worldProjMat
.getPos()+casterPos
);
573 // Now compute the textureMatrix, from WorldSpace to UV.
575 wsTextMat
= worldProjMat
;
578 // setup the Material.
579 _ReceiveShadowMaterial
.setTexture(0, sm
->getTexture());
580 /// Get The Mean Ambiant and Diffuse the caster receive (and cast, by approximation)
581 CRGBA ambient
, diffuse
;
582 computeShadowColors(scene
, caster
, ambient
, diffuse
);
583 // In some case, the ambient may be black, which cause problems because the shadow pop while diffuse fade.
584 // ThereFore, supose always a minimum of ambiant 10.
585 ambient
.R
= max(uint8(10), ambient
.R
);
586 ambient
.G
= max(uint8(10), ambient
.G
);
587 ambient
.B
= max(uint8(10), ambient
.B
);
588 // copute the shadowColor so that modulating a Medium diffuse terrain will get the correct result.
589 uint R
= ambient
.R
+ (diffuse
.R
>>1);
590 uint G
= ambient
.G
+ (diffuse
.G
>>1);
591 uint B
= ambient
.B
+ (diffuse
.B
>>1);
595 /* screen= text*(a+d*0.5) (mean value). if we do shadowColor= a/(a+d*0.5f),
596 then we'll have "in theory" screen= text*a
598 R
= (uint
)(256 * ambient
.R
/ (float)R
);
599 G
= (uint
)(256 * ambient
.G
/ (float)G
);
600 B
= (uint
)(256 * ambient
.B
/ (float)B
);
604 /// Finally "modulate" with FinalFade.
605 if(sm
->getFinalFade()>0)
607 sint factor
= OptFastFloor( 256 * sm
->getFinalFade() );
608 clamp(factor
, 0, 256);
609 R
= 255*factor
+ R
*(256-factor
); R
>>=8;
610 G
= 255*factor
+ G
*(256-factor
); G
>>=8;
611 B
= 255*factor
+ B
*(256-factor
); B
>>=8;
613 _ReceiveShadowMaterial
.setColor(CRGBA(uint8(R
),uint8(G
),uint8(B
),255));
615 // init the _ShadowMapProjector
616 _ShadowMapProjector
.setWorldSpaceTextMat(wsTextMat
);
619 _ShadowReceiverGrid
.select(worldBB
.getMin(), worldBB
.getMax());
621 TShadowReceiverGrid::CIterator it
;
622 for(it
= _ShadowReceiverGrid
.begin();it
!=_ShadowReceiverGrid
.end();it
++)
624 CTransform
*receiver
= *it
;
625 // Avoid Auto-Casting.
629 // update the material texture projection
630 // see getReceiverRenderWorldMatrix() Doc for why using this instead of getWorldMatrix()
631 _ShadowMapProjector
.applyToMaterial(receiver
->getReceiverRenderWorldMatrix(), _ReceiveShadowMaterial
);
633 // cast the shadow on them
634 receiver
->receiveShadowMap(sm
, casterPos
, _ReceiveShadowMaterial
);
637 // Additionaly, the VisualCollisionManager may manage some shadow receiving
638 CVisualCollisionManager
*shadowVcm
= scene
->getVisualCollisionManagerForShadow();
641 shadowVcm
->receiveShadowMap(driver
, sm
, casterPos
, _ReceiveShadowMaterial
, _ShadowMapProjector
);
647 driver
->setupFog(driver
->getFogStart(), driver
->getFogEnd(), bkupFogColor
);
649 // Leave Light Setup in a clean State
650 scene
->getRenderTrav().resetLightSetup();
653 // TestYoyo. Display Projection BBox.
655 for(uint i=0;i<_ShadowCasters.size();i++)
657 // get the transform to compute shadow map.
658 CTransform *sc= _ShadowCasters[i];
660 CShadowMap *sm= sc->getShadowMap();
663 CVector p0= sm->LocalProjectionMatrix.getPos() + sc->getWorldMatrix().getPos();
664 IDriver &drv= *driver;
666 drv.setupModelMatrix(CMatrix::Identity);
668 CDRU::drawWiredBox(p0, sm->LocalProjectionMatrix.getI(), sm->LocalProjectionMatrix.getJ(),
669 sm->LocalProjectionMatrix.getK(), CRGBA::White, drv);
676 sic.initFullScreen();
677 // TODO_SHADOW: optim: need scissor?
678 driver->setupScissor(sic);
679 driver->setupViewMatrix(CMatrix::Identity);
680 driver->setupModelMatrix(CMatrix::Identity);
681 driver->setFrustum (0,1,0,1,-1,1,false);
682 // Render the shadow in the final shadow texture
684 vp.init (0, 0, 0.5f, 0.5f);
685 driver->setupViewport(vp);
687 static CVertexBuffer CopyQuads;
688 static CMaterial CopyMaterial;
689 CopyMaterial.initUnlit();
690 CopyMaterial.setColor(CRGBA::White);
691 CopyMaterial.setZWrite(false);
692 CopyMaterial.setZFunc(CMaterial::always);
693 CopyMaterial.setDoubleSided(true);
694 CopyMaterial.setBlend (false);
695 CopyMaterial.setAlphaTest (false);
696 CopyMaterial.setBlendFunc (CMaterial::one, CMaterial::zero);
697 CopyMaterial.texEnvOpRGB(0, CMaterial::Replace);
698 CopyMaterial.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcAlpha);
699 CopyQuads.setVertexFormat (CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag);
700 CopyQuads.setNumVertices(4);
702 CVertexBufferReadWrite vba;
703 CopyQuads.lock (vba);
704 vba.setVertexCoord (0, CVector (0, 0, 0));
705 vba.setVertexCoord (1, CVector (1, 0, 0));
706 vba.setVertexCoord (2, CVector (1, 0, 1));
707 vba.setVertexCoord (3, CVector (0, 0, 1));
708 vba.setTexCoord (0, 0, 0, 0);
709 vba.setTexCoord (1, 0, 1, 0);
710 vba.setTexCoord (2, 0, 1, 1);
711 vba.setTexCoord (3, 0, 0, 1);
713 driver->activeVertexBuffer (CopyQuads);
715 if (!_ShadowCasters.empty())
717 // get the transform to compute shadow map.
718 CTransform *sc= _ShadowCasters[0];
720 CShadowMap *sm= sc->getShadowMap();
723 CopyMaterial.setTexture (0, sm->getTexture());
724 driver->renderRawQuads (CopyMaterial, 0, 1);
731 clearAllShadowCasters();
735 // ***************************************************************************
736 void CShadowMapManager::computeShadowDirection(CScene
*scene
, CTransform
*sc
, CVector
&lightDir
)
738 // merge the sunLight and pointLights into a single directional
739 lightDir
= scene
->getSunDirection();
740 const CLightContribution
&lc
= sc
->getLightContribution();
741 // For Better result, weight with the light color too.
742 CRGBA color
= scene
->getSunDiffuse();
743 lightDir
*= (float)lc
.SunContribution
* (color
.R
+ color
.G
+ color
.B
);
746 const CVector
&modelPos
= sc
->getWorldMatrix().getPos();
747 for(uint i
=0;i
<NL3D_MAX_LIGHT_CONTRIBUTION
;i
++)
749 CPointLight
*pl
= lc
.PointLight
[i
];
754 CVector plDir
= modelPos
- pl
->getPosition();
756 // Sum with this light, weighted by AttFactor, and light color
757 color
= pl
->getDiffuse();
758 lightDir
+= plDir
* (float)lc
.AttFactor
[i
] * (float)(color
.R
+ color
.G
+ color
.B
);
761 // normalize merged dir
762 lightDir
.normalize();
764 // clamp the light direction in z, according to Caster restriction
765 float zThre
= sc
->getShadowMapDirectionZThreshold();
768 /* normalize the x/y component so z=zthre
769 we want this: sqrt(x2+y2+z2)==1, which solve for x2+y2= 1-z2
770 the scale to apply to x and y is therefore deduced from:
771 sqr(scale)=(1-z2)/(x2+y2)
774 if(lightDir
.x
!=0.f
|| lightDir
.y
!=0.f
)
775 scale
= sqrtf( (1-sqr(zThre
)) / (sqr(lightDir
.x
)+sqr(lightDir
.y
)) );
778 // force z component to be at least zthre
781 // re-normalize in case of precision problems
782 lightDir
.normalize();
787 // ***************************************************************************
788 void CShadowMapManager::computeShadowColors(CScene
*scene
, CTransform
*sc
, CRGBA
&ambient
, CRGBA
&diffuse
)
790 const CLightContribution
&lc
= sc
->getLightContribution();
792 // Get the current ambiant
793 ambient
= lc
.computeCurrentAmbient(scene
->getSunAmbient());
795 // Compute the current diffuse as a sum (not a mean)
797 CRGBA color
= scene
->getSunDiffuse();
798 r
= color
.R
* lc
.SunContribution
;
799 g
= color
.G
* lc
.SunContribution
;
800 b
= color
.B
* lc
.SunContribution
;
802 // Add PointLights contribution
803 for(uint i
=0;i
<NL3D_MAX_LIGHT_CONTRIBUTION
;i
++)
805 CPointLight
*pl
= lc
.PointLight
[i
];
810 // Sum with this light, weighted by AttFactor
811 color
= pl
->getDiffuse();
812 r
+= color
.R
* lc
.AttFactor
[i
];
813 g
+= color
.G
* lc
.AttFactor
[i
];
814 b
+= color
.B
* lc
.AttFactor
[i
];
822 // Don't take the MergedPointLight into consideration (should add to the diffuse part here, but rare case)
824 diffuse
.R
= uint8(min(r
, 255U));
825 diffuse
.G
= uint8(min(g
, 255U));
826 diffuse
.B
= uint8(min(b
, 255U));
830 // ***************************************************************************
831 void CShadowMapManager::fillBlackBorder(IDriver
*drv
, uint numPassText
, uint numTextW
, uint numTextH
, uint baseTextureSize
)
836 // the number of lines that have all their column disp.
837 uint numFullLine
= numPassText
/numTextW
;
838 // for the last line not full, the number of column setuped
839 uint lastLineNumCol
= numPassText
- (numFullLine
*numTextW
);
840 // number of line including the last line if not empty
841 uint numTotalLine
= numFullLine
+ (lastLineNumCol
?1:0);
843 // Compute how many quads to render
844 uint numHQuads
= numTotalLine
* 2;
848 numTotalCol
= numTextW
;
850 numTotalCol
= lastLineNumCol
;
851 numVQuads
= numTotalCol
* 2;
853 _FillQuads
.setNumVertices((numVQuads
+ numHQuads
)*4);
857 for(i
=0;i
<numTotalLine
;i
++)
861 w
= numTextW
*baseTextureSize
;
863 w
= lastLineNumCol
*baseTextureSize
;
865 setBlackQuad(i
*2+0, 0, i
*baseTextureSize
, w
, 1);
867 setBlackQuad(i
*2+1, 0, (i
+1)*baseTextureSize
-1, w
, 1);
871 uint baseId
= numTotalLine
*2;
872 for(i
=0;i
<numTotalCol
;i
++)
876 h
= numTotalLine
*baseTextureSize
;
878 h
= numFullLine
*baseTextureSize
;
880 setBlackQuad(baseId
+ i
*2+0, i
*baseTextureSize
, 0, 1, h
);
882 setBlackQuad(baseId
+ i
*2+1, (i
+1)*baseTextureSize
-1, 0, 1, h
);
886 _FillMaterial
.setColor(CRGBA(0,0,0,0));
887 drv
->activeVertexBuffer(_FillQuads
);
888 drv
->renderRawQuads(_FillMaterial
, 0, numHQuads
+numVQuads
);
892 // ***************************************************************************
893 void CShadowMapManager::setBlackQuad(uint index
, sint x
, sint y
, sint w
, sint h
)
897 float x1
= (float)x
+(float)w
;
898 float y1
= (float)y
+(float)h
;
900 CVertexBufferReadWrite vba
;
901 _FillQuads
.lock(vba
);
902 vba
.setVertexCoord (index
+0, CVector (x0
, 0, y0
));
903 vba
.setVertexCoord (index
+1, CVector (x1
, 0, y0
));
904 vba
.setVertexCoord (index
+2, CVector (x1
, 0, y1
));
905 vba
.setVertexCoord (index
+3, CVector (x0
, 0, y1
));
908 // ***************************************************************************
909 void CShadowMapManager::updateBlurTexture(IDriver
&drv
, uint w
, uint h
)
913 // if same size than setup, quit
914 if(_BlurTextureW
==w
&& _BlurTextureH
==h
)
917 // release old SmartPtr
921 _BlurMaterial
[i
].setTexture(0, NULL
);
922 _BlurMaterial
[i
].setTexture(1, NULL
);
923 _BlurMaterial
[i
].setTexture(2, NULL
);
924 _BlurMaterial
[i
].setTexture(3, NULL
);
926 _BlurTexture
[0]= NULL
;
927 _BlurTexture
[1]= NULL
;
930 // NB: the format must be RGBA; else slow copyFrameBufferToTexture()
933 uint8
*tmpMem
= new uint8
[4*_BlurTextureW
*_BlurTextureH
];
934 _BlurTexture
[i
] = new CTextureMem (tmpMem
, 4*_BlurTextureW
*_BlurTextureH
, true, false, _BlurTextureW
, _BlurTextureH
);
935 _BlurTexture
[i
]->setWrapS (ITexture::Clamp
);
936 _BlurTexture
[i
]->setWrapT (ITexture::Clamp
);
937 _BlurTexture
[i
]->setFilterMode (ITexture::Linear
, ITexture::LinearMipMapOff
);
938 _BlurTexture
[i
]->generate();
939 _BlurTexture
[i
]->setReleasable (false);
940 _BlurTexture
[i
]->setRenderTarget (true);
941 // For Texture Profiling
942 _BlurTexture
[i
]->setTextureCategory(_TextureCategory
);
945 uint maxNumCstLighted
;
946 uint maxNumCstUnlighted
;
947 drv
.getNumPerStageConstant(maxNumCstLighted
, maxNumCstUnlighted
);
949 // set to the material
952 for (j
=0; j
<maxNumCstUnlighted
; j
++)
954 _BlurMaterial
[i
].setTexture(j
, _BlurTexture
[i
]);
958 // compute values for texturing
959 _BlurTextureOOW
= 1.f
/ _BlurTextureW
;
960 _BlurTextureOOH
= 1.f
/ _BlurTextureH
;
961 // The Delta HalfPixel
962 _BlurTextureD05W
= 0.5f
*_BlurTextureOOW
;
963 _BlurTextureD05H
= 0.5f
*_BlurTextureOOH
;
967 // ***************************************************************************
968 void CShadowMapManager::copyScreenToBlurTexture(IDriver
*drv
, uint numPassText
, uint numTextW
, uint numTextH
, uint baseTextureSize
)
973 // TODO_SHADOW: optim: split into 2 copy for less pixel draw on the last line? No because of OverHead?
975 // number of line including the last line if not empty
976 // uint numTotalLine= (numPassText+numTextW-1)/numTextW;
978 // uint numTotalCol= (numPassText<numTextW)?numPassText:numTextW;
980 /* todo hulud shadows
981 drv->copyFrameBufferToTexture(_BlurTexture, 0, 0, 0, 0, 0, numTotalCol*baseTextureSize, numTotalLine*baseTextureSize); */
984 // ***************************************************************************
985 void CShadowMapManager::applyFakeGaussianBlur(IDriver
*drv
, uint numPassText
, uint numTextW
, uint numTextH
, uint baseTextureSize
, uint blurSource
)
990 // the number of lines that have all their column disp.
991 uint numFullLine
= numPassText
/numTextW
;
992 // for the last line not full, the number of column setuped
993 uint lastLineNumCol
= numPassText
- (numFullLine
*numTextW
);
995 // Split into 2 quads. one for the first full lines, and one for the last not full line.
998 setBlurQuadFakeGaussian(index
++, 0, 0, numTextW
*baseTextureSize
, numFullLine
*baseTextureSize
);
1000 setBlurQuadFakeGaussian(index
++, 0, numFullLine
*baseTextureSize
, lastLineNumCol
*baseTextureSize
, baseTextureSize
);
1003 drv
->activeVertexBuffer(_BlurQuads
);
1004 drv
->renderRawQuads(_BlurMaterial
[blurSource
], 0, index
);
1008 // ***************************************************************************
1009 void CShadowMapManager::setBlurQuadFakeGaussian(uint index
, sint x
, sint y
, sint w
, sint h
)
1013 float x1
= (float)x
+(float)w
;
1014 float y1
= (float)y
+(float)h
;
1015 float u0
= x0
*_BlurTextureOOW
;
1016 float v0
= y0
*_BlurTextureOOH
;
1017 float u1
= x1
*_BlurTextureOOW
;
1018 float v1
= y1
*_BlurTextureOOH
;
1021 // NB: the order of the Delta (--,++,-+,+-) is made so it works well with 2,3 or 4 texture support.
1024 CVertexBufferReadWrite vba
;
1025 _BlurQuads
.lock(vba
);
1026 vba
.setVertexCoord (index
+0, CVector (x0
, 0, y0
));
1027 vba
.setTexCoord(index
+0, 0, u0
-_BlurTextureD05W
, v0
-_BlurTextureD05H
);
1028 vba
.setTexCoord(index
+0, 1, u0
+_BlurTextureD05W
, v0
+_BlurTextureD05H
);
1029 vba
.setTexCoord(index
+0, 2, u0
-_BlurTextureD05W
, v0
+_BlurTextureD05H
);
1030 vba
.setTexCoord(index
+0, 3, u0
+_BlurTextureD05W
, v0
-_BlurTextureD05H
);
1032 vba
.setVertexCoord (index
+1, CVector (x1
, 0, y0
));
1033 vba
.setTexCoord(index
+1, 0, u1
-_BlurTextureD05W
, v0
-_BlurTextureD05H
);
1034 vba
.setTexCoord(index
+1, 1, u1
+_BlurTextureD05W
, v0
+_BlurTextureD05H
);
1035 vba
.setTexCoord(index
+1, 2, u1
-_BlurTextureD05W
, v0
+_BlurTextureD05H
);
1036 vba
.setTexCoord(index
+1, 3, u1
+_BlurTextureD05W
, v0
-_BlurTextureD05H
);
1038 vba
.setVertexCoord (index
+2, CVector (x1
, 0, y1
));
1039 vba
.setTexCoord(index
+2, 0, u1
-_BlurTextureD05W
, v1
-_BlurTextureD05H
);
1040 vba
.setTexCoord(index
+2, 1, u1
+_BlurTextureD05W
, v1
+_BlurTextureD05H
);
1041 vba
.setTexCoord(index
+2, 2, u1
-_BlurTextureD05W
, v1
+_BlurTextureD05H
);
1042 vba
.setTexCoord(index
+2, 3, u1
+_BlurTextureD05W
, v1
-_BlurTextureD05H
);
1044 vba
.setVertexCoord (index
+3, CVector (x0
, 0, y1
));
1045 vba
.setTexCoord(index
+3, 0, u0
-_BlurTextureD05W
, v1
-_BlurTextureD05H
);
1046 vba
.setTexCoord(index
+3, 1, u0
+_BlurTextureD05W
, v1
+_BlurTextureD05H
);
1047 vba
.setTexCoord(index
+3, 2, u0
-_BlurTextureD05W
, v1
+_BlurTextureD05H
);
1048 vba
.setTexCoord(index
+3, 3, u0
+_BlurTextureD05W
, v1
-_BlurTextureD05H
);
1052 // ***************************************************************************
1053 struct CShadowMapSort
1058 bool operator<(const CShadowMapSort
&o
) const
1060 return Weight
<o
.Weight
;
1064 // ***************************************************************************
1065 void CShadowMapManager::selectShadowMapsToGenerate(CScene
*scene
)
1067 // TODO: Scene option.
1068 const uint maxPerFrame
= 8;
1069 const float minCamDist
= 10;
1070 const CVector
&camPos
= scene
->getRenderTrav().CamPos
;
1074 clearGenerateShadowCasters();
1076 // If the scene filter skeleton render, suppose no generation at all. Ugly.
1077 if(! (scene
->getFilterRenderFlags() & UScene::FilterSkeleton
) )
1081 // For all ShadowCaster inserted
1082 static vector
<CShadowMapSort
> sortList
;
1084 sortList
.reserve(_ShadowCasters
.size());
1085 for(i
=0;i
<_ShadowCasters
.size();i
++)
1087 CTransform
*caster
= _ShadowCasters
[i
];
1088 /* If the shadowMap exist, and if not totaly faded
1089 NB: take FinalFade here because if 1, it won't be rendered in renderProject()
1090 so don't really need to update (useful for update reason, but LastGenerationFrame do the job)
1092 if(caster
->getShadowMap() && caster
->getShadowMap()->getFinalFade()<1 )
1096 // The Weight is the positive delta of frame
1097 sms
.Weight
= (float)(scene
->getNumRender() - caster
->getShadowMap()->LastGenerationFrame
);
1098 // Modulated by Caster Distance from Camera.
1099 float distToCam
= (caster
->getWorldMatrix().getPos() - camPos
).norm();
1100 distToCam
= max(distToCam
, minCamDist
);
1101 // The farthest, the less important
1102 sms
.Weight
/= distToCam
;
1105 sortList
.push_back(sms
);
1110 sort(sortList
.begin(), sortList
.end());
1113 uint numSel
= min((uint
)sortList
.size(), maxPerFrame
);
1114 _GenerateShadowCasters
.resize(numSel
);
1115 for(i
= 0;i
<numSel
;i
++)
1117 _GenerateShadowCasters
[i
]= sortList
[sortList
.size()-1-i
].Caster
;
1120 // **** Flag selecteds
1121 // For All selected models, indicate that they will generate shadowMap for this Frame.
1122 for(i
=0;i
<_GenerateShadowCasters
.size();i
++)
1124 _GenerateShadowCasters
[i
]->setGeneratingShadowMap(true);
1129 // ***************************************************************************
1130 void CShadowMapManager::clearAllShadowCasters()
1132 _ShadowReceiverGrid
.clear();
1133 _ShadowCasters
.clear();
1134 clearGenerateShadowCasters();
1138 // ***************************************************************************
1139 void CShadowMapManager::clearGenerateShadowCasters()
1141 // Reset first each flag of all models
1142 for(uint i
=0;i
<_GenerateShadowCasters
.size();i
++)
1144 _GenerateShadowCasters
[i
]->setGeneratingShadowMap(false);
1147 _GenerateShadowCasters
.clear();
1150 // ***************************************************************************
1151 ITexture
*CShadowMapManager::allocateTexture(uint textSize
)
1153 nlassert( isPowerOf2(textSize
) );
1155 // **** First, find a free texture already allocated.
1156 if(!_FreeShadowTextures
.empty())
1158 ITexture
*freeText
= _FreeShadowTextures
.back()->second
;
1159 // If Ok for the size.
1160 if(freeText
->getWidth() == textSize
)
1162 // take this texture => no more free.
1163 _FreeShadowTextures
.pop_back();
1166 // else, suppose that we still take this slot.
1169 // but since bad size, delete this slot from the map and create a new one (below)
1170 _ShadowTextureMap
.erase(_FreeShadowTextures
.back());
1172 _FreeShadowTextures
.pop_back();
1176 // **** Else Allocate new one.
1177 // NB: the format must be RGBA; else slow copyFrameBufferToTexture()
1178 uint8
*tmpMem
= new uint8
[4*textSize
*textSize
];
1180 text
= new CTextureMem (tmpMem
, 4*textSize
*textSize
, true, false, textSize
, textSize
);
1181 text
->setWrapS (ITexture::Clamp
);
1182 text
->setWrapT (ITexture::Clamp
);
1183 text
->setFilterMode (ITexture::Linear
, ITexture::LinearMipMapOff
);
1185 text
->setReleasable (false);
1186 text
->setRenderTarget (true);
1187 // For Texture Profiling
1188 text
->setTextureCategory(_TextureCategory
);
1190 // Setup in the map.
1191 _ShadowTextureMap
[text
]= text
;
1196 // ***************************************************************************
1197 void CShadowMapManager::releaseTexture(ITexture
*text
)
1202 ItTextureMap it
= _ShadowTextureMap
.find(text
);
1203 nlassert(it
!=_ShadowTextureMap
.end());
1205 // Don't release it, but insert in Free Space
1206 _FreeShadowTextures
.push_back(it
);
1209 // ***************************************************************************
1210 void CShadowMapManager::garbageShadowTextures(CScene
*scene
)
1212 uint defSize
= scene
->getShadowMapTextureSize();
1214 // For all Free Textures only, release the one that are no more of the wanted default ShadowMap Size.
1215 std::vector
<ItTextureMap
>::iterator itVec
= _FreeShadowTextures
.begin();
1216 for(;itVec
!=_FreeShadowTextures
.end();)
1218 if((*itVec
)->second
->getWidth() != defSize
)
1220 // release the map texture iterator
1221 _ShadowTextureMap
.erase(*itVec
);
1222 // release the Vector Free iterator.
1223 itVec
= _FreeShadowTextures
.erase(itVec
);
1231 // For memory optimisation, allow only a small extra of Texture allocated.
1232 if(_FreeShadowTextures
.size()>NL3D_SMM_MAX_FREETEXT
)
1234 // Release the extra texture (Hysteresis: divide by 2 the max wanted free to leave)
1235 uint startToFree
= NL3D_SMM_MAX_FREETEXT
/2;
1236 for(uint i
=startToFree
;i
<_FreeShadowTextures
.size();i
++)
1238 // Free the texture entry.
1239 _ShadowTextureMap
.erase(_FreeShadowTextures
[i
]);
1242 _FreeShadowTextures
.resize(startToFree
);