BsShadowRendering.cpp 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsShadowRendering.h"
  4. #include "BsRendererView.h"
  5. #include "BsRendererScene.h"
  6. #include "BsLight.h"
  7. #include "BsRendererUtility.h"
  8. #include "BsGpuParamsSet.h"
  9. #include "BsMesh.h"
  10. #include "BsCamera.h"
  11. #include "BsBitwise.h"
  12. #include "BsVertexDataDesc.h"
  13. namespace bs { namespace ct
  14. {
  15. ShadowParamsDef gShadowParamsDef;
  16. ShadowDepthNormalMat::ShadowDepthNormalMat()
  17. { }
  18. void ShadowDepthNormalMat::_initDefines(ShaderDefines& defines)
  19. {
  20. // No defines
  21. }
  22. void ShadowDepthNormalMat::bind(const SPtr<GpuParamBlockBuffer>& shadowParams)
  23. {
  24. mParamsSet->setParamBlockBuffer("ShadowParams", shadowParams);
  25. gRendererUtility().setPass(mMaterial);
  26. }
  27. void ShadowDepthNormalMat::setPerObjectBuffer(const SPtr<GpuParamBlockBuffer>& perObjectParams)
  28. {
  29. mParamsSet->setParamBlockBuffer("PerObject", perObjectParams);
  30. gRendererUtility().setPassParams(mParamsSet);
  31. }
  32. ShadowDepthDirectionalMat::ShadowDepthDirectionalMat()
  33. { }
  34. void ShadowDepthDirectionalMat::_initDefines(ShaderDefines& defines)
  35. {
  36. // No defines
  37. }
  38. void ShadowDepthDirectionalMat::bind(const SPtr<GpuParamBlockBuffer>& shadowParams)
  39. {
  40. mParamsSet->setParamBlockBuffer("ShadowParams", shadowParams);
  41. gRendererUtility().setPass(mMaterial);
  42. }
  43. void ShadowDepthDirectionalMat::setPerObjectBuffer(const SPtr<GpuParamBlockBuffer>& perObjectParams)
  44. {
  45. mParamsSet->setParamBlockBuffer("PerObject", perObjectParams);
  46. gRendererUtility().setPassParams(mParamsSet);
  47. }
  48. ShadowCubeMatricesDef gShadowCubeMatricesDef;
  49. ShadowCubeMasksDef gShadowCubeMasksDef;
  50. ShadowDepthCubeMat::ShadowDepthCubeMat()
  51. { }
  52. void ShadowDepthCubeMat::_initDefines(ShaderDefines& defines)
  53. {
  54. // No defines
  55. }
  56. void ShadowDepthCubeMat::bind(const SPtr<GpuParamBlockBuffer>& shadowParams,
  57. const SPtr<GpuParamBlockBuffer>& shadowCubeMatrices)
  58. {
  59. mParamsSet->setParamBlockBuffer("ShadowParams", shadowParams);
  60. mParamsSet->setParamBlockBuffer("ShadowCubeMatrices", shadowCubeMatrices);
  61. gRendererUtility().setPass(mMaterial);
  62. }
  63. void ShadowDepthCubeMat::setPerObjectBuffer(const SPtr<GpuParamBlockBuffer>& perObjectParams,
  64. const SPtr<GpuParamBlockBuffer>& shadowCubeMasks)
  65. {
  66. mParamsSet->setParamBlockBuffer("PerObject", perObjectParams);
  67. mParamsSet->setParamBlockBuffer("ShadowCubeMasks", shadowCubeMasks);
  68. gRendererUtility().setPassParams(mParamsSet);
  69. }
  70. ShadowProjectParamsDef gShadowProjectParamsDef;
  71. ShadowProjectVertParamsDef gShadowProjectVertParamsDef;
  72. template<bool Directional, bool ZFailStencil>
  73. ShadowProjectStencilMat<Directional, ZFailStencil>::ShadowProjectStencilMat()
  74. {
  75. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  76. mVertParams = gShadowProjectVertParamsDef.createBuffer();
  77. if(params->hasParamBlock(GPT_VERTEX_PROGRAM, "VertParams"))
  78. params->setParamBlockBuffer(GPT_VERTEX_PROGRAM, "VertParams", mVertParams);
  79. }
  80. template<bool Directional, bool ZFailStencil>
  81. void ShadowProjectStencilMat<Directional, ZFailStencil>::_initDefines(ShaderDefines& defines)
  82. {
  83. defines.set("NEEDS_TRANSFORM", Directional ? 0 : 1);
  84. if(ZFailStencil)
  85. defines.set("USE_ZFAIL_STENCIL", 1);
  86. }
  87. template<bool Directional, bool ZFailStencil>
  88. void ShadowProjectStencilMat<Directional, ZFailStencil>::bind(const SPtr<GpuParamBlockBuffer>& perCamera)
  89. {
  90. Vector4 lightPosAndScale(0, 0, 0, 0); // Not used
  91. gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
  92. mParamsSet->setParamBlockBuffer("PerCamera", perCamera);
  93. gRendererUtility().setPass(mMaterial);
  94. gRendererUtility().setPassParams(mParamsSet);
  95. }
  96. void ShadowProjectStencilMaterials::bind(bool directional, bool useZFailStencil,
  97. const SPtr<GpuParamBlockBuffer>& perCamera)
  98. {
  99. if(directional)
  100. {
  101. // Always uses z-fail stencil
  102. mTT.bind(perCamera);
  103. }
  104. else
  105. {
  106. if (useZFailStencil)
  107. mFT.bind(perCamera);
  108. else
  109. mFF.bind(perCamera);
  110. }
  111. }
  112. template<int ShadowQuality, bool Directional, bool MSAA>
  113. ShadowProjectMat<ShadowQuality, Directional, MSAA>::ShadowProjectMat()
  114. : mGBufferParams(mMaterial, mParamsSet)
  115. {
  116. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  117. params->getTextureParam(GPT_FRAGMENT_PROGRAM, "gShadowTex", mShadowMapParam);
  118. params->getSamplerStateParam(GPT_FRAGMENT_PROGRAM, "gShadowSampler", mShadowSamplerParam);
  119. SAMPLER_STATE_DESC desc;
  120. desc.minFilter = FO_POINT;
  121. desc.magFilter = FO_POINT;
  122. desc.mipFilter = FO_POINT;
  123. desc.addressMode.u = TAM_CLAMP;
  124. desc.addressMode.v = TAM_CLAMP;
  125. desc.addressMode.w = TAM_CLAMP;
  126. mSamplerState = SamplerState::create(desc);
  127. mVertParams = gShadowProjectVertParamsDef.createBuffer();
  128. if(params->hasParamBlock(GPT_VERTEX_PROGRAM, "VertParams"))
  129. params->setParamBlockBuffer(GPT_VERTEX_PROGRAM, "VertParams", mVertParams);
  130. }
  131. template<int ShadowQuality, bool Directional, bool MSAA>
  132. void ShadowProjectMat<ShadowQuality, Directional, MSAA>::_initDefines(ShaderDefines& defines)
  133. {
  134. switch(ShadowQuality)
  135. {
  136. default:
  137. case 1:
  138. defines.set("SHADOW_QUALITY", 1);
  139. break;
  140. case 2:
  141. defines.set("SHADOW_QUALITY", 2);
  142. break;
  143. case 3:
  144. defines.set("SHADOW_QUALITY", 3);
  145. break;
  146. case 4:
  147. defines.set("SHADOW_QUALITY", 4);
  148. break;
  149. }
  150. if(Directional)
  151. defines.set("FADE_PLANE", 1);
  152. defines.set("NEEDS_TRANSFORM", Directional ? 0 : 1);
  153. defines.set("MSAA_COUNT", MSAA ? 2 : 1); // Actual count doesn't matter, as long as its >1 if enabled
  154. }
  155. /**
  156. * Converts a point in mixed space (clip_x, clip_y, view_z, view_w) to UV coordinates on a shadow map (x, y),
  157. * and normalized linear depth from the shadow caster's perspective (z).
  158. */
  159. Matrix4 createMixedToShadowUVMatrix(const Matrix4& viewP, const Matrix4& viewInvVP, const Rect2& shadowMapArea,
  160. float depthRange, const Matrix4& shadowViewProj)
  161. {
  162. // Projects a point from (clip_x, clip_y, view_z, view_w) into clip space
  163. Matrix4 mixedToShadow = Matrix4::IDENTITY;
  164. mixedToShadow[2][2] = viewP[2][2];
  165. mixedToShadow[2][3] = viewP[2][3];
  166. mixedToShadow[3][2] = viewP[3][2];
  167. mixedToShadow[3][3] = 0.0f;
  168. // Projects a point in clip space back to homogeneus world space
  169. mixedToShadow = viewInvVP * mixedToShadow;
  170. // Projects a point in world space to shadow clip space
  171. mixedToShadow = shadowViewProj * mixedToShadow;
  172. // Convert shadow clip space coordinates to UV coordinates relative to the shadow map rectangle, and normalize
  173. // depth
  174. RenderAPI& rapi = RenderAPI::instance();
  175. const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
  176. float flipY = -1.0f;
  177. // Either of these flips the Y axis, but if they're both true they cancel out
  178. if (rapiInfo.isFlagSet(RenderAPIFeatureFlag::UVYAxisUp) ^ rapiInfo.isFlagSet(RenderAPIFeatureFlag::NDCYAxisDown))
  179. flipY = -flipY;
  180. Matrix4 shadowMapTfrm
  181. (
  182. shadowMapArea.width * 0.5f, 0, 0, shadowMapArea.x + 0.5f,
  183. 0, flipY * shadowMapArea.height * 0.5f, 0, shadowMapArea.y + 0.5f,
  184. 0, 0, 1.0f / depthRange, 0,
  185. 0, 0, 0, 1
  186. );
  187. return shadowMapTfrm * mixedToShadow;
  188. }
  189. template <int ShadowQuality, bool Directional, bool MSAA>
  190. void ShadowProjectMat<ShadowQuality, Directional, MSAA>::bind(const ShadowProjectParams& params)
  191. {
  192. Vector4 lightPosAndScale(params.light.getPosition(), params.light.getAttenuationRadius());
  193. gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
  194. TextureSurface surface;
  195. surface.arraySlice = params.shadowMapFace;
  196. mShadowMapParam.set(params.shadowMap, surface);
  197. mShadowSamplerParam.set(mSamplerState);
  198. mGBufferParams.bind(params.renderTargets);
  199. mParamsSet->setParamBlockBuffer("Params", params.shadowParams);
  200. mParamsSet->setParamBlockBuffer("PerCamera", params.perCamera);
  201. gRendererUtility().setPass(mMaterial);
  202. gRendererUtility().setPassParams(mParamsSet);
  203. }
  204. void ShadowProjectMaterials::bind(UINT32 quality, bool directional, bool MSAA, const ShadowProjectParams& params)
  205. {
  206. #define BIND_MAT(QUALITY) \
  207. { \
  208. if(directional) \
  209. if (MSAA) \
  210. mMat##QUALITY##TT.bind(params); \
  211. else \
  212. mMat##QUALITY##TF.bind(params); \
  213. else \
  214. if (MSAA) \
  215. mMat##QUALITY##FT.bind(params); \
  216. else \
  217. mMat##QUALITY##FF.bind(params); \
  218. }
  219. if(quality <= 1)
  220. BIND_MAT(1)
  221. else if(quality == 2)
  222. BIND_MAT(2)
  223. else if(quality == 3)
  224. BIND_MAT(3)
  225. else // 4 or higher
  226. BIND_MAT(4)
  227. #undef BIND_MAT
  228. }
  229. ShadowProjectOmniParamsDef gShadowProjectOmniParamsDef;
  230. template<int ShadowQuality, bool Inside, bool MSAA>
  231. ShadowProjectOmniMat<ShadowQuality, Inside, MSAA>::ShadowProjectOmniMat()
  232. : mGBufferParams(mMaterial, mParamsSet)
  233. {
  234. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  235. params->getTextureParam(GPT_FRAGMENT_PROGRAM, "gShadowCubeTex", mShadowMapParam);
  236. params->getSamplerStateParam(GPT_FRAGMENT_PROGRAM, "gShadowCubeSampler", mShadowSamplerParam);
  237. SAMPLER_STATE_DESC desc;
  238. desc.minFilter = FO_LINEAR;
  239. desc.magFilter = FO_LINEAR;
  240. desc.mipFilter = FO_POINT;
  241. desc.addressMode.u = TAM_CLAMP;
  242. desc.addressMode.v = TAM_CLAMP;
  243. desc.addressMode.w = TAM_CLAMP;
  244. desc.comparisonFunc = CMPF_GREATER_EQUAL;
  245. mSamplerState = SamplerState::create(desc);
  246. mVertParams = gShadowProjectVertParamsDef.createBuffer();
  247. if(params->hasParamBlock(GPT_VERTEX_PROGRAM, "VertParams"))
  248. params->setParamBlockBuffer(GPT_VERTEX_PROGRAM, "VertParams", mVertParams);
  249. }
  250. template<int ShadowQuality, bool Inside, bool MSAA>
  251. void ShadowProjectOmniMat<ShadowQuality, Inside, MSAA>::_initDefines(ShaderDefines& defines)
  252. {
  253. switch(ShadowQuality)
  254. {
  255. default:
  256. case 1:
  257. defines.set("SHADOW_QUALITY", 1);
  258. break;
  259. case 2:
  260. defines.set("SHADOW_QUALITY", 2);
  261. break;
  262. case 3:
  263. defines.set("SHADOW_QUALITY", 3);
  264. break;
  265. case 4:
  266. defines.set("SHADOW_QUALITY", 4);
  267. break;
  268. }
  269. defines.set("NEEDS_TRANSFORM", 1);
  270. defines.set("MSAA_COUNT", MSAA ? 2 : 1); // Actual count doesn't matter, as long as its >1 if enabled
  271. if (Inside)
  272. defines.set("VIEWER_INSIDE_VOLUME", 1);
  273. }
  274. template<int ShadowQuality, bool Inside, bool MSAA>
  275. void ShadowProjectOmniMat<ShadowQuality, Inside, MSAA>::bind(const ShadowProjectParams& params)
  276. {
  277. Vector4 lightPosAndScale(params.light.getPosition(), params.light.getAttenuationRadius());
  278. gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
  279. mShadowMapParam.set(params.shadowMap);
  280. mShadowSamplerParam.set(mSamplerState);
  281. mGBufferParams.bind(params.renderTargets);
  282. mParamsSet->setParamBlockBuffer("Params", params.shadowParams);
  283. mParamsSet->setParamBlockBuffer("PerCamera", params.perCamera);
  284. gRendererUtility().setPass(mMaterial);
  285. gRendererUtility().setPassParams(mParamsSet);
  286. }
  287. void ShadowProjectOmniMaterials::bind(UINT32 quality, bool directional, bool MSAA, const ShadowProjectParams& params)
  288. {
  289. #define BIND_MAT(QUALITY) \
  290. { \
  291. if(directional) \
  292. if (MSAA) \
  293. mMat##QUALITY##TT.bind(params); \
  294. else \
  295. mMat##QUALITY##TF.bind(params); \
  296. else \
  297. if (MSAA) \
  298. mMat##QUALITY##FT.bind(params); \
  299. else \
  300. mMat##QUALITY##FF.bind(params); \
  301. }
  302. if(quality <= 1)
  303. BIND_MAT(1)
  304. else if(quality == 2)
  305. BIND_MAT(2)
  306. else if(quality == 3)
  307. BIND_MAT(3)
  308. else // 4 or higher
  309. BIND_MAT(4)
  310. #undef BIND_MAT
  311. }
  312. void ShadowInfo::updateNormArea(UINT32 atlasSize)
  313. {
  314. normArea.x = area.x / (float)atlasSize;
  315. normArea.y = area.y / (float)atlasSize;
  316. normArea.width = area.width / (float)atlasSize;
  317. normArea.height = area.height / (float)atlasSize;
  318. }
  319. ShadowMapAtlas::ShadowMapAtlas(UINT32 size)
  320. :mLastUsedCounter(0)
  321. {
  322. mAtlas = GpuResourcePool::instance().get(
  323. POOLED_RENDER_TEXTURE_DESC::create2D(SHADOW_MAP_FORMAT, size, size, TU_DEPTHSTENCIL));
  324. }
  325. ShadowMapAtlas::~ShadowMapAtlas()
  326. {
  327. GpuResourcePool::instance().release(mAtlas);
  328. }
  329. bool ShadowMapAtlas::addMap(UINT32 size, Rect2I& area, UINT32 border)
  330. {
  331. UINT32 sizeWithBorder = size + border * 2;
  332. UINT32 x, y;
  333. if (!mLayout.addElement(sizeWithBorder, sizeWithBorder, x, y))
  334. return false;
  335. area.width = area.height = size;
  336. area.x = x + border;
  337. area.y = y + border;
  338. mLastUsedCounter = 0;
  339. return true;
  340. }
  341. void ShadowMapAtlas::clear()
  342. {
  343. mLayout.clear();
  344. mLastUsedCounter++;
  345. }
  346. bool ShadowMapAtlas::isEmpty() const
  347. {
  348. return mLayout.isEmpty();
  349. }
  350. SPtr<Texture> ShadowMapAtlas::getTexture() const
  351. {
  352. return mAtlas->texture;
  353. }
  354. SPtr<RenderTexture> ShadowMapAtlas::getTarget() const
  355. {
  356. return mAtlas->renderTexture;
  357. }
  358. ShadowMapBase::ShadowMapBase(UINT32 size)
  359. : mSize(size), mIsUsed(false), mLastUsedCounter (0)
  360. { }
  361. SPtr<Texture> ShadowMapBase::getTexture() const
  362. {
  363. return mShadowMap->texture;
  364. }
  365. ShadowCubemap::ShadowCubemap(UINT32 size)
  366. :ShadowMapBase(size)
  367. {
  368. mShadowMap = GpuResourcePool::instance().get(
  369. POOLED_RENDER_TEXTURE_DESC::createCube(SHADOW_MAP_FORMAT, size, size, TU_DEPTHSTENCIL));
  370. RENDER_TEXTURE_DESC rtDesc;
  371. rtDesc.depthStencilSurface.texture = mShadowMap->texture;
  372. rtDesc.depthStencilSurface.numFaces = 6;
  373. }
  374. ShadowCubemap::~ShadowCubemap()
  375. {
  376. GpuResourcePool::instance().release(mShadowMap);
  377. }
  378. SPtr<RenderTexture> ShadowCubemap::getTarget() const
  379. {
  380. return mShadowMap->renderTexture;
  381. }
  382. ShadowCascadedMap::ShadowCascadedMap(UINT32 size)
  383. :ShadowMapBase(size)
  384. {
  385. mShadowMap = GpuResourcePool::instance().get(POOLED_RENDER_TEXTURE_DESC::create2D(SHADOW_MAP_FORMAT, size, size,
  386. TU_DEPTHSTENCIL, 0, false, NUM_CASCADE_SPLITS));
  387. RENDER_TEXTURE_DESC rtDesc;
  388. rtDesc.depthStencilSurface.texture = mShadowMap->texture;
  389. rtDesc.depthStencilSurface.numFaces = 1;
  390. for (int i = 0; i < NUM_CASCADE_SPLITS; ++i)
  391. {
  392. rtDesc.depthStencilSurface.face = i;
  393. mTargets[i] = RenderTexture::create(rtDesc);
  394. }
  395. }
  396. ShadowCascadedMap::~ShadowCascadedMap()
  397. {
  398. GpuResourcePool::instance().release(mShadowMap);
  399. }
  400. SPtr<RenderTexture> ShadowCascadedMap::getTarget(UINT32 cascadeIdx) const
  401. {
  402. return mTargets[cascadeIdx];
  403. }
  404. const UINT32 ShadowRendering::MAX_ATLAS_SIZE = 8192;
  405. const UINT32 ShadowRendering::MAX_UNUSED_FRAMES = 60;
  406. const UINT32 ShadowRendering::MIN_SHADOW_MAP_SIZE = 32;
  407. const UINT32 ShadowRendering::SHADOW_MAP_FADE_SIZE = 64;
  408. const UINT32 ShadowRendering::SHADOW_MAP_BORDER = 4;
  409. const float ShadowRendering::CASCADE_FRACTION_FADE = 0.1f;
  410. ShadowRendering::ShadowRendering(UINT32 shadowMapSize)
  411. : mShadowMapSize(shadowMapSize)
  412. {
  413. SPtr<VertexDataDesc> vertexDesc = VertexDataDesc::create();
  414. vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
  415. mPositionOnlyVD = VertexDeclaration::create(vertexDesc);
  416. // Create plane index and vertex buffers
  417. {
  418. VERTEX_BUFFER_DESC vbDesc;
  419. vbDesc.numVerts = 8;
  420. vbDesc.usage = GBU_DYNAMIC;
  421. vbDesc.vertexSize = mPositionOnlyVD->getProperties().getVertexSize(0);
  422. mPlaneVB = VertexBuffer::create(vbDesc);
  423. INDEX_BUFFER_DESC ibDesc;
  424. ibDesc.indexType = IT_32BIT;
  425. ibDesc.numIndices = 12;
  426. mPlaneIB = IndexBuffer::create(ibDesc);
  427. UINT32 indices[] =
  428. {
  429. // Far plane, back facing
  430. 4, 7, 6,
  431. 4, 6, 5,
  432. // Near plane, front facing
  433. 0, 1, 2,
  434. 0, 2, 3
  435. };
  436. mPlaneIB->writeData(0, sizeof(indices), indices);
  437. }
  438. // Create frustum index and vertex buffers
  439. {
  440. VERTEX_BUFFER_DESC vbDesc;
  441. vbDesc.numVerts = 8;
  442. vbDesc.usage = GBU_DYNAMIC;
  443. vbDesc.vertexSize = mPositionOnlyVD->getProperties().getVertexSize(0);
  444. mFrustumVB = VertexBuffer::create(vbDesc);
  445. INDEX_BUFFER_DESC ibDesc;
  446. ibDesc.indexType = IT_32BIT;
  447. ibDesc.numIndices = 36;
  448. mFrustumIB = IndexBuffer::create(ibDesc);
  449. mFrustumIB->writeData(0, sizeof(AABox::CUBE_INDICES), AABox::CUBE_INDICES);
  450. }
  451. }
  452. void ShadowRendering::setShadowMapSize(UINT32 size)
  453. {
  454. if (mShadowMapSize == size)
  455. return;
  456. mCascadedShadowMaps.clear();
  457. mDynamicShadowMaps.clear();
  458. mShadowCubemaps.clear();
  459. }
  460. void ShadowRendering::renderShadowMaps(RendererScene& scene, const RendererViewGroup& viewGroup,
  461. const FrameInfo& frameInfo)
  462. {
  463. // Note: Currently all shadows are dynamic and are rebuilt every frame. I should later added support for static
  464. // shadow maps which can be used for immovable lights. Such a light can then maintain a set of shadow maps,
  465. // one of which is static and only effects the static geometry, while the rest are per-object shadow maps used
  466. // for dynamic objects. Then only a small subset of geometry needs to be redrawn, instead of everything.
  467. // Note: Add support for per-object shadows and a way to force a renderable to use per-object shadows. This can be
  468. // used for adding high quality shadows on specific objects (e.g. important characters during cinematics).
  469. const SceneInfo& sceneInfo = scene.getSceneInfo();
  470. const VisibilityInfo& visibility = viewGroup.getVisibilityInfo();
  471. // Clear all transient data from last frame
  472. mShadowInfos.clear();
  473. mSpotLightShadows.resize(sceneInfo.spotLights.size());
  474. mRadialLightShadows.resize(sceneInfo.radialLights.size());
  475. mDirectionalLightShadows.resize(sceneInfo.directionalLights.size());
  476. mSpotLightShadowOptions.clear();
  477. mRadialLightShadowOptions.clear();
  478. // Clear all dynamic light atlases
  479. for (auto& entry : mCascadedShadowMaps)
  480. entry.clear();
  481. for (auto& entry : mDynamicShadowMaps)
  482. entry.clear();
  483. for (auto& entry : mShadowCubemaps)
  484. entry.clear();
  485. // Determine shadow map sizes and sort them
  486. UINT32 shadowInfoCount = 0;
  487. for (UINT32 i = 0; i < (UINT32)sceneInfo.spotLights.size(); ++i)
  488. {
  489. const RendererLight& light = sceneInfo.spotLights[i];
  490. // Note: I'm using visibility across all views, while I could be using visibility for every view individually,
  491. // if I kept that information somewhere
  492. if (!light.internal->getCastsShadow() || !visibility.spotLights[i])
  493. continue;
  494. ShadowMapOptions options;
  495. options.lightIdx = i;
  496. float maxFadePercent;
  497. calcShadowMapProperties(light, scene, options.mapSize, options.fadePercents, maxFadePercent);
  498. // Don't render shadow maps that will end up nearly completely faded out
  499. if (maxFadePercent < 0.005f)
  500. continue;
  501. mSpotLightShadowOptions.push_back(options);
  502. mSpotLightShadows[i].startIdx = shadowInfoCount;
  503. mSpotLightShadows[i].numShadows = 0;
  504. shadowInfoCount++; // For now, always a single fully dynamic shadow for a single light, but that may change
  505. }
  506. for (UINT32 i = 0; i < (UINT32)sceneInfo.radialLights.size(); ++i)
  507. {
  508. const RendererLight& light = sceneInfo.radialLights[i];
  509. // Note: I'm using visibility across all views, while I could be using visibility for every view individually,
  510. // if I kept that information somewhere
  511. if (!light.internal->getCastsShadow() || !visibility.radialLights[i])
  512. continue;
  513. ShadowMapOptions options;
  514. options.lightIdx = i;
  515. float maxFadePercent;
  516. calcShadowMapProperties(light, scene, options.mapSize, options.fadePercents, maxFadePercent);
  517. // Don't render shadow maps that will end up nearly completely faded out
  518. if (maxFadePercent < 0.005f)
  519. continue;
  520. mRadialLightShadowOptions.push_back(options);
  521. mRadialLightShadows[i].startIdx = shadowInfoCount;
  522. mRadialLightShadows[i].numShadows = 0;
  523. shadowInfoCount++; // For now, always a single fully dynamic shadow for a single light, but that may change
  524. }
  525. // Sort spot lights by size so they fit neatly in the texture atlas
  526. std::sort(mSpotLightShadowOptions.begin(), mSpotLightShadowOptions.end(),
  527. [](const ShadowMapOptions& a, const ShadowMapOptions& b) { return a.mapSize > b.mapSize; } );
  528. // Reserve space for shadow infos
  529. mShadowInfos.resize(shadowInfoCount);
  530. // Render shadow maps
  531. for (UINT32 i = 0; i < (UINT32)sceneInfo.directionalLights.size(); ++i)
  532. {
  533. const RendererLight& light = sceneInfo.directionalLights[i];
  534. if (!light.internal->getCastsShadow())
  535. return;
  536. for (UINT32 j = 0; j < (UINT32)sceneInfo.views.size(); ++j)
  537. renderCascadedShadowMaps(j, i, scene, frameInfo);
  538. }
  539. for(auto& entry : mSpotLightShadowOptions)
  540. {
  541. UINT32 lightIdx = entry.lightIdx;
  542. renderSpotShadowMap(sceneInfo.spotLights[lightIdx], entry, scene, frameInfo);
  543. }
  544. for (auto& entry : mRadialLightShadowOptions)
  545. {
  546. UINT32 lightIdx = entry.lightIdx;
  547. renderRadialShadowMap(sceneInfo.radialLights[lightIdx], entry, scene, frameInfo);
  548. }
  549. // Deallocate unused textures
  550. for(auto iter = mDynamicShadowMaps.begin(); iter != mDynamicShadowMaps.end(); ++iter)
  551. {
  552. if(iter->getLastUsedCounter() >= MAX_UNUSED_FRAMES)
  553. {
  554. // These are always populated in order, so we can assume all following atlases are also empty
  555. mDynamicShadowMaps.erase(iter, mDynamicShadowMaps.end());
  556. break;
  557. }
  558. }
  559. for(auto iter = mCascadedShadowMaps.begin(); iter != mCascadedShadowMaps.end();)
  560. {
  561. if (iter->getLastUsedCounter() >= MAX_UNUSED_FRAMES)
  562. iter = mCascadedShadowMaps.erase(iter);
  563. else
  564. ++iter;
  565. }
  566. for(auto iter = mShadowCubemaps.begin(); iter != mShadowCubemaps.end();)
  567. {
  568. if (iter->getLastUsedCounter() >= MAX_UNUSED_FRAMES)
  569. iter = mShadowCubemaps.erase(iter);
  570. else
  571. ++iter;
  572. }
  573. }
  574. /**
  575. * Generates a frustum from the provided view-projection matrix.
  576. *
  577. * @param[in] invVP Inverse of the view-projection matrix to use for generating the frustum.
  578. * @param[out] worldFrustum Generated frustum planes, in world space.
  579. * @return Individual vertices of the frustum corners, in world space. Ordered using the
  580. * AABox::CornerEnum.
  581. */
  582. std::array<Vector3, 8> getFrustum(const Matrix4& invVP, ConvexVolume& worldFrustum)
  583. {
  584. std::array<Vector3, 8> output;
  585. RenderAPI& rapi = RenderAPI::instance();
  586. const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
  587. AABox frustumCube(
  588. Vector3(-1, -1, rapiInfo.getMinimumDepthInputValue()),
  589. Vector3(1, 1, rapiInfo.getMaximumDepthInputValue())
  590. );
  591. for(size_t i = 0; i < output.size(); i++)
  592. {
  593. Vector3 corner = frustumCube.getCorner((AABox::Corner)i);
  594. output[i] = invVP.multiply(corner);
  595. }
  596. Vector<Plane> planes(6);
  597. planes[FRUSTUM_PLANE_NEAR] = Plane(output[AABox::NEAR_LEFT_BOTTOM], output[AABox::NEAR_RIGHT_BOTTOM], output[AABox::NEAR_RIGHT_TOP]);
  598. planes[FRUSTUM_PLANE_FAR] = Plane(output[AABox::FAR_LEFT_BOTTOM], output[AABox::FAR_LEFT_TOP], output[AABox::FAR_RIGHT_TOP]);
  599. planes[FRUSTUM_PLANE_LEFT] = Plane(output[AABox::NEAR_LEFT_BOTTOM], output[AABox::NEAR_LEFT_TOP], output[AABox::FAR_LEFT_TOP]);
  600. planes[FRUSTUM_PLANE_RIGHT] = Plane(output[AABox::FAR_RIGHT_TOP], output[AABox::NEAR_RIGHT_TOP], output[AABox::NEAR_RIGHT_BOTTOM]);
  601. planes[FRUSTUM_PLANE_TOP] = Plane(output[AABox::NEAR_LEFT_TOP], output[AABox::NEAR_RIGHT_TOP], output[AABox::FAR_RIGHT_TOP]);
  602. planes[FRUSTUM_PLANE_BOTTOM] = Plane(output[AABox::NEAR_LEFT_BOTTOM], output[AABox::FAR_LEFT_BOTTOM], output[AABox::FAR_RIGHT_BOTTOM]);
  603. worldFrustum = ConvexVolume(planes);
  604. return output;
  605. }
  606. void ShadowRendering::renderShadowOcclusion(const RendererScene& scene, UINT32 shadowQuality,
  607. const RendererLight& rendererLight, UINT32 viewIdx)
  608. {
  609. const Light* light = rendererLight.internal;
  610. UINT32 lightIdx = light->getRendererId();
  611. RendererView* view = scene.getSceneInfo().views[viewIdx];
  612. auto viewProps = view->getProperties();
  613. const Matrix4& viewP = viewProps.projTransform;
  614. Matrix4 viewInvVP = viewProps.viewProjTransform.inverse();
  615. SPtr<GpuParamBlockBuffer> perViewBuffer = view->getPerViewBuffer();
  616. RenderAPI& rapi = RenderAPI::instance();
  617. // TODO - Calculate and set a scissor rectangle for the light
  618. SPtr<GpuParamBlockBuffer> shadowParamBuffer = gShadowProjectParamsDef.createBuffer();
  619. SPtr<GpuParamBlockBuffer> shadowOmniParamBuffer = gShadowProjectOmniParamsDef.createBuffer();
  620. Vector<const ShadowInfo*> shadowInfos;
  621. if(light->getType() == LightType::Radial)
  622. {
  623. const LightShadows& shadows = mRadialLightShadows[lightIdx];
  624. for(UINT32 i = 0; i < shadows.numShadows; ++i)
  625. {
  626. UINT32 shadowIdx = shadows.startIdx + i;
  627. const ShadowInfo& shadowInfo = mShadowInfos[shadowIdx];
  628. if (shadowInfo.fadePerView[viewIdx] < 0.005f)
  629. continue;
  630. for(UINT32 j = 0; j < 6; j++)
  631. gShadowProjectOmniParamsDef.gFaceVPMatrices.set(shadowOmniParamBuffer, shadowInfo.shadowVPTransforms[j], j);
  632. gShadowProjectOmniParamsDef.gDepthBias.set(shadowOmniParamBuffer, shadowInfo.depthBias);
  633. gShadowProjectOmniParamsDef.gFadePercent.set(shadowOmniParamBuffer, shadowInfo.fadePerView[viewIdx]);
  634. gShadowProjectOmniParamsDef.gInvResolution.set(shadowOmniParamBuffer, 1.0f / shadowInfo.area.width);
  635. Vector4 lightPosAndRadius(light->getPosition(), light->getAttenuationRadius());
  636. gShadowProjectOmniParamsDef.gLightPosAndRadius.set(shadowOmniParamBuffer, lightPosAndRadius);
  637. // Reduce shadow quality based on shadow map resolution for spot lights
  638. UINT32 effectiveShadowQuality = getShadowQuality(shadowQuality, shadowInfo.area.width, 2);
  639. // Check if viewer is inside the light bounds
  640. //// Expand the light bounds slightly to handle the case when the near plane is intersecting the light volume
  641. float lightRadius = light->getAttenuationRadius() + viewProps.nearPlane * 3.0f;
  642. bool viewerInsideVolume = (light->getPosition() - viewProps.viewOrigin).length() < lightRadius;
  643. SPtr<Texture> shadowMap = mShadowCubemaps[shadowInfo.textureIdx].getTexture();
  644. SPtr<RenderTargets> renderTargets = view->getRenderTargets();
  645. ShadowProjectParams shadowParams(*light, shadowMap, 0, shadowParamBuffer, perViewBuffer, *renderTargets);
  646. mProjectOmniMaterials.bind(effectiveShadowQuality, viewerInsideVolume, viewProps.numSamples > 1, shadowParams);
  647. gRendererUtility().draw(gRendererUtility().getRadialLightStencil());
  648. }
  649. }
  650. else // Directional & spot
  651. {
  652. shadowInfos.clear();
  653. bool isCSM = light->getType() == LightType::Directional;
  654. if(!isCSM)
  655. {
  656. const LightShadows& shadows = mSpotLightShadows[lightIdx];
  657. for (UINT32 i = 0; i < shadows.numShadows; ++i)
  658. {
  659. UINT32 shadowIdx = shadows.startIdx + i;
  660. const ShadowInfo& shadowInfo = mShadowInfos[shadowIdx];
  661. if (shadowInfo.fadePerView[viewIdx] < 0.005f)
  662. continue;
  663. shadowInfos.push_back(&shadowInfo);
  664. }
  665. }
  666. else // Directional
  667. {
  668. UINT32 mapIdx = mDirectionalLightShadows[lightIdx];
  669. ShadowCascadedMap& cascadedMap = mCascadedShadowMaps[mapIdx];
  670. // Render cascades in far to near order.
  671. // Note: If rendering other non-cascade maps they should be rendered after cascades.
  672. for (UINT32 i = NUM_CASCADE_SPLITS; i >= 0; i--)
  673. shadowInfos.push_back(&cascadedMap.getShadowInfo(i));
  674. }
  675. for(auto& shadowInfo : shadowInfos)
  676. {
  677. Matrix4 mixedToShadowUV = createMixedToShadowUVMatrix(viewP, viewInvVP, shadowInfo->normArea,
  678. shadowInfo->depthRange, shadowInfo->shadowVPTransform);
  679. Vector2 shadowMapSize((float)shadowInfo->area.width, (float)shadowInfo->area.height);
  680. float transitionScale = getFadeTransition(*light, shadowInfo->depthRange, shadowInfo->area.width);
  681. gShadowProjectParamsDef.gFadePercent.set(shadowParamBuffer, shadowInfo->fadePerView[viewIdx]);
  682. gShadowProjectParamsDef.gFadePlaneDepth.set(shadowParamBuffer, shadowInfo->depthFade);
  683. gShadowProjectParamsDef.gInvFadePlaneRange.set(shadowParamBuffer, 1.0f / shadowInfo->fadeRange);
  684. gShadowProjectParamsDef.gMixedToShadowSpace.set(shadowParamBuffer, mixedToShadowUV);
  685. gShadowProjectParamsDef.gShadowMapSize.set(shadowParamBuffer, shadowMapSize);
  686. gShadowProjectParamsDef.gShadowMapSizeInv.set(shadowParamBuffer, 1.0f / shadowMapSize);
  687. gShadowProjectParamsDef.gSoftTransitionScale.set(shadowParamBuffer, transitionScale);
  688. // Generate a stencil buffer to avoid evaluating pixels without any receiver geometry in the shadow area
  689. std::array<Vector3, 8> frustumVertices;
  690. UINT32 effectiveShadowQuality = shadowQuality;
  691. if(!isCSM)
  692. {
  693. ConvexVolume shadowFrustum;
  694. frustumVertices = getFrustum(shadowInfo->shadowVPTransform.inverse(), shadowFrustum);
  695. // Check if viewer is inside the frustum. Frustum is slightly expanded so that if the near plane is
  696. // intersecting the shadow frustum, it is counted as inside. This needs to be conservative as the code
  697. // for handling viewer outside the frustum will not properly render intersections with the near plane.
  698. bool viewerInsideFrustum = shadowFrustum.contains(viewProps.viewOrigin, viewProps.nearPlane * 3.0f);
  699. mProjectStencilMaterials.bind(false, viewerInsideFrustum, perViewBuffer);
  700. drawFrustum(frustumVertices);
  701. // Reduce shadow quality based on shadow map resolution for spot lights
  702. effectiveShadowQuality = getShadowQuality(shadowQuality, shadowInfo->area.width, 2);
  703. }
  704. else
  705. {
  706. // Need to generate near and far planes to clip the geometry within the current CSM slice.
  707. // Note: If the render API supports built-in depth bound tests that could be used instead.
  708. Vector3 near = viewProps.projTransform.multiply(Vector3(0, 0, shadowInfo->depthNear));
  709. Vector3 far = viewProps.projTransform.multiply(Vector3(0, 0, shadowInfo->depthFar));
  710. mProjectStencilMaterials.bind(true, true, perViewBuffer);
  711. drawNearFarPlanes(near.z, far.z, shadowInfo->cascadeIdx != 0);
  712. }
  713. SPtr<Texture> shadowMap;
  714. UINT32 shadowMapFace = 0;
  715. if(!isCSM)
  716. shadowMap = mDynamicShadowMaps[shadowInfo->textureIdx].getTexture();
  717. else
  718. {
  719. shadowMap = mCascadedShadowMaps[shadowInfo->textureIdx].getTexture();
  720. shadowMapFace = shadowInfo->cascadeIdx;
  721. }
  722. SPtr<RenderTargets> renderTargets = view->getRenderTargets();
  723. ShadowProjectParams shadowParams(*light, shadowMap, shadowMapFace, shadowParamBuffer, perViewBuffer,
  724. *renderTargets);
  725. mProjectMaterials.bind(effectiveShadowQuality, isCSM, viewProps.numSamples > 1, shadowParams);
  726. if(!isCSM)
  727. drawFrustum(frustumVertices);
  728. else
  729. gRendererUtility().drawScreenQuad();
  730. }
  731. }
  732. }
  733. void ShadowRendering::renderCascadedShadowMaps(UINT32 viewIdx, UINT32 lightIdx, RendererScene& scene,
  734. const FrameInfo& frameInfo)
  735. {
  736. // Note: Currently I'm using spherical bounds for the cascaded frustum which might result in non-optimal usage
  737. // of the shadow map. A different approach would be to generate a bounding box and then both adjust the aspect
  738. // ratio (and therefore dimensions) of the shadow map, as well as rotate the camera so the visible area best fits
  739. // in the map. It remains to be seen if this is viable.
  740. const SceneInfo& sceneInfo = scene.getSceneInfo();
  741. const RendererView* view = sceneInfo.views[viewIdx];
  742. const RendererLight& rendererLight = sceneInfo.directionalLights[lightIdx];
  743. Light* light = rendererLight.internal;
  744. RenderAPI& rapi = RenderAPI::instance();
  745. Vector3 lightDir = light->getRotation().zAxis();
  746. SPtr<GpuParamBlockBuffer> shadowParamsBuffer = gShadowParamsDef.createBuffer();
  747. ShadowInfo shadowInfo;
  748. shadowInfo.lightIdx = lightIdx;
  749. shadowInfo.textureIdx = -1;
  750. UINT32 mapSize = std::min(mShadowMapSize, MAX_ATLAS_SIZE);
  751. for (UINT32 i = 0; i < (UINT32)mCascadedShadowMaps.size(); i++)
  752. {
  753. ShadowCascadedMap& shadowMap = mCascadedShadowMaps[i];
  754. if (!shadowMap.isUsed() && shadowMap.getSize() == mapSize)
  755. {
  756. shadowInfo.textureIdx = i;
  757. shadowMap.markAsUsed();
  758. break;
  759. }
  760. }
  761. if (shadowInfo.textureIdx == -1)
  762. {
  763. shadowInfo.textureIdx = (UINT32)mCascadedShadowMaps.size();
  764. mCascadedShadowMaps.push_back(ShadowCascadedMap(mapSize));
  765. ShadowCascadedMap& shadowMap = mCascadedShadowMaps.back();
  766. shadowMap.markAsUsed();
  767. }
  768. ShadowCascadedMap& shadowMap = mCascadedShadowMaps[shadowInfo.textureIdx];
  769. Matrix4 viewMat = Matrix4::view(light->getPosition(), light->getRotation());
  770. for (int i = 0; i < NUM_CASCADE_SPLITS; ++i)
  771. {
  772. Sphere frustumBounds;
  773. ConvexVolume cascadeCullVolume = getCSMSplitFrustum(*view, -lightDir, i, NUM_CASCADE_SPLITS, frustumBounds);
  774. float orthoSize = frustumBounds.getRadius();
  775. Matrix4 proj = Matrix4::projectionOrthographic(-orthoSize, orthoSize, -orthoSize, orthoSize, 0.0f, 1000.0f);
  776. RenderAPI::instance().convertProjectionMatrix(proj, proj);
  777. shadowInfo.cascadeIdx = i;
  778. shadowInfo.shadowVPTransform = proj * viewMat;
  779. // Determine split range
  780. float splitNear = getCSMSplitDistance(*view, i, NUM_CASCADE_SPLITS);
  781. float splitFar = getCSMSplitDistance(*view, i + 1, NUM_CASCADE_SPLITS);
  782. shadowInfo.depthNear = splitNear;
  783. shadowInfo.depthFade = splitFar;
  784. if ((i + 1) < NUM_CASCADE_SPLITS)
  785. shadowInfo.fadeRange = CASCADE_FRACTION_FADE * (shadowInfo.depthFade - shadowInfo.depthNear);
  786. else
  787. shadowInfo.fadeRange = 0.0f;
  788. shadowInfo.depthFar = shadowInfo.depthFade + shadowInfo.fadeRange;
  789. shadowInfo.depthRange = shadowInfo.depthFar - shadowInfo.depthNear;
  790. shadowInfo.depthBias = getDepthBias(*light, shadowInfo.depthRange, mapSize);
  791. gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, shadowInfo.depthBias);
  792. gShadowParamsDef.gInvDepthRange.set(shadowParamsBuffer, 1.0f / shadowInfo.depthRange);
  793. gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, shadowInfo.shadowVPTransform);
  794. gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
  795. rapi.setRenderTarget(shadowMap.getTarget(i));
  796. rapi.clearRenderTarget(FBT_DEPTH);
  797. mDepthDirectionalMat.bind(shadowParamsBuffer);
  798. for (UINT32 j = 0; j < sceneInfo.renderables.size(); j++)
  799. {
  800. if (!cascadeCullVolume.intersects(sceneInfo.renderableCullInfos[j].bounds.getSphere()))
  801. continue;
  802. scene.prepareRenderable(j, frameInfo);
  803. RendererObject* renderable = sceneInfo.renderables[j];
  804. mDepthDirectionalMat.setPerObjectBuffer(renderable->perObjectParamBuffer);
  805. for (auto& element : renderable->elements)
  806. {
  807. if (element.morphVertexDeclaration == nullptr)
  808. gRendererUtility().draw(element.mesh, element.subMesh);
  809. else
  810. gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer,
  811. element.morphVertexDeclaration);
  812. }
  813. }
  814. shadowMap.setShadowInfo(i, shadowInfo);
  815. }
  816. mDirectionalLightShadows[lightIdx] = shadowInfo.textureIdx;
  817. }
  818. void ShadowRendering::renderSpotShadowMap(const RendererLight& rendererLight, const ShadowMapOptions& options,
  819. RendererScene& scene, const FrameInfo& frameInfo)
  820. {
  821. Light* light = rendererLight.internal;
  822. const SceneInfo& sceneInfo = scene.getSceneInfo();
  823. SPtr<GpuParamBlockBuffer> shadowParamsBuffer = gShadowParamsDef.createBuffer();
  824. ShadowInfo mapInfo;
  825. mapInfo.fadePerView = options.fadePercents;
  826. mapInfo.lightIdx = options.lightIdx;
  827. mapInfo.cascadeIdx = -1;
  828. bool foundSpace = false;
  829. for (UINT32 i = 0; i < (UINT32)mDynamicShadowMaps.size(); i++)
  830. {
  831. ShadowMapAtlas& atlas = mDynamicShadowMaps[i];
  832. if (atlas.addMap(options.mapSize, mapInfo.area, SHADOW_MAP_BORDER))
  833. {
  834. mapInfo.textureIdx = i;
  835. foundSpace = true;
  836. break;
  837. }
  838. }
  839. if (!foundSpace)
  840. {
  841. mapInfo.textureIdx = (UINT32)mDynamicShadowMaps.size();
  842. mDynamicShadowMaps.push_back(ShadowMapAtlas(MAX_ATLAS_SIZE));
  843. ShadowMapAtlas& atlas = mDynamicShadowMaps.back();
  844. atlas.addMap(options.mapSize, mapInfo.area, SHADOW_MAP_BORDER);
  845. }
  846. mapInfo.updateNormArea(MAX_ATLAS_SIZE);
  847. ShadowMapAtlas& atlas = mDynamicShadowMaps[mapInfo.textureIdx];
  848. RenderAPI& rapi = RenderAPI::instance();
  849. rapi.setRenderTarget(atlas.getTarget());
  850. rapi.setViewport(mapInfo.normArea);
  851. rapi.clearViewport(FBT_DEPTH);
  852. mapInfo.depthNear = 0.05f;
  853. mapInfo.depthFar = light->getAttenuationRadius();
  854. mapInfo.depthFade = mapInfo.depthFar;
  855. mapInfo.fadeRange = 0.0f;
  856. mapInfo.depthRange = mapInfo.depthFar - mapInfo.depthNear;
  857. mapInfo.depthBias = getDepthBias(*light, mapInfo.depthRange, options.mapSize);
  858. Matrix4 view = Matrix4::view(light->getPosition(), light->getRotation());
  859. Matrix4 proj = Matrix4::projectionPerspective(light->getSpotAngle(), 1.0f, 0.05f, light->getAttenuationRadius());
  860. RenderAPI::instance().convertProjectionMatrix(proj, proj);
  861. mapInfo.shadowVPTransform = proj * view;
  862. gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, mapInfo.depthBias);
  863. gShadowParamsDef.gInvDepthRange.set(shadowParamsBuffer, 1.0f / mapInfo.depthRange);
  864. gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, mapInfo.shadowVPTransform);
  865. gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
  866. mDepthNormalMat.bind(shadowParamsBuffer);
  867. ConvexVolume localFrustum = ConvexVolume(proj);
  868. const Vector<Plane>& frustumPlanes = localFrustum.getPlanes();
  869. Matrix4 worldMatrix = view.transpose();
  870. Vector<Plane> worldPlanes(frustumPlanes.size());
  871. UINT32 j = 0;
  872. for (auto& plane : frustumPlanes)
  873. {
  874. worldPlanes[j] = worldMatrix.multiplyAffine(plane);
  875. j++;
  876. }
  877. ConvexVolume worldFrustum(worldPlanes);
  878. for (UINT32 i = 0; i < sceneInfo.renderables.size(); i++)
  879. {
  880. if (!worldFrustum.intersects(sceneInfo.renderableCullInfos[i].bounds.getSphere()))
  881. continue;
  882. scene.prepareRenderable(i, frameInfo);
  883. RendererObject* renderable = sceneInfo.renderables[i];
  884. mDepthNormalMat.setPerObjectBuffer(renderable->perObjectParamBuffer);
  885. for (auto& element : renderable->elements)
  886. {
  887. if (element.morphVertexDeclaration == nullptr)
  888. gRendererUtility().draw(element.mesh, element.subMesh);
  889. else
  890. gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer,
  891. element.morphVertexDeclaration);
  892. }
  893. }
  894. // Restore viewport
  895. rapi.setViewport(Rect2(0.0f, 0.0f, 1.0f, 1.0f));
  896. LightShadows& lightShadows = mSpotLightShadows[options.lightIdx];
  897. mShadowInfos[lightShadows.startIdx + lightShadows.numShadows] = mapInfo;
  898. lightShadows.numShadows++;
  899. }
  900. void ShadowRendering::renderRadialShadowMap(const RendererLight& rendererLight,
  901. const ShadowMapOptions& options, RendererScene& scene, const FrameInfo& frameInfo)
  902. {
  903. Light* light = rendererLight.internal;
  904. const SceneInfo& sceneInfo = scene.getSceneInfo();
  905. SPtr<GpuParamBlockBuffer> shadowParamsBuffer = gShadowParamsDef.createBuffer();
  906. SPtr<GpuParamBlockBuffer> shadowCubeMatricesBuffer = gShadowCubeMatricesDef.createBuffer();
  907. SPtr<GpuParamBlockBuffer> shadowCubeMasksBuffer = gShadowCubeMasksDef.createBuffer();
  908. ShadowInfo mapInfo;
  909. mapInfo.lightIdx = options.lightIdx;
  910. mapInfo.textureIdx = -1;
  911. mapInfo.fadePerView = options.fadePercents;
  912. mapInfo.cascadeIdx = -1;
  913. for (UINT32 i = 0; i < (UINT32)mShadowCubemaps.size(); i++)
  914. {
  915. ShadowCubemap& cubemap = mShadowCubemaps[i];
  916. if (!cubemap.isUsed() && cubemap.getSize() == options.mapSize)
  917. {
  918. mapInfo.textureIdx = i;
  919. cubemap.markAsUsed();
  920. break;
  921. }
  922. }
  923. if (mapInfo.textureIdx == -1)
  924. {
  925. mapInfo.textureIdx = (UINT32)mShadowCubemaps.size();
  926. mShadowCubemaps.push_back(ShadowCubemap(options.mapSize));
  927. ShadowCubemap& cubemap = mShadowCubemaps.back();
  928. cubemap.markAsUsed();
  929. }
  930. ShadowCubemap& cubemap = mShadowCubemaps[mapInfo.textureIdx];
  931. mapInfo.depthNear = 0.05f;
  932. mapInfo.depthFar = light->getAttenuationRadius();
  933. mapInfo.depthFade = mapInfo.depthFar;
  934. mapInfo.fadeRange = 0.0f;
  935. mapInfo.depthRange = mapInfo.depthFar - mapInfo.depthNear;
  936. mapInfo.depthBias = getDepthBias(*light, mapInfo.depthRange, options.mapSize);
  937. Matrix4 proj = Matrix4::projectionPerspective(Degree(90.0f), 1.0f, 0.05f, light->getAttenuationRadius());
  938. RenderAPI::instance().convertProjectionMatrix(proj, proj);
  939. ConvexVolume localFrustum(proj);
  940. gShadowParamsDef.gDepthBias.set(shadowParamsBuffer, mapInfo.depthBias);
  941. gShadowParamsDef.gInvDepthRange.set(shadowParamsBuffer, 1.0f / mapInfo.depthRange);
  942. gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, Matrix4::IDENTITY);
  943. gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
  944. Matrix4 viewOffsetMat = Matrix4::translation(-light->getPosition());
  945. ConvexVolume frustums[6];
  946. Vector<Plane> boundingPlanes;
  947. for (UINT32 i = 0; i < 6; i++)
  948. {
  949. // Calculate view matrix
  950. Vector3 forward;
  951. Vector3 up = Vector3::UNIT_Y;
  952. switch (i)
  953. {
  954. case CF_PositiveX:
  955. forward = Vector3::UNIT_X;
  956. break;
  957. case CF_NegativeX:
  958. forward = -Vector3::UNIT_X;
  959. break;
  960. case CF_PositiveY:
  961. forward = Vector3::UNIT_Y;
  962. up = -Vector3::UNIT_Z;
  963. break;
  964. case CF_NegativeY:
  965. forward = Vector3::UNIT_X;
  966. up = Vector3::UNIT_Z;
  967. break;
  968. case CF_PositiveZ:
  969. forward = Vector3::UNIT_Z;
  970. break;
  971. case CF_NegativeZ:
  972. forward = -Vector3::UNIT_Z;
  973. break;
  974. }
  975. Vector3 right = Vector3::cross(up, forward);
  976. Matrix3 viewRotationMat = Matrix3(right, up, forward);
  977. Matrix4 view = Matrix4(viewRotationMat) * viewOffsetMat;
  978. mapInfo.shadowVPTransforms[i] = proj * view;
  979. gShadowCubeMatricesDef.gFaceVPMatrices.set(shadowCubeMatricesBuffer, mapInfo.shadowVPTransforms[i], i);
  980. // Calculate world frustum for culling
  981. const Vector<Plane>& frustumPlanes = localFrustum.getPlanes();
  982. Matrix4 worldMatrix = view.transpose();
  983. Vector<Plane> worldPlanes(frustumPlanes.size());
  984. UINT32 j = 0;
  985. for (auto& plane : frustumPlanes)
  986. {
  987. worldPlanes[j] = worldMatrix.multiplyAffine(plane);
  988. j++;
  989. }
  990. frustums[i] = ConvexVolume(worldPlanes);
  991. // Register far plane of all frustums
  992. boundingPlanes.push_back(worldPlanes.back());
  993. }
  994. RenderAPI& rapi = RenderAPI::instance();
  995. rapi.setRenderTarget(cubemap.getTarget());
  996. rapi.clearRenderTarget(FBT_DEPTH);
  997. mDepthCubeMat.bind(shadowParamsBuffer, shadowCubeMatricesBuffer);
  998. // First cull against a global volume
  999. ConvexVolume boundingVolume(boundingPlanes);
  1000. for (UINT32 i = 0; i < sceneInfo.renderables.size(); i++)
  1001. {
  1002. const Sphere& bounds = sceneInfo.renderableCullInfos[i].bounds.getSphere();
  1003. if (!boundingVolume.intersects(bounds))
  1004. continue;
  1005. scene.prepareRenderable(i, frameInfo);
  1006. for(UINT32 j = 0; j < 6; j++)
  1007. {
  1008. int mask = frustums->intersects(bounds) ? 1 : 0;
  1009. gShadowCubeMasksDef.gFaceMasks.set(shadowCubeMasksBuffer, mask, j);
  1010. }
  1011. RendererObject* renderable = sceneInfo.renderables[i];
  1012. mDepthCubeMat.setPerObjectBuffer(renderable->perObjectParamBuffer, shadowCubeMasksBuffer);
  1013. for (auto& element : renderable->elements)
  1014. {
  1015. if (element.morphVertexDeclaration == nullptr)
  1016. gRendererUtility().draw(element.mesh, element.subMesh);
  1017. else
  1018. gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer,
  1019. element.morphVertexDeclaration);
  1020. }
  1021. }
  1022. LightShadows& lightShadows = mRadialLightShadows[options.lightIdx];
  1023. mShadowInfos[lightShadows.startIdx + lightShadows.numShadows] = mapInfo;
  1024. lightShadows.numShadows++;
  1025. }
  1026. void ShadowRendering::calcShadowMapProperties(const RendererLight& light, RendererScene& scene, UINT32& size,
  1027. SmallVector<float, 4>& fadePercents, float& maxFadePercent) const
  1028. {
  1029. const SceneInfo& sceneInfo = scene.getSceneInfo();
  1030. // Find a view in which the light has the largest radius
  1031. float maxRadiusPercent = 0.0f;
  1032. maxFadePercent = 0.0f;
  1033. for (int i = 0; i < (int)sceneInfo.views.size(); ++i)
  1034. {
  1035. const RendererViewProperties& viewProps = sceneInfo.views[i]->getProperties();
  1036. float viewScaleX = viewProps.projTransform[0][0] * 0.5f;
  1037. float viewScaleY = viewProps.projTransform[1][1] * 0.5f;
  1038. float viewScale = std::max(viewScaleX, viewScaleY);
  1039. const Matrix4& viewVP = viewProps.viewProjTransform;
  1040. Vector4 lightClipPos = viewVP.multiply(Vector4(light.internal->getPosition(), 1.0f));
  1041. float radiusNDC = light.internal->getBounds().getRadius() / std::max(lightClipPos.w, 1.0f);
  1042. // Radius of light bounds in percent of the view surface
  1043. float radiusPercent = radiusNDC * viewScale;
  1044. maxRadiusPercent = std::max(maxRadiusPercent, radiusPercent);
  1045. float optimalMapSize = mShadowMapSize * radiusPercent;
  1046. // Determine if the shadow should fade out
  1047. float fadePercent = Math::lerp01(optimalMapSize, (float)MIN_SHADOW_MAP_SIZE, (float)SHADOW_MAP_FADE_SIZE);
  1048. fadePercents.push_back(fadePercent);
  1049. maxFadePercent = std::max(maxFadePercent, fadePercent);
  1050. }
  1051. // If light fully (or nearly fully) covers the screen, use full shadow map resolution, otherwise
  1052. // scale it down to smaller power of two, while clamping to minimal allowed resolution
  1053. float maxOptimalMapSize = mShadowMapSize * maxRadiusPercent;
  1054. UINT32 effectiveMapSize = Bitwise::nextPow2((UINT32)maxOptimalMapSize);
  1055. effectiveMapSize = Math::clamp(effectiveMapSize, MIN_SHADOW_MAP_SIZE, mShadowMapSize);
  1056. // Leave room for border
  1057. size = std::max(effectiveMapSize - 2 * SHADOW_MAP_BORDER, 1u);
  1058. }
  1059. void ShadowRendering::drawNearFarPlanes(float near, float far, bool drawNear)
  1060. {
  1061. RenderAPI& rapi = RenderAPI::instance();
  1062. const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
  1063. float flipY = rapiInfo.isFlagSet(RenderAPIFeatureFlag::NDCYAxisDown) ? -1.0f : 1.0f;
  1064. // Update VB with new vertices
  1065. Vector3 vertices[8] =
  1066. {
  1067. // Near plane
  1068. { -1.0f, -1.0f * flipY, near },
  1069. { 1.0f, -1.0f * flipY, near },
  1070. { 1.0f, 1.0f * flipY, near },
  1071. { -1.0f, 1.0f * flipY, near },
  1072. // Far plane
  1073. { -1.0f, -1.0f * flipY, far },
  1074. { 1.0f, -1.0f * flipY, far },
  1075. { 1.0f, 1.0f * flipY, far },
  1076. { -1.0f, 1.0f * flipY, far },
  1077. };
  1078. mPlaneVB->writeData(0, sizeof(vertices), vertices, BWT_DISCARD);
  1079. // Draw the mesh
  1080. rapi.setVertexDeclaration(mPositionOnlyVD);
  1081. rapi.setVertexBuffers(0, &mPlaneVB, 1);
  1082. rapi.setIndexBuffer(mPlaneIB);
  1083. rapi.setDrawOperation(DOT_TRIANGLE_LIST);
  1084. rapi.drawIndexed(drawNear ? 0 : 6, drawNear ? 12 : 6, 0, drawNear ? 8 : 4);
  1085. }
  1086. void ShadowRendering::drawFrustum(const std::array<Vector3, 8>& corners)
  1087. {
  1088. RenderAPI& rapi = RenderAPI::instance();
  1089. // Update VB with new vertices
  1090. mFrustumVB->writeData(0, sizeof(Vector3) * 8, corners.data(), BWT_DISCARD);
  1091. // Draw the mesh
  1092. rapi.setVertexDeclaration(mPositionOnlyVD);
  1093. rapi.setVertexBuffers(0, &mFrustumVB, 1);
  1094. rapi.setIndexBuffer(mFrustumIB);
  1095. rapi.setDrawOperation(DOT_TRIANGLE_LIST);
  1096. rapi.drawIndexed(0, 36, 0, 8);
  1097. }
  1098. UINT32 ShadowRendering::getShadowQuality(UINT32 requestedQuality, UINT32 shadowMapResolution, UINT32 minAllowedQuality)
  1099. {
  1100. static const UINT32 TARGET_RESOLUTION = 512;
  1101. // If shadow map resolution is smaller than some target resolution drop the number of PCF samples (shadow quality)
  1102. // so that the penumbra better matches with larger sized shadow maps.
  1103. while(requestedQuality > minAllowedQuality && shadowMapResolution < TARGET_RESOLUTION)
  1104. {
  1105. shadowMapResolution *= 2;
  1106. requestedQuality = std::max(requestedQuality - 1, 1U);
  1107. }
  1108. return requestedQuality;
  1109. }
  1110. ConvexVolume ShadowRendering::getCSMSplitFrustum(const RendererView& view, const Vector3& lightDir, UINT32 cascade,
  1111. UINT32 numCascades, Sphere& outBounds)
  1112. {
  1113. // Determine split range
  1114. float splitNear = getCSMSplitDistance(view, cascade, numCascades);
  1115. float splitFar = getCSMSplitDistance(view, cascade + 1, numCascades);
  1116. // Calculate the eight vertices of the split frustum
  1117. auto& viewProps = view.getProperties();
  1118. const Matrix4& projMat = viewProps.projTransform;
  1119. float aspect;
  1120. float nearHalfWidth, nearHalfHeight;
  1121. float farHalfWidth, farHalfHeight;
  1122. if(viewProps.projType == PT_PERSPECTIVE)
  1123. {
  1124. aspect = projMat[0][0] / projMat[1][1];
  1125. float tanHalfFOV = 1.0f / projMat[0][0];
  1126. nearHalfWidth = splitNear * tanHalfFOV;
  1127. nearHalfHeight = nearHalfWidth * aspect;
  1128. farHalfWidth = splitFar * tanHalfFOV;
  1129. farHalfHeight = farHalfWidth * aspect;
  1130. }
  1131. else
  1132. {
  1133. aspect = projMat[0][0] / projMat[1][1];
  1134. nearHalfWidth = farHalfWidth = projMat[0][0] / 4.0f;
  1135. nearHalfHeight = farHalfHeight = projMat[1][1] / 4.0f;
  1136. }
  1137. const Matrix4& viewMat = viewProps.viewTransform;
  1138. Vector3 cameraRight = Vector3(viewMat[0]);
  1139. Vector3 cameraUp = Vector3(viewMat[1]);
  1140. const Vector3& viewOrigin = viewProps.viewOrigin;
  1141. const Vector3& viewDir = viewProps.viewDirection;
  1142. Vector3 frustumVerts[] =
  1143. {
  1144. viewOrigin + viewDir * splitNear - cameraRight * nearHalfWidth + cameraUp * nearHalfHeight, // Near, left, top
  1145. viewOrigin + viewDir * splitNear + cameraRight * nearHalfWidth + cameraUp * nearHalfHeight, // Near, right, top
  1146. viewOrigin + viewDir * splitNear + cameraRight * nearHalfWidth - cameraUp * nearHalfHeight, // Near, right, bottom
  1147. viewOrigin + viewDir * splitNear - cameraRight * nearHalfWidth - cameraUp * nearHalfHeight, // Near, left, bottom
  1148. viewOrigin + viewDir * splitFar - cameraRight * farHalfWidth + cameraUp * farHalfHeight, // Far, left, top
  1149. viewOrigin + viewDir * splitFar + cameraRight * farHalfWidth + cameraUp * farHalfHeight, // Far, right, top
  1150. viewOrigin + viewDir * splitFar + cameraRight * farHalfWidth - cameraUp * farHalfHeight, // Far, right, bottom
  1151. viewOrigin + viewDir * splitFar - cameraRight * farHalfWidth - cameraUp * farHalfHeight, // Far, left, bottom
  1152. };
  1153. // Calculate the bounding sphere of the frustum
  1154. float diagonalNearSq = nearHalfWidth * nearHalfWidth + nearHalfHeight * nearHalfHeight;
  1155. float diagonalFarSq = farHalfWidth * farHalfWidth + farHalfHeight * farHalfHeight;
  1156. float length = splitFar - splitNear;
  1157. float offset = (diagonalNearSq - diagonalFarSq) / 2 * length + length * 0.5f;
  1158. float distToCenter = Math::clamp(splitFar - offset, splitNear, splitFar);
  1159. Vector3 center = viewOrigin + viewDir * distToCenter;
  1160. float radius = 0.0f;
  1161. for (auto& entry : frustumVerts)
  1162. radius = std::max(radius, center.squaredDistance(entry));
  1163. radius = std::max(sqrt(radius), 1.0f);
  1164. outBounds = Sphere(center, radius);
  1165. // Generate light frustum planes
  1166. Plane viewPlanes[6];
  1167. viewPlanes[FRUSTUM_PLANE_NEAR] = Plane(frustumVerts[0], frustumVerts[1], frustumVerts[2]);
  1168. viewPlanes[FRUSTUM_PLANE_FAR] = Plane(frustumVerts[5], frustumVerts[4], frustumVerts[7]);
  1169. viewPlanes[FRUSTUM_PLANE_LEFT] = Plane(frustumVerts[4], frustumVerts[0], frustumVerts[3]);
  1170. viewPlanes[FRUSTUM_PLANE_RIGHT] = Plane(frustumVerts[1], frustumVerts[5], frustumVerts[6]);
  1171. viewPlanes[FRUSTUM_PLANE_TOP] = Plane(frustumVerts[4], frustumVerts[5], frustumVerts[1]);
  1172. viewPlanes[FRUSTUM_PLANE_BOTTOM] = Plane(frustumVerts[3], frustumVerts[2], frustumVerts[6]);
  1173. Vector<Plane> lightVolume;
  1174. //// Add camera's planes facing towards the lights (forming the back of the volume)
  1175. for(auto& entry : viewPlanes)
  1176. {
  1177. if (entry.normal.dot(lightDir) < 0.0f)
  1178. lightVolume.push_back(entry);
  1179. }
  1180. //// Determine edge planes by testing adjacent planes with different facing
  1181. ////// Pairs of frustum planes that share an edge
  1182. UINT32 adjacentPlanes[][2] =
  1183. {
  1184. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_LEFT },
  1185. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_RIGHT },
  1186. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_TOP },
  1187. { FRUSTUM_PLANE_NEAR, FRUSTUM_PLANE_BOTTOM },
  1188. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_LEFT },
  1189. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_RIGHT },
  1190. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_TOP },
  1191. { FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_BOTTOM },
  1192. { FRUSTUM_PLANE_LEFT, FRUSTUM_PLANE_TOP },
  1193. { FRUSTUM_PLANE_TOP, FRUSTUM_PLANE_RIGHT },
  1194. { FRUSTUM_PLANE_RIGHT, FRUSTUM_PLANE_BOTTOM },
  1195. { FRUSTUM_PLANE_BOTTOM, FRUSTUM_PLANE_LEFT },
  1196. };
  1197. ////// Vertex indices of edges on the boundary between two planes
  1198. UINT32 sharedEdges[][2] =
  1199. {
  1200. { 3, 0 },{ 1, 2 },{ 0, 1 },{ 2, 3 },
  1201. { 4, 7 },{ 6, 5 },{ 5, 4 },{ 7, 6 },
  1202. { 4, 0 },{ 5, 1 },{ 6, 2 },{ 7, 3 }
  1203. };
  1204. for(UINT32 i = 0; i < 12; i++)
  1205. {
  1206. const Plane& planeA = viewPlanes[adjacentPlanes[i][0]];
  1207. const Plane& planeB = viewPlanes[adjacentPlanes[i][1]];
  1208. float dotA = planeA.normal.dot(lightDir);
  1209. float dotB = planeB.normal.dot(lightDir);
  1210. if((dotA * dotB) < 0.0f)
  1211. {
  1212. const Vector3& vertA = frustumVerts[sharedEdges[i][0]];
  1213. const Vector3& vertB = frustumVerts[sharedEdges[i][1]];
  1214. Vector3 vertC = vertA + lightDir;
  1215. if (dotA >= 0.0f)
  1216. lightVolume.push_back(Plane(vertA, vertB, vertC));
  1217. else
  1218. lightVolume.push_back(Plane(vertB, vertA, vertC));
  1219. }
  1220. }
  1221. return ConvexVolume(lightVolume);
  1222. }
  1223. float ShadowRendering::getCSMSplitDistance(const RendererView& view, UINT32 index, UINT32 numCascades)
  1224. {
  1225. // Determines the size of each subsequent cascade split. Value of 1 means the cascades will be linearly split.
  1226. // Value of 2 means each subsequent split will be twice the size of the previous one. Valid range is roughly
  1227. // [1, 4].
  1228. // Note: Make this an adjustable property?
  1229. const static float DISTRIBUTON_EXPONENT = 1.0f;
  1230. // First determine the scale of the split, relative to the entire range
  1231. float scaleModifier = 1.0f;
  1232. float scale = 0.0f;
  1233. float totalScale = 0.0f;
  1234. //// Split 0 corresponds to near plane
  1235. if (index > 0)
  1236. {
  1237. for (UINT32 i = 0; i < numCascades; i++)
  1238. {
  1239. if (i < index)
  1240. scale += scaleModifier;
  1241. totalScale += scaleModifier;
  1242. scaleModifier *= DISTRIBUTON_EXPONENT;
  1243. }
  1244. }
  1245. scale = scale / totalScale;
  1246. // Calculate split distance in Z
  1247. auto& viewProps = view.getProperties();
  1248. float near = viewProps.nearPlane;
  1249. float far = viewProps.farPlane;
  1250. return near + (far - near) * scale;
  1251. }
  1252. float ShadowRendering::getDepthBias(const Light& light, float depthRange, UINT32 mapSize)
  1253. {
  1254. const static float RADIAL_LIGHT_BIAS = 0.05f;
  1255. const static float SPOT_DEPTH_BIAS = 1.0f;
  1256. const static float DIR_DEPTH_BIAS = 5.0f;
  1257. const static float DEFAULT_RESOLUTION = 512.0f;
  1258. // Increase bias if map size smaller than some resolution
  1259. float resolutionScale;
  1260. if (light.getType() == LightType::Directional)
  1261. resolutionScale = light.getBounds().getRadius() / (float)mapSize;
  1262. else
  1263. resolutionScale = DEFAULT_RESOLUTION / (float)mapSize;
  1264. // Decrease bias with larger depth range
  1265. float rangeScale = 1.0f / depthRange;
  1266. float defaultBias = 1.0f;
  1267. switch(light.getType())
  1268. {
  1269. case LightType::Directional:
  1270. defaultBias = DIR_DEPTH_BIAS;
  1271. break;
  1272. case LightType::Radial:
  1273. defaultBias = RADIAL_LIGHT_BIAS;
  1274. break;
  1275. case LightType::Spot:
  1276. defaultBias = SPOT_DEPTH_BIAS;
  1277. break;
  1278. }
  1279. return defaultBias * light.getShadowBias() *resolutionScale * rangeScale;
  1280. }
  1281. float ShadowRendering::getFadeTransition(const Light& light, float depthRange, UINT32 mapSize)
  1282. {
  1283. const static float SPOT_LIGHT_SCALE = 1.0f / 50.0f;
  1284. const static float DIR_LIGHT_SCALE = 20.0f;
  1285. // Note: Currently fade transitions are only used in spot & directional (non omni-directional) lights, so no need
  1286. // to account for radial light type.
  1287. if (light.getType() == LightType::Directional)
  1288. {
  1289. // Reduce the size of the transition region when shadow map resolution is higher
  1290. float resolutionScale = 1.0f / (float)mapSize;
  1291. // Reduce the size of the transition region when the depth range is larger
  1292. float rangeScale = 1.0f / depthRange;
  1293. // Increase the size of the transition region for larger lights
  1294. float radiusScale = light.getBounds().getRadius();
  1295. return light.getShadowBias() * DIR_LIGHT_SCALE * rangeScale * resolutionScale * radiusScale;
  1296. }
  1297. else
  1298. return light.getShadowBias() * SPOT_LIGHT_SCALE;
  1299. }
  1300. }}