diff --git a/build/engine/textures/dev/dev_floor_normal.dds b/build/engine/textures/dev/dev_floor_normal.dds
index 939ee60d6e58a9e0167f0c3754b1cecef0d878f5..6090c53fb7822e29f22685425b70a2bd465c72a3 100644
Binary files a/build/engine/textures/dev/dev_floor_normal.dds and b/build/engine/textures/dev/dev_floor_normal.dds differ
diff --git a/build/engine/textures/dev/dev_floor_normal.psd b/build/engine/textures/dev/dev_floor_normal.psd
new file mode 100644
index 0000000000000000000000000000000000000000..a32f6e496a8f49da035c3bb7618716c24bb483e4
Binary files /dev/null and b/build/engine/textures/dev/dev_floor_normal.psd differ
diff --git a/build/gamesource/gui/menu/settings_video.html b/build/gamesource/gui/menu/settings_video.html
index aaff1a2b9bda3d6cc65f75e299f41be2fa1ca186..1873829d281d67034ac4c29725f4f7fc22225da7 100644
--- a/build/gamesource/gui/menu/settings_video.html
+++ b/build/gamesource/gui/menu/settings_video.html
@@ -9,12 +9,13 @@
 			<div id="title">Видео</div>
 			<div id="left_menu">
 				<div class="menu-item">Разрешение</div>
-				<div class="menu-item">Во весь экран</div>				
-				<div class="menu-item">Сглаживание</div>				
+				<!-- <div class="menu-item">Во весь экран</div>				 -->
+				<!-- <div class="menu-item">Сглаживание</div>				 -->
 				<div class="menu-item">Разрешение текстур</div>				
-				<div class="menu-item">Контраст</div>				
-				<div class="menu-item">Гамма</div>				
-				<div class="menu-item">Яркость</div>				
+				<div class="menu-item">Глобальное освещение</div>				
+				<!-- <div class="menu-item">Контраст</div>				 -->
+				<!-- <div class="menu-item">Гамма</div>				 -->
+				<!-- <div class="menu-item">Яркость</div>				 -->
 				
 				<div class="menu-item" onclick="engine_command" args="
 				    gui_load menu_settings_video_ex menu/settings_video_ex.html
@@ -29,24 +30,30 @@
 					<option value="1">1920x1080</option>
 					<option value="0">1366x768</option>
 				</select>
-				<select class="set_item" cvar="r_win_windowed">
-					<option value="0">Да</option>
-					<option value="1">Нет</option>
-				</select>
-				<select class="set_item" cvar="pp_dlaa|pp_nfaa">
-					<option value="0|0">Нет</option>
-					<option value="1|0">DLAA</option>
-					<option value="0|1">NFAA</option>
+				<!-- <select class="set_item" cvar="r_win_windowed"> -->
+					<!-- <option value="0">Да</option> -->
+					<!-- <option value="1">Нет</option> -->
+				<!-- </select> -->
+				<!-- <select class="set_item" cvar="pp_dlaa|pp_nfaa"> -->
+					<!-- <option value="0|0">Нет</option> -->
+					<!-- <option value="1|0">DLAA</option> -->
+					<!-- <option value="0|1">NFAA</option> -->
 					<!-- <option value="1|1">NFAA+DLAA</option> -->
-				</select>
+				<!-- </select> -->
 				<select class="set_item" cvar="r_texfilter_max_miplevel">
 					<option value="0">Высокое</option>
 					<option value="1">Среднее</option>
 					<option value="2">Низкое</option>
 				</select>
-				<div style="height: 40px; position: relative; top: 10px;"><range class="set_item" min="0" max="1" step="0.01" format="%.2f" cvar="pp_contrast" /></div>
-				<div style="height: 40px; position: relative; top: 10px;"><range class="set_item" min="0" max="1" step="0.01" format="%.2f" cvar="pp_gamma" /></div>
-				<div style="height: 40px; position: relative; top: 10px;"><range class="set_item" min="0" max="1" step="0.01" format="%.2f" cvar="pp_bright" /></div>
+				<select class="set_item" cvar="lpv_cascades_count">
+					<option value="0">Нет</option>
+					<option value="1">Низкое</option>
+					<option value="2">Среднее</option>
+					<option value="3">Высокое</option>
+				</select>
+				<!-- <div style="height: 40px; position: relative; top: 10px;"><range class="set_item" min="0" max="1" step="0.01" format="%.2f" cvar="pp_contrast" /></div> -->
+				<!-- <div style="height: 40px; position: relative; top: 10px;"><range class="set_item" min="0" max="1" step="0.01" format="%.2f" cvar="pp_gamma" /></div> -->
+				<!-- <div style="height: 40px; position: relative; top: 10px;"><range class="set_item" min="0" max="1" step="0.01" format="%.2f" cvar="pp_bright" /></div> -->
 				
 				<div class="menu-item bottom" onclick="settings_commit" style="text-align: right;">Применить</div>
 			</div>
diff --git a/build/gamesource/gui/menu/settings_video_ex.html b/build/gamesource/gui/menu/settings_video_ex.html
index ded330006ed46c24ceed22ca9fc9d34fa1778b77..e1e667924e9acc83992e4657e18be641b102192c 100644
--- a/build/gamesource/gui/menu/settings_video_ex.html
+++ b/build/gamesource/gui/menu/settings_video_ex.html
@@ -9,13 +9,14 @@
 			<div id="title">Расширенные</div>
 			<div id="left_menu">	
 				<div class="menu-item">Фильтрация текстур</div>	
-				<div class="menu-item">Эффект bloom</div>			
-				<div class="menu-item">Эффект lensflare</div>				
-				<div class="menu-item">Эффект SSAO</div>			
+				<!-- <div class="menu-item">Эффект bloom</div>			 -->
+				<!-- <div class="menu-item">Эффект lensflare</div>				 -->
+				<!-- <div class="menu-item">Эффект SSAO</div>			 -->
 				<div class="menu-item">Глобальные тени</div>			
-				<div class="menu-item">Локальные тени</div>				
-				<div class="menu-item">Размытие теней</div>		
-				<div class="menu-item">Размытие в движении</div>			
+				<div class="menu-item">Точечные тени</div>				
+				<div class="menu-item">Направленные тени</div>				
+				<!-- <div class="menu-item">Размытие теней</div>		 -->
+				<!-- <div class="menu-item">Размытие в движении</div>			 -->
 				
 				<div class="menu-item bottom" onclick="on_cancel">Назад</div>
 			</div>
@@ -29,7 +30,7 @@
 					<option value="2|8">Анизотропная 8X</option>
 					<option value="2|16">Анизотропная 16X</option>
 				</select>
-				<select class="set_item" cvar="pp_bloom">
+				<!-- <select class="set_item" cvar="pp_bloom">
 					<option value="1">Включить</option>
 					<option value="0">Выключить</option>
 				</select>
@@ -40,13 +41,19 @@
 				<select class="set_item" cvar="pp_ssao">
 					<option value="1">Включить</option>
 					<option value="0">Выключить</option>
+				</select> -->
+				<select class="set_item" cvar="r_pssm_quality">
+					<!-- <option value="0|0.500000">Нет</option> -->
+					<option value="0.500000">Низкое</option>
+					<option value="1.000000">Среднее</option>
+					<option value="2.000000">Высокое</option>
+					<option value="4.000000">Очень высокое</option>
 				</select>
-				<select class="set_item" cvar="r_pssm_shadowed|r_pssm_quality">
-					<option value="0|0.500000">Нет</option>
-					<option value="1|0.500000">Низкое</option>
-					<option value="1|1.000000">Среднее</option>
-					<option value="1|2.000000">Высокое</option>
-					<option value="1|4.000000">Очень высокое</option>
+				<select class="set_item" cvar="r_psm_quality">
+					<option value="0.500000">Низкое</option>
+					<option value="1.000000">Среднее</option>
+					<option value="2.000000">Высокое</option>
+					<option value="4.000000">Очень высокое</option>
 				</select>
 				<select class="set_item" cvar="r_lsm_quality">
 					<option value="0.500000">Низкое</option>
@@ -54,7 +61,7 @@
 					<option value="2.000000">Высокое</option>
 					<option value="4.000000">Очень высокое</option>
 				</select>
-				<select class="set_item" cvar="r_shadow_soft">
+				<!-- <select class="set_item" cvar="r_shadow_soft">
 					<option value="0">Нет</option>
 					<option value="1">1 проход</option>
 					<option value="2">2 прохода</option>
@@ -62,7 +69,7 @@
 				<select class="set_item" cvar="pp_motionblur">
 					<option value="1">Включить</option>
 					<option value="0">Выключить</option>
-				</select>
+				</select> -->
 				
 				<div class="menu-item bottom" onclick="settings_commit" style="text-align: right;">Применить</div>
 			</div>
diff --git a/source/anim/RenderableVisibility.cpp b/source/anim/RenderableVisibility.cpp
index ca01c54d0d0406a0dbbfe98ce6e1bc5fa4ba6ec3..7f15e413e1b6f31774919e6434c76beb75b5d86f 100644
--- a/source/anim/RenderableVisibility.cpp
+++ b/source/anim/RenderableVisibility.cpp
@@ -10,7 +10,7 @@ CRenderableVisibility::CRenderableVisibility(ID idPlugin, CAnimatedModelProvider
 {
 }
 
-ID CRenderableVisibility::getPluginId()
+ID CRenderableVisibility::getPluginId() const
 {
 	return(m_idPlugin);
 }
@@ -30,7 +30,7 @@ void CRenderableVisibility::updateForFrustum(const IFrustum *pFrustum, const IXR
 	CRenderableVisibility *pRef = NULL;
 	if(pReference)
 	{
-		assert(((IXRenderableVisibility*)pReference)->getPluginId() == -1);
+		assert(((IXRenderableVisibility*)pReference)->getPluginId() == getPluginId());
 		pRef = (CRenderableVisibility*)pReference;
 	}
 
@@ -38,6 +38,90 @@ void CRenderableVisibility::updateForFrustum(const IFrustum *pFrustum, const IXR
 	m_pProviderDynamic->computeVisibility(pFrustum, this, pRef);
 }
 
+void CRenderableVisibility::append(const IXRenderableVisibility *pOther_)
+{
+	assert(((IXRenderableVisibility*)pOther_)->getPluginId() == getPluginId());
+	const CRenderableVisibility *pOther = (const CRenderableVisibility*)pOther_;
+
+	item_s *pItem;
+	const item_s *pOtherItem;
+	for(UINT i = 0, l = pOther->m_aItems.size(); i < l; ++i)
+	{
+		pOtherItem = &pOther->m_aItems[i];
+		if(pOtherItem->isVisible)
+		{
+			pItem = &m_aItems[i];
+			if(pItem->isVisible)
+			{
+				if(pOtherItem->uLod < pItem->uLod)
+				{
+					pItem->uLod = pOtherItem->uLod;
+				}
+			}
+			else
+			{
+				pItem->isTransparent = pOtherItem->isTransparent;
+				pItem->isVisible = true;
+				pItem->uLod = pOtherItem->uLod;
+			}
+		}
+	}
+
+	for(UINT i = 0, l = pOther->m_aItemsDynamic.size(); i < l; ++i)
+	{
+		pOtherItem = &pOther->m_aItemsDynamic[i];
+		if(pOtherItem->isVisible)
+		{
+			pItem = &m_aItemsDynamic[i];
+			if(pItem->isVisible)
+			{
+				if(pOtherItem->uLod < pItem->uLod)
+				{
+					pItem->uLod = pOtherItem->uLod;
+				}
+			}
+			else
+			{
+				pItem->isTransparent = pOtherItem->isTransparent;
+				pItem->isVisible = true;
+				pItem->uLod = pOtherItem->uLod;
+			}
+		}
+	}
+
+	//! @todo implement for transparency too!
+}
+
+void CRenderableVisibility::substract(const IXRenderableVisibility *pOther_)
+{
+	assert(((IXRenderableVisibility*)pOther_)->getPluginId() == getPluginId());
+	const CRenderableVisibility *pOther = (const CRenderableVisibility*)pOther_;
+
+	item_s *pItem;
+	const item_s *pOtherItem;
+	for(UINT i = 0, l = pOther->m_aItems.size(); i < l; ++i)
+	{
+		pOtherItem = &pOther->m_aItems[i];
+		pItem = &m_aItems[i];
+		if(pItem->isVisible && pOtherItem->isVisible)
+		{
+			pItem->isVisible = false;
+		}
+	}
+
+	for(UINT i = 0, l = pOther->m_aItemsDynamic.size(); i < l; ++i)
+	{
+		pOtherItem = &pOther->m_aItemsDynamic[i];
+		pItem = &m_aItemsDynamic[i];
+		if(pItem->isVisible && pOtherItem->isVisible)
+		{
+			pItem->isVisible = false;
+		}
+	}
+
+	//! @todo implement for transparency too!
+}
+
 void CRenderableVisibility::setItemCount(UINT uCount)
 {
 	if(m_aItems.GetAllocSize() > uCount * 2)
diff --git a/source/anim/RenderableVisibility.h b/source/anim/RenderableVisibility.h
index f890ae17867d53d21bc52f485455c4afbc9d1004..d0d4112e11641c3789bc92a8ca24e0a12d7b47ce 100644
--- a/source/anim/RenderableVisibility.h
+++ b/source/anim/RenderableVisibility.h
@@ -11,14 +11,18 @@ class CRenderableVisibility: public IXRenderableVisibility
 public:
 	CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *m_pProviderAnimated, CDynamicModelProvider *m_pProviderDynamic);
 
-	ID getPluginId();
+	ID getPluginId() const override;
 
-	void setOcclusionCuller(IXOcclusionCuller *pOcclusionCuller);
+	void setOcclusionCuller(IXOcclusionCuller *pOcclusionCuller) override;
 
-	void updateForCamera(ICamera *pCamera, const IXRenderableVisibility *pReference = NULL);
+	void updateForCamera(ICamera *pCamera, const IXRenderableVisibility *pReference = NULL) override;
 
-	void updateForFrustum(const IFrustum *pFrustum, const IXRenderableVisibility *pReference = NULL);
+	void updateForFrustum(const IFrustum *pFrustum, const IXRenderableVisibility *pReference = NULL) override;
 	
+	void append(const IXRenderableVisibility *pOther) override;
+
+	void substract(const IXRenderableVisibility *pOther) override;
+
 	struct item_s
 	{
 		bool isVisible = false;
diff --git a/source/common b/source/common
index cc34760a0f9dc08eaa9db29930f96c9697a7e975..a887a31b4b9541a0997e9fa8ac34ad62977813fc 160000
--- a/source/common
+++ b/source/common
@@ -1 +1 @@
-Subproject commit cc34760a0f9dc08eaa9db29930f96c9697a7e975
+Subproject commit a887a31b4b9541a0997e9fa8ac34ad62977813fc
diff --git a/source/light/LightSystem.cpp b/source/light/LightSystem.cpp
index 519ca34ff02d19491bbdc66dba744ba5211cb3f1..24615237e24dac39749deb63581a875bc0672473 100644
--- a/source/light/LightSystem.cpp
+++ b/source/light/LightSystem.cpp
@@ -32,6 +32,11 @@ private:
 CLightSystem::CLightSystem(IXCore *pCore):
 	m_pCore(pCore)
 {
+	Core_0RegisterCVarFloat("lpv_size_0", 1.0f, "Коэфициент размера первого каскада LPV");
+	Core_0RegisterCVarFloat("lpv_size_1", 2.0f, "Коэфициент размера второго каскада LPV");
+	Core_0RegisterCVarFloat("lpv_size_2", 4.0f, "Коэфициент размера третьего каскада LPV");
+	Core_0RegisterCVarInt("lpv_cascades_count", 3, "Количество активных каскадов LPV [0;3]");
+
 	m_pLevelListener = new CLevelLoadListener(this, pCore);
 
 	m_pLevelChannel = pCore->getEventChannel<XEventLevel>(EVENT_LEVEL_GUID);
@@ -198,7 +203,13 @@ CLightSystem::CLightSystem(IXCore *pCore):
 		m_idComLightingSpotShadow = SGCore_ShaderCreateKit(idResPos, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "lighting_com.ps", "lighting_com_spot_shadow.ps", Defines_IS_SPOT_SHADOWED));
 		m_idComLightingPSSMShadow = SGCore_ShaderCreateKit(idResPos, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "lighting_com.ps", "lighting_com_pssm_shadow.ps", Defines_IS_PSSM_SHADOWED));
 
-		m_idComLightingGI = SGCore_ShaderCreateKit(idResPos, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "lighting_gi.ps"));
+		// LPV_CASCADES_COUNT - iCascades
+		GXMacro Defines_LPV_CASCADE_3[] = {{"LPV_START_CASCADE", "0"}, {0, 0}};
+		m_idComLightingGI[2] = SGCore_ShaderCreateKit(idResPos, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "lighting_gi.ps", 0, Defines_LPV_CASCADE_3));
+		GXMacro Defines_LPV_CASCADE_2[] = {{"LPV_START_CASCADE", "1"}, {0, 0}};
+		m_idComLightingGI[1] = SGCore_ShaderCreateKit(idResPos, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "lighting_gi.ps", 0, Defines_LPV_CASCADE_2));
+		GXMacro Defines_LPV_CASCADE_1[] = {{"LPV_START_CASCADE", "2"}, {0, 0}};
+		m_idComLightingGI[0] = SGCore_ShaderCreateKit(idResPos, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "lighting_gi.ps", 0, Defines_LPV_CASCADE_1));
 
 		m_idHDRinitLuminance = SGCore_ShaderCreateKit(idScreenOut, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "hdr_luminance.ps"));
 		m_idHDRAdaptLuminance = SGCore_ShaderCreateKit(idScreenOut, SGCore_ShaderLoad(SHADER_TYPE_PIXEL, "hdr_adapt.ps"));
@@ -351,7 +362,6 @@ void CLightSystem::destroySun(IXLightSun *pLight)
 {
 	assert(m_pSun == pLight);
 	_deleteLight(m_pSun);
-	mem_delete(m_pSun);
 }
 void CLightSystem::destroyPoint(IXLightPoint *_pLight)
 {
@@ -404,16 +414,37 @@ void XMETHODCALLTYPE CLightSystem::updateVisibility()
 
 	float3 vCamPos;
 	m_pMainCamera->getPosition(&vCamPos);
+	float3 vCamDir;
+	m_pMainCamera->getLook(&vCamDir);
 
-	//! @todo fix this values!
-	float3 vLPVmin = float3(-16.0f, -16.0f, -16.0f) + vCamPos;
-	float3 vLPVmax = float3(16.0f, 16.0f, 16.0f) + vCamPos;
+	static const float *lpv_size_2 = GET_PCVAR_FLOAT("lpv_size_2");
+	static const int *lpv_cascades_count = GET_PCVAR_INT("lpv_cascades_count");
+
+	int iCascades = *lpv_cascades_count;
+	if(iCascades < 0)
+	{
+		iCascades = 0;
+	}
+	if(iCascades > LPV_CASCADES_COUNT)
+	{
+		iCascades = LPV_CASCADES_COUNT;
+	}
+	
+	float3 vLPVmin;
+	float3 vLPVmax;
+	if(iCascades)
+	{
+		vLPVmin = vLPVmax = vCamPos + vCamDir * (LPV_GRID_SIZE / 2 - LPV_STEP_COUNT) * *lpv_size_2;
+
+		vLPVmin += float3(-16.0f, -16.0f, -16.0f) * *lpv_size_2;
+		vLPVmax += float3(16.0f, 16.0f, 16.0f) * *lpv_size_2;
+	}
 
 	for(UINT i = 0, l = m_aLights.size(); i < l; ++i)
 	{
 		if(m_aLights[i]->isEnabled())
 		{
-			m_aLights[i]->updateVisibility(m_pMainCamera, vLPVmin, vLPVmax);
+			m_aLights[i]->updateVisibility(m_pMainCamera, vLPVmin, vLPVmax, iCascades > 0);
 		}
 	}
 }
@@ -423,14 +454,24 @@ void XMETHODCALLTYPE CLightSystem::setFrameObserverCamera(ICamera *pMainCamera)
 	//! @todo uncomment me!
 	// pMainCamera->AddRef();
 	m_pMainCamera = pMainCamera;
+	if(m_pSun)
+	{
+		m_pSun->setCamera(pMainCamera);
+	}
 }
 
 void XMETHODCALLTYPE CLightSystem::setGBuffer(IGXTexture2D *pColor, IGXTexture2D *pNormals, IGXTexture2D *pParams, IGXTexture2D *pDepth)
 {
+	mem_release(m_pGBufferColor);
+	mem_release(m_pGBufferNormals);
+	mem_release(m_pGBufferParams);
+	mem_release(m_pGBufferDepth);
+
 	pColor->AddRef();
 	pNormals->AddRef();
 	pParams->AddRef();
 	pDepth->AddRef();
+
 	m_pGBufferColor = pColor;
 	m_pGBufferNormals = pNormals;
 	m_pGBufferParams = pParams;
@@ -476,7 +517,7 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 	//очищаем рт и стенсил
 	pCtx->clear(GX_CLEAR_COLOR);
 	//	pCtx->clear(GX_CLEAR_COLOR | GX_CLEAR_STENCIL);
-	
+
 	m_pRenderPipeline->renderGI();
 
 	float3 vCamDir;
@@ -484,15 +525,25 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 	float3 vCamPos;
 	m_pMainCamera->getPosition(&vCamPos);
 
+	static const float *lpv_size_0 = GET_PCVAR_FLOAT("lpv_size_0");
+	static const float *lpv_size_1 = GET_PCVAR_FLOAT("lpv_size_1");
+	static const float *lpv_size_2 = GET_PCVAR_FLOAT("lpv_size_2");
+	static const int *lpv_cascades_count = GET_PCVAR_INT("lpv_cascades_count");
 
-	const float c_aLPVsizes[] = {
-		//0.5f,
-		//1.0f,
-		//2.0f
+	int iCascades = *lpv_cascades_count;
+	if(iCascades < 0)
+	{
+		iCascades = 0;
+	}
+	if(iCascades > LPV_CASCADES_COUNT)
+	{
+		iCascades = LPV_CASCADES_COUNT;
+	}
 
-		1.0f,
-		2.0f,
-		4.0f
+	const float c_aLPVsizes[] = {
+		*lpv_size_0,
+		*lpv_size_1,
+		*lpv_size_2
 	};
 
 	m_lpvCentersShaderData.vs.vCenterSize[0] = float4(vCamPos + vCamDir * (LPV_GRID_SIZE / 2 - LPV_STEP_COUNT) * c_aLPVsizes[0], c_aLPVsizes[0]);
@@ -500,31 +551,6 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 	m_lpvCentersShaderData.vs.vCenterSize[2] = float4(vCamPos + vCamDir * (LPV_GRID_SIZE / 2 - LPV_STEP_COUNT) * c_aLPVsizes[2], c_aLPVsizes[2]);
 	m_pLPVcentersShaderData->update(&m_lpvCentersShaderData.vs);
 
-	/*
-	vs:
-	cbuffer0: (per light)
-	- g_mW
-	cbuffer1: (per frame)
-	- g_mVP
-	- g_mViewInv
-	- g_vNearFar
-	- g_vParamProj
-
-	ps:
-	cbuffer0: (per light)
-	- g_vLightPos
-	- g_vLightColor
-	- g_vLightPowerDistShadow
-	cbuffer1: (per frame)
-	- g_vViewPos
-
-
-	{
-	render shadowmaps
-	render direct light with shadows
-	inject VPLs into LPV grid
-	}
-	*/
 
 	// Определим список лампочек, которые будут участвовать в текущем кадре
 	CXLight *pLight;
@@ -550,25 +576,9 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 	static const float *r_near = GET_PCVAR_FLOAT("r_near");
 	static const float *r_far = GET_PCVAR_FLOAT("r_far");
 
-	// 	float4x4 &mCamView = gdata::mCamView;
-	// 	// Core_RMatrixGet(G_RI_MATRIX_OBSERVER_VIEW, &mCamView);
-	// 	m_shadowShaderData.vs.mViewInv = SMMatrixTranspose(SMMatrixInverse(NULL, mCamView));
-	// 	m_shadowShaderData.vs.vNearFar = gdata::vNearFar; // float2(*r_near, *r_far);
-	// 	m_shadowShaderData.vs.vParamProj = float3((float)m_uOutWidth, (float)m_uOutHeight, gdata::fProjFov); // *r_default_fov);
-	// 	m_pShadowShaderDataVS->update(&m_shadowShaderData.vs);
-
-	//@TODO: убрать это
-	//Core_RFloat3Get(G_RI_FLOAT3_OBSERVER_POSITION, &m_shadowShaderData.ps.vPosCam);
-	//m_pShadowShaderDataPS->update(&m_shadowShaderData.ps);
-
-
-	//m_pDevice->setPixelShaderConstant(m_pShadowShaderDataPS, 7);
 
 	pCtx->setRasterizerState(m_pRasterizerConservative);
 
-	//pCtx->setVSConstant(m_pCameraShaderDataVS, 8);
-	//pCtx->setPSConstant(m_pCameraShaderDataVS, 8);
-
 	UINT uShadowCount = 0;
 	while((uShadowCount = m_pShadowCache->processNextBunch()))
 	{
@@ -576,9 +586,6 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 		pCtx->setDepthStencilSurface(pOldDSSurface);
 		pCtx->setBlendState(m_pBlendAlphaOneOne);
 
-	//	pCtx->setVSConstant(m_pLightingShaderDataVS, 1);
-	//	pCtx->setPSConstant(m_pLightingShaderDataPS, 1);
-
 		IBaseShadowMap *pShadow = NULL;
 
 		//render direct light with shadows
@@ -611,10 +618,6 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 			pCtx->setRasterizerState(NULL);
 
 
-
-			//pCtx->setVSConstant(m_pShadowShaderDataVS, 1);
-		//	pCtx->setVSConstant(m_pLightingShaderDataVS, 1);
-
 			pCtx->setBlendState(m_pBlendRed);
 			pShadow->genShadow(m_pGBufferDepth, m_pGBufferNormals);
 			pCtx->setBlendState(m_pBlendAlphaOneOne);
@@ -638,8 +641,6 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 				assert(!"Unknown light type!");
 			}
 
-		//	pCtx->setVSConstant(m_pLightingShaderDataVS, 1);
-
 			//теперь когда будем считать освещение надо сбросить значения в стенсил буфере, чтобы каждый кадр не чистить
 			//если стенсил тест прошел успешно, устанавливаем значнеие в нуль
 			if(pLight->getType() != LIGHT_TYPE_SUN)
@@ -663,17 +664,13 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 
 			SGCore_ScreenQuadDraw();
 		}
-
-
-		//pCtx->setSamplerState(gdata::rstates::pSamplerLinearWrap, 0);
-		//pCtx->setSamplerState(gdata::rstates::pSamplerLinearWrap, 1);
 	}
 
 	pCtx->setVSConstant(m_pLPVcentersShaderData, 9);
 	pCtx->setPSConstant(m_pLPVcentersShaderData, 9);
 
 	bool isFirstRun[3] = {true, true, true};
-	while((uShadowCount = m_pShadowCache->processNextRSMBunch()))
+	while(iCascades && (uShadowCount = m_pShadowCache->processNextRSMBunch()))
 	{
 		pCtx->setVSConstant(m_pLPVcurrentCascadeShaderData, 10);
 		pCtx->setPSConstant(m_pLPVcurrentCascadeShaderData, 10);
@@ -681,15 +678,15 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 		pCtx->setDepthStencilSurface(pOldDSSurface);
 		pCtx->setBlendState(m_pBlendAlphaOneOne);
 
-	//	pCtx->setVSConstant(m_pLightingShaderDataVS, 1);
-	//	pCtx->setPSConstant(m_pLightingShaderDataPS, 1);
+		//	pCtx->setVSConstant(m_pLightingShaderDataVS, 1);
+		//	pCtx->setPSConstant(m_pLightingShaderDataPS, 1);
 
 		IGXDepthStencilSurface *pOldSurface = pCtx->getDepthStencilSurface();
 		pCtx->unsetDepthStencilSurface();
 
 		IBaseReflectiveShadowMap *pShadow = NULL;
 
-		for(UINT i = 0; i < 3; ++i)
+		for(UINT i = LPV_CASCADES_COUNT - iCascades; i < LPV_CASCADES_COUNT; ++i)
 		{
 			float4_t vTmp((float)i + 0.5f); // just to be sure
 			m_pLPVcurrentCascadeShaderData->update(&vTmp);
@@ -744,38 +741,42 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 
 		//break;
 	}
-	for(UINT i = 0; i < 3; ++i)
+
+	bool isClean[3] = {0};
+	if(iCascades)
 	{
-		if(isFirstRun[i])
+		for(UINT i = 0; i < 3; ++i)
 		{
-			IGXSurface *pLPVRed = m_aLPVs[i].pGIAccumRed->asRenderTarget();
-			IGXSurface *pLPVGreen = m_aLPVs[i].pGIAccumGreen->asRenderTarget();
-			IGXSurface *pLPVBlue = m_aLPVs[i].pGIAccumBlue->asRenderTarget();
+			if(isFirstRun[i])
+			{
+				IGXSurface *pLPVRed = m_aLPVs[i].pGIAccumRed->asRenderTarget();
+				IGXSurface *pLPVGreen = m_aLPVs[i].pGIAccumGreen->asRenderTarget();
+				IGXSurface *pLPVBlue = m_aLPVs[i].pGIAccumBlue->asRenderTarget();
 
-			pCtx->setColorTarget(pLPVRed);
-			pCtx->setColorTarget(pLPVGreen, 1);
-			pCtx->setColorTarget(pLPVBlue, 2);
+				pCtx->setColorTarget(pLPVRed);
+				pCtx->setColorTarget(pLPVGreen, 1);
+				pCtx->setColorTarget(pLPVBlue, 2);
 
-			mem_release(pLPVRed);
-			mem_release(pLPVGreen);
-			mem_release(pLPVBlue);
+				mem_release(pLPVRed);
+				mem_release(pLPVGreen);
+				mem_release(pLPVBlue);
 
-			pCtx->clear(GX_CLEAR_COLOR);
-			isFirstRun[i] = false;
+				pCtx->clear(GX_CLEAR_COLOR);
+				isFirstRun[i] = false;
 
-			pCtx->setColorTarget(NULL);
-			pCtx->setColorTarget(NULL, 1);
-			pCtx->setColorTarget(NULL, 2);
+				pCtx->setColorTarget(NULL);
+				pCtx->setColorTarget(NULL, 1);
+				pCtx->setColorTarget(NULL, 2);
+
+				isClean[i] = true;
+			}
 		}
 	}
-
 	SGCore_ShaderUnBind();
 
 	mem_release(pOldDSSurface);
 
-	//pCtx->setVSConstant(m_pLightingShaderDataVS, 1);
-	//pCtx->setPSConstant(m_pLightingShaderDataPS, 1);
-
+	if(iCascades)
 	{
 		SGCore_ShaderBind(m_idLPVPropagateShader);
 		UINT uStepCount[] = {4, 6, 8};
@@ -784,6 +785,10 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 		//{
 		for(UINT j = 0; j < 3; ++j)
 		{
+			if(isClean[j])
+			{
+				continue;
+			}
 			for(UINT i = 0; i < uStepCount[j]; ++i)
 			{
 				pCtx->setCSTexture(m_aLPVs[j].pGIAccumRed, 0);
@@ -830,6 +835,7 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 	}
 
 
+	if(iCascades)
 	{
 		pCtx->setColorTarget(pAmbientSurf);
 		//gdata::pDXDevice->setRasterizerState(NULL);
@@ -837,13 +843,7 @@ void XMETHODCALLTYPE CLightSystem::renderGI(IGXTexture2D *pLightTotal, IGXTextur
 		pCtx->setBlendState(m_pBlendAlphaOneOne);
 		pCtx->setDepthStencilState(m_pDepthStencilStateLightShadowGlobal);
 
-		//pCtx->setPSConstant(m_pLightingShaderDataPS, 1);
-
-		ID idshaderkit = m_idComLightingGI;
-
-		//SGCore_ShaderSetVRF(SHADER_TYPE_PIXEL, idshader, "g_vViewPos", &gdata::vConstCurrCamPos);
-
-		//	pCtx->setPSConstant(m_pCameraShaderDataVS, 8);
+		ID idshaderkit = m_idComLightingGI[iCascades - 1];
 
 		SGCore_ShaderBind(idshaderkit);
 
diff --git a/source/light/LightSystem.h b/source/light/LightSystem.h
index bc33f45a63f4dc62f22ccbd5c52fbbd332e92924..ce108a9c8084ef90408f83cca7fdfbc9f7ef039b 100644
--- a/source/light/LightSystem.h
+++ b/source/light/LightSystem.h
@@ -114,7 +114,7 @@ protected:
 	ID m_idComLightingShadow = -1;
 	ID m_idComLightingSpotShadow = -1;
 	ID m_idComLightingPSSMShadow = -1;
-	ID m_idComLightingGI = -1;
+	ID m_idComLightingGI[LPV_CASCADES_COUNT];
 
 	ID m_idHDRinitLuminance = -1;
 	ID m_idHDRAdaptLuminance = -1;
diff --git a/source/light/light.cpp b/source/light/light.cpp
index bdf07c8c2a35123cf06f8681a73d64f3a9bb5585..ea4275b635070c1b9a772b486f668254041608f6 100644
--- a/source/light/light.cpp
+++ b/source/light/light.cpp
@@ -181,11 +181,11 @@ float CXLight::getMaxDistance()
 	return(SMVector3Length2(m_vColor));
 }
 
-void CXLight::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax)
+void CXLight::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV)
 {
 	updateFrustum();
 
-	if(m_pFrustum->boxInFrustum(&vLPVmin, &vLPVmax))
+	if(useLPV && m_pFrustum->boxInFrustum(&vLPVmin, &vLPVmax))
 	{
 		m_renderType |= LRT_LPV;
 	}
@@ -250,7 +250,7 @@ void CXLightPoint::updateFrustum()
 	m_pFrustum->update(planes, true);
 }
 
-void CXLightPoint::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax)
+void CXLightPoint::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV)
 {
 	m_renderType = LRT_NONE;
 	
@@ -260,7 +260,7 @@ void CXLightPoint::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin,
 		m_renderType |= LRT_LIGHT;
 	}
 
-	CXLight::updateVisibility(pMainCamera, vLPVmin, vLPVmax);
+	CXLight::updateVisibility(pMainCamera, vLPVmin, vLPVmax, useLPV);
 }
 
 //##########################################################################
@@ -269,15 +269,33 @@ CXLightSun::CXLightSun(CLightSystem *pLightSystem):
 	CXLight(pLightSystem)
 {
 	m_type = LIGHT_TYPE_SUN;
+
+	IXRenderPipeline *pPipeline;
+	Core_GetIXCore()->getRenderPipeline(&pPipeline);
+	pPipeline->newVisData(&m_pReflectiveVisibility);
+	pPipeline->newVisData(&m_pTempVisibility);
+	mem_release(pPipeline);
+
+	m_pReflectiveFrustum = SGCore_CrFrustum();
+
+	for(UINT i = 0; i < PSSM_MAX_SPLITS; ++i)
+	{
+		m_pPSSMFrustum[i] = SGCore_CrFrustum();
+	}
 }
 
-void CXLightSun::Release()
+CXLightSun::~CXLightSun()
 {
-	--m_uRefCount;
-	if(!m_uRefCount)
+	mem_release(m_pReflectiveFrustum);
+	mem_release(m_pReflectiveVisibility);
+	mem_release(m_pTempVisibility);
+
+	for(UINT i = 0; i < PSSM_MAX_SPLITS; ++i)
 	{
-		m_pLightSystem->destroySun(this);
+		mem_release(m_pPSSMFrustum[i]);
 	}
+
+	m_pLightSystem->destroySun(this);
 }
 
 SMQuaternion CXLightSun::getDirection()
@@ -301,6 +319,7 @@ void CXLightSun::updatePSConstants(IGXDevice *pDevice)
 	{
 		m_pPSData = pDevice->createConstantBuffer(sizeof(m_dataPS));
 	}
+
 	m_dataPS.vLightColor = float4(m_vColor, SMVector3Length2(m_vColor));
 	m_dataPS.vLightPos = float4(m_qDirection * -LIGHTS_DIR_BASE, m_fShadowIntensity);
 	m_pPSData->update(&m_dataPS);
@@ -316,19 +335,225 @@ void CXLightSun::setMaxDistance(float fMax)
 	m_fMaxDistance = fMax;
 }
 
-void CXLightSun::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax)
+void CXLightSun::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV)
 {
 	m_renderType = LRT_LIGHT | LRT_LPV;
-#if 0
-	m_renderType = LRT_NONE;
 
-	float3 vOrigin = getPosition();
-	if(pMainCamera->getFrustum()->sphereInFrustum(&vOrigin, getMaxDistance()))
+	CXLight::updateVisibility(pMainCamera, vLPVmin, vLPVmax, useLPV);
+	
+	static const int *lpv_cascades_count = GET_PCVAR_INT("lpv_cascades_count");
+
+	if(lpv_cascades_count > 0)
 	{
-		m_renderType |= LRT_LIGHT;
+		m_pReflectiveVisibility->updateForFrustum(m_pReflectiveFrustum);
 	}
-#endif
-	CXLight::updateVisibility(pMainCamera, vLPVmin, vLPVmax);
+
+	static const int *r_pssm_splits = GET_PCVAR_INT("r_pssm_splits");
+
+	for(UINT i = 1; i < *r_pssm_splits; ++i)
+	{
+		m_pTempVisibility->updateForFrustum(m_pPSSMFrustum[i - 1]);
+		m_pVisibility->append(m_pTempVisibility);
+	}
+}
+
+void CXLightSun::updateFrustum()
+{
+	assert(m_pCamera);
+	
+	float3 vLightDir = getDirection() * LIGHTS_DIR_BASE;
+	float3 vUpDir = getDirection() * float3(1.0f, 0.0f, 0.0f);
+
+	float3 vStart;
+	m_pCamera->getPosition(&vStart);
+	float3 vDir;
+	m_pCamera->getLook(&vDir);
+	vDir = SMVector3Normalize(vDir);
+	float3 vRight;
+	m_pCamera->getRight(&vRight);
+	float3 vUp;
+	m_pCamera->getUp(&vUp);
+
+	static const int *r_win_width = GET_PCVAR_INT("r_win_width");
+	static const int *r_win_height = GET_PCVAR_INT("r_win_height");
+	static const float *r_effective_fov = GET_PCVAR_FLOAT("r_default_fov");
+
+	float fAspectRatio = (float)*r_win_width / (float)*r_win_height;
+	float fFovTan = tanf(*r_effective_fov * 0.5f);
+
+	{
+		static const float *s_pfRPSSMQuality = GET_PCVAR_FLOAT("r_pssm_quality");
+		const float fSize = 512.0f * *s_pfRPSSMQuality;
+
+
+		
+		static const float *r_near = GET_PCVAR_FLOAT("r_near");
+		static const float *r_far = GET_PCVAR_FLOAT("r_far");
+		static const float *r_pssm_max_distance = GET_PCVAR_FLOAT("r_pssm_max_distance");
+
+		static const int *r_pssm_splits = GET_PCVAR_INT("r_pssm_splits");
+		if(*r_pssm_splits < 1)
+		{
+			Core_0SetCVarInt("r_pssm_splits", 1);
+		}
+		else if(*r_pssm_splits > PSSM_MAX_SPLITS)
+		{
+			Core_0SetCVarInt("r_pssm_splits", PSSM_MAX_SPLITS);
+		}
+
+		float fSplitWeight = 0.8f;
+		float fShadowDistance = min(*r_pssm_max_distance, *r_far);
+
+		float fMaxDistanceSun = getMaxDistance();
+		if(fShadowDistance > fMaxDistanceSun)
+		{
+			fShadowDistance = fMaxDistanceSun;
+		}
+
+		float aSplitDistances[PSSM_MAX_SPLITS];
+		for(int i = 0; i < *r_pssm_splits; ++i)
+		{
+			float f = (i + 1.0f) / *r_pssm_splits;
+			float fLogDistance = *r_near * pow(fShadowDistance / *r_near, f);
+			float fUniformDistance = *r_near + (fShadowDistance - *r_near) * f;
+			aSplitDistances[i] = lerpf(fUniformDistance, fLogDistance, fSplitWeight);
+
+			if(i == 0)
+			{
+				m_splits[i].vNearFar = float2(*r_near, aSplitDistances[i]);
+			}
+			else
+			{
+				m_splits[i].vNearFar = float2(aSplitDistances[i - 1], aSplitDistances[i]);
+			}
+		}
+
+
+		
+		for(int i = 0; i < *r_pssm_splits; ++i)
+		{
+			Split &split = m_splits[i];
+
+			float3 vNearCenter = vStart + vDir * split.vNearFar.x;
+			float3 vFarCenter = vStart + vDir * split.vNearFar.y;
+
+			float fNearHalfHeight = fFovTan * split.vNearFar.x;
+			float fFarHalfHeight = fFovTan * split.vNearFar.y;
+
+			float fNearHalfWidth = fNearHalfHeight * fAspectRatio;
+			float fFarHalfWidth = fFarHalfHeight * fAspectRatio;
+
+			float3 vA = vNearCenter - vUp * fNearHalfHeight + vRight * fNearHalfWidth;
+			float3 vB = vFarCenter + vUp * fFarHalfHeight - vRight * fFarHalfWidth;
+			float3 vC = vFarCenter - vUp * fFarHalfHeight + vRight * fFarHalfWidth;
+
+			float3 vCenter = SMTriangleCircumcenter3(vA, vB, vC);
+
+
+			float fRadius = SMVector3Length(vCenter - vA);
+
+			SMMATRIX mLight(SMMatrixTranspose(SMMATRIX(
+				float4(SMVector3Cross(vUpDir, vLightDir)),
+				float4(vUpDir),
+				float4(vLightDir),
+				float4(0.0f, 0.0f, 0.0f, 1.0f)
+				)));
+			SMMATRIX mLightInv = SMMatrixInverse(NULL, mLight);
+
+			vCenter = SMVector3Transform(vCenter, mLight);
+			float fStep = (fRadius * 2.0f / fSize);
+			vCenter.x -= fmodf(vCenter.x, fStep);
+			vCenter.y -= fmodf(vCenter.y, fStep);
+
+			vCenter = SMVector3Transform(vCenter, mLightInv);
+
+			float fMaxDistance = PSSM_LIGHT_FAR;
+
+			split.mProj = SMMatrixOrthographicLH(fRadius * 2.0f, fRadius * 2.0f, PSSM_LIGHT_NEAR, fMaxDistance);
+			split.mView = SMMatrixLookToLH(vCenter - vLightDir * (fMaxDistance /*- fRadius * 2*/ * 0.5f), vLightDir, vUpDir);
+
+			m_mVPs[i] = SMMatrixTranspose(split.mView * split.mProj);
+
+			if(i == 0)
+			{
+				m_pFrustum->update(&split.mView, &split.mProj);
+			}
+			else
+			{
+				m_pPSSMFrustum[i - 1]->update(&split.mView, &split.mProj);
+			}
+		}
+	}
+
+	static const int *lpv_cascades_count = GET_PCVAR_INT("lpv_cascades_count");
+	int iCascades = *lpv_cascades_count;
+	if(iCascades < 0)
+	{
+		iCascades = 0;
+	}
+	if(iCascades > LPV_CASCADES_COUNT)
+	{
+		iCascades = LPV_CASCADES_COUNT;
+	}
+
+	if(iCascades)
+	{
+		static const float *lpv_size_2 = GET_PCVAR_FLOAT("lpv_size_2");
+		
+		float3 vGridCenter = vStart + vDir * (LPV_GRID_SIZE / 2 - LPV_STEP_COUNT) * *lpv_size_2;
+		//float fGridRadius = sqrtf(16.0f * 16.0f * 3.0f);
+		float fDim = 16.0f * *lpv_size_2;
+		float fGridRadius = sqrtf(fDim * fDim * 3.0f);
+
+
+		SMMATRIX mLight(SMMatrixTranspose(SMMATRIX(
+			float4(SMVector3Cross(vUpDir, vLightDir)),
+			float4(vUpDir),
+			float4(vLightDir),
+			float4(0.0f, 0.0f, 0.0f, 1.0f)
+			)));
+		SMMATRIX mLightInv = SMMatrixInverse(NULL, mLight);
+
+		vGridCenter = SMVector3Transform(vGridCenter, mLight);
+		float fStep = (fGridRadius * 2.0f / (float)RSM_SUN_SIZE);
+		vGridCenter.x -= fmodf(vGridCenter.x, fStep);
+		vGridCenter.y -= fmodf(vGridCenter.y, fStep);
+
+		vGridCenter = SMVector3Transform(vGridCenter, mLightInv);
+
+		float fMaxDistance = PSSM_LIGHT_FAR;
+
+		m_mReflectiveProj = SMMatrixOrthographicLH(fGridRadius * 2.0f, fGridRadius * 2.0f, PSSM_LIGHT_NEAR, fMaxDistance);
+		m_mReflectiveView = SMMatrixLookToLH(vGridCenter - vLightDir * (fMaxDistance /*- fRadius * 2*/ * 0.5f), vLightDir, vUpDir);
+
+		m_pReflectiveFrustum->update(&m_mReflectiveView, &m_mReflectiveProj);
+	}
+}
+
+void CXLightSun::setCamera(ICamera *pCamera)
+{
+	m_pCamera = pCamera;
+}
+
+const SMMATRIX* CXLightSun::getPSSMVPs() const
+{
+	return(m_mVPs);
+}
+
+const CXLightSun::Split* CXLightSun::getPSSMsplits() const
+{
+	return(m_splits);
+}
+
+IXRenderableVisibility* CXLightSun::getReflectiveVisibility()
+{
+	return(m_pReflectiveVisibility);
+}
+
+void CXLightSun::getReflectiveVP(SMMATRIX *pView, SMMATRIX *pProj) const
+{
+	*pView = m_mReflectiveView;
+	*pProj = m_mReflectiveProj;
 }
 
 //##########################################################################
@@ -425,7 +650,7 @@ void CXLightSpot::updateFrustum()
 	m_pFrustum->update(&mView, &mProj);
 }
 
-void CXLightSpot::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax)
+void CXLightSpot::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV)
 {
 	m_renderType = LRT_NONE;
 
@@ -436,5 +661,5 @@ void CXLightSpot::updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin,
 		m_renderType |= LRT_LIGHT;
 	}
 
-	CXLight::updateVisibility(pMainCamera, vLPVmin, vLPVmax);
+	CXLight::updateVisibility(pMainCamera, vLPVmin, vLPVmax, useLPV);
 }
diff --git a/source/light/light.h b/source/light/light.h
index c5952cd50daac142300b905c56bf552722b27206..b67b0c18b011b1268bfc6f4f9df94425e67e64dd 100644
--- a/source/light/light.h
+++ b/source/light/light.h
@@ -19,6 +19,10 @@ See the license in LICENSE
 
 #include "IXLight.h"
 
+#define PSSM_MAX_SPLITS 6
+#define PSSM_LIGHT_NEAR 0.1f
+#define PSSM_LIGHT_FAR 5000.0f
+
 class CLightSystem;
 class CXLight: public virtual IXLight
 {
@@ -52,7 +56,7 @@ public:
 
 	float getMaxDistance();
 
-	virtual void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax);
+	virtual void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV);
 	IXRenderableVisibility *getVisibility() override;
 	LIGHT_RENDER_TYPE getRenderType()
 	{
@@ -126,7 +130,7 @@ public:
 
 	void updateFrustum() override;
 
-	void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax) override;
+	void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV) override;
 
 protected:
 	void updatePSConstants(IGXDevice *pDevice);
@@ -140,7 +144,7 @@ public:
 	SX_ALIGNED_OP_MEM2();
 
 	CXLightSun(CLightSystem *pLightSystem);
-	void XMETHODCALLTYPE Release();
+	~CXLightSun();
 
 	SMQuaternion getDirection();
 	void setDirection(const SMQuaternion &qDirection);
@@ -148,7 +152,27 @@ public:
 	float getMaxDistance();
 	void setMaxDistance(float fMax);
 
-	void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax) override;
+	void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV) override;
+
+	void updateFrustum() override;
+
+	void setCamera(ICamera *pCamera);
+
+	const SMMATRIX* getPSSMVPs() const;
+	void getReflectiveVP(SMMATRIX *pView, SMMATRIX *pProj) const;
+
+	IXRenderableVisibility* getReflectiveVisibility();
+
+	struct Split
+	{
+		SX_ALIGNED_OP_MEM2();
+
+		float2 vNearFar;
+		float4x4 mView;
+		float4x4 mProj;
+	};
+
+	const Split* getPSSMsplits() const;
 
 protected:
 	void updatePSConstants(IGXDevice *pDevice);
@@ -157,6 +181,19 @@ protected:
 	SMQuaternion m_qDirection;
 
 	float m_fMaxDistance = 1000.0f;
+
+	ICamera *m_pCamera = NULL;
+
+	Split m_splits[PSSM_MAX_SPLITS];
+
+	SMMATRIX m_mVPs[PSSM_MAX_SPLITS];
+	SMMATRIX m_mReflectiveView;
+	SMMATRIX m_mReflectiveProj;
+
+	IFrustum *m_pPSSMFrustum[PSSM_MAX_SPLITS - 1];
+	IFrustum *m_pReflectiveFrustum = NULL;
+	IXRenderableVisibility *m_pReflectiveVisibility = NULL;
+	IXRenderableVisibility *m_pTempVisibility = NULL;
 };
 
 class CXLightSpot: public CXLight, public virtual IXLightSpot
@@ -174,7 +211,7 @@ public:
 
 	SMMATRIX getWorldTM();
 
-	void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax) override;
+	void updateVisibility(ICamera *pMainCamera, const float3 &vLPVmin, const float3 &vLPVmax, bool useLPV) override;
 protected:
 	void updatePSConstants(IGXDevice *pDevice);
 	void updateFrustum() override;
diff --git a/source/light/shadow.cpp b/source/light/shadow.cpp
index e15b175b7c05f0fee1de189cd3a02630870e89cb..20bdaa8384f038e453bc3c049c4e0d9c7598c432 100644
--- a/source/light/shadow.cpp
+++ b/source/light/shadow.cpp
@@ -547,7 +547,7 @@ void CShadowPSSM::init(IGXDevice *pContext, UINT uSize)
 
 	
 	m_pShaderDataPS = m_pDevice->createConstantBuffer(sizeof(m_shaderData.ps));
-	m_pShaderDataGS = m_pDevice->createConstantBuffer(sizeof(m_shaderData.gs));
+	m_pShaderDataGS = m_pDevice->createConstantBuffer(sizeof(SMMATRIX) * PSSM_MAX_SPLITS);
 	m_pCameraShaderDataVS = m_pDevice->createConstantBuffer(sizeof(m_cameraShaderData.vs));
 }
 
@@ -602,7 +602,7 @@ void CShadowPSSM::process(IXRenderPipeline *pRenderPipeline)
 	mem_release(pLightConstants);
 
 	pRenderPipeline->renderShadows();
-	pRenderPipeline->renderStage(XRS_SHADOWS, NULL); // m_pLight->getVisibility());
+	pRenderPipeline->renderStage(XRS_SHADOWS, m_pLight->getVisibility());
 
 	pCtx->setColorTarget(NULL);
 
@@ -612,122 +612,11 @@ void CShadowPSSM::process(IXRenderPipeline *pRenderPipeline)
 	}*/
 }
 
-#define PSSM_LIGHT_NEAR 0.1f
-#define PSSM_LIGHT_FAR 5000.0f
-
 void CShadowPSSM::updateFrustums()
 {
-	assert(m_pCamera);
-
 	CXLightSun *pSunLight = (CXLightSun*)m_pLight;
-	float3 vLightDir = pSunLight->getDirection() * LIGHTS_DIR_BASE;
-	float3 vUpDir = pSunLight->getDirection() * float3(1.0f, 0.0f, 0.0f);
-
-	static const float *r_near = GET_PCVAR_FLOAT("r_near");
-	static const float *r_far = GET_PCVAR_FLOAT("r_far");
-	static const float *r_pssm_max_distance = GET_PCVAR_FLOAT("r_pssm_max_distance");
 	
-	static const int *r_win_width = GET_PCVAR_INT("r_win_width");
-	static const int *r_win_height = GET_PCVAR_INT("r_win_height");
-	static const float *r_effective_fov = GET_PCVAR_FLOAT("r_default_fov");
-	static const int *r_pssm_splits = GET_PCVAR_INT("r_pssm_splits");
-	if(*r_pssm_splits < 1)
-	{
-		Core_0SetCVarInt("r_pssm_splits", 1);
-	}
-	else if(*r_pssm_splits > PSSM_MAX_SPLITS)
-	{
-		Core_0SetCVarInt("r_pssm_splits", PSSM_MAX_SPLITS);
-	}
-
-	float fSplitWeight = 0.8f;
-	float fShadowDistance = min(*r_pssm_max_distance, *r_far);
-
-	float fMaxDistanceSun = pSunLight->getMaxDistance();
-	if(fShadowDistance > fMaxDistanceSun)
-	{
-		fShadowDistance = fMaxDistanceSun;
-	}
-
-	float aSplitDistances[PSSM_MAX_SPLITS];
-	for(int i = 0; i < *r_pssm_splits; ++i)
-	{
-		float f = (i + 1.0f) / *r_pssm_splits;
-		float fLogDistance = *r_near * pow(fShadowDistance / *r_near, f);
-		float fUniformDistance = *r_near + (fShadowDistance - *r_near) * f;
-		aSplitDistances[i] = lerpf(fUniformDistance, fLogDistance, fSplitWeight);
-
-		if(i == 0)
-		{
-			m_splits[i].vNearFar = float2(*r_near, aSplitDistances[i]);
-		}
-		else
-		{
-			m_splits[i].vNearFar = float2(aSplitDistances[i - 1], aSplitDistances[i]);
-		}
-	}
-
-
-	float3 vStart;
-	m_pCamera->getPosition(&vStart);
-	float3 vDir;
-	m_pCamera->getLook(&vDir);
-	vDir = SMVector3Normalize(vDir);
-	float3 vRight;
-	m_pCamera->getRight(&vRight);
-	float3 vUp;
-	m_pCamera->getUp(&vUp);
-	float fAspectRatio = (float)*r_win_width / (float)*r_win_height;
-	float fFovTan = tanf(*r_effective_fov * 0.5f);
-	for(int i = 0; i < *r_pssm_splits; ++i)
-	{
-		Split &split = m_splits[i];
-
-		float3 vNearCenter = vStart + vDir * split.vNearFar.x;
-		float3 vFarCenter = vStart + vDir * split.vNearFar.y;
-
-		float fNearHalfHeight = fFovTan * split.vNearFar.x;
-		float fFarHalfHeight = fFovTan * split.vNearFar.y;
-
-		float fNearHalfWidth = fNearHalfHeight * fAspectRatio;
-		float fFarHalfWidth = fFarHalfHeight * fAspectRatio;
-
-		float3 vA = vNearCenter - vUp * fNearHalfHeight + vRight * fNearHalfWidth;
-		float3 vB = vFarCenter + vUp * fFarHalfHeight - vRight * fFarHalfWidth;
-		float3 vC = vFarCenter - vUp * fFarHalfHeight + vRight * fFarHalfWidth;
-
-		float3 vCenter = SMTriangleCircumcenter3(vA, vB, vC);
- 
-
-		float fRadius = SMVector3Length(vCenter - vA);
-		//float fRadius1 = SMVector3Length(vCenter - vB);
-		//float fRadius2 = SMVector3Length(vCenter - vC);
-		
-		// vCenter.mmv = _mm_round_ps(vCenter, _MM_ROUND_NEAREST);
-
-		SMMATRIX mLight(SMMatrixTranspose(SMMATRIX(
-			float4(SMVector3Cross(vUpDir, vLightDir)),
-			float4(vUpDir),
-			float4(vLightDir),
-			float4(0.0f, 0.0f, 0.0f, 1.0f)
-			)));
-		SMMATRIX mLightInv = SMMatrixInverse(NULL, mLight);
-
-		vCenter = SMVector3Transform(vCenter, mLight);
-		float fStep = (fRadius * 2.0f / m_fSize);
-		vCenter.x -= fmodf(vCenter.x, fStep);
-		vCenter.y -= fmodf(vCenter.y, fStep);
-
-		vCenter = SMVector3Transform(vCenter, mLightInv);
-
-		float fMaxDistance = PSSM_LIGHT_FAR;
-
-		split.mProj = SMMatrixOrthographicLH(fRadius * 2.0f, fRadius * 2.0f, PSSM_LIGHT_NEAR, fMaxDistance);
-		split.mView = SMMatrixLookToLH(vCenter - vLightDir * (fMaxDistance /*- fRadius * 2*/ * 0.5f), vLightDir, vUpDir);
-
-		m_shaderData.gs.mVP[i] = SMMatrixTranspose(split.mView * split.mProj);
-	}
-	m_pShaderDataGS->update(&m_shaderData.gs);
+	m_pShaderDataGS->update(pSunLight->getPSSMVPs());
 }
 
 void CShadowPSSM::genShadow(IGXTexture2D *pGBufferDepth, IGXTexture2D *pGBufferNormals)
@@ -741,6 +630,8 @@ void CShadowPSSM::genShadow(IGXTexture2D *pGBufferDepth, IGXTexture2D *pGBufferN
 	// static const float *r_near = GET_PCVAR_FLOAT("r_near");
 	// static const float *r_far = GET_PCVAR_FLOAT("r_far");
 
+	CXLightSun *pSunLight = (CXLightSun*)m_pLight;
+
 	IGXContext *pCtx = m_pDevice->getThreadContext();
 
 	// const float c_fTexWidth = (float)pShadowMap->getWidth();
@@ -764,12 +655,14 @@ void CShadowPSSM::genShadow(IGXTexture2D *pGBufferDepth, IGXTexture2D *pGBufferN
 	//pCtx->setPSTexture(ms_pRandomTexture, 2);
 	pCtx->setPSTexture(pGBufferNormals, 3);
 
+	const CXLightSun::Split *pSplits = pSunLight->getPSSMsplits();
+
 	SMMATRIX tmp = SMMatrixTranspose(m_mScaleBiasMat);
 	for(int i = 0; i < PSSM_MAX_SPLITS; ++i)
 	{
 		//m_shaderData.ps.mMatrixTextureV[i] = tmp * m_shaderData.gs.mVP[i];
-		m_shaderData.ps.mMatrixTextureV[i] = SMMatrixTranspose(m_splits[i].mView);
-		m_shaderData.ps.mMatrixTextureP[i] = SMMatrixTranspose(m_splits[i].mProj * m_mScaleBiasMat);
+		m_shaderData.ps.mMatrixTextureV[i] = SMMatrixTranspose(pSplits[i].mView);
+		m_shaderData.ps.mMatrixTextureP[i] = SMMatrixTranspose(pSplits[i].mProj * m_mScaleBiasMat);
 	}
 	//m_shaderData.ps.vPixelMapSizeBias = float3(m_fBlurPixel / m_fSize, m_fSize, m_fBias);
 	m_shaderData.ps.vSizeBoundNearFar = float4(m_fSize, PSSM_LIGHT_NEAR * tanf(0.001f), PSSM_LIGHT_NEAR, PSSM_LIGHT_FAR);
@@ -937,61 +830,27 @@ void CReflectiveShadowSun::process(IXRenderPipeline *pRenderPipeline)
 
 	//m_pLight->setPSConstants(m_pDevice, 5);
 	pRenderPipeline->renderShadows();
-	pRenderPipeline->renderStage(XRS_SHADOWS, NULL); // m_pLight->getVisibility());
+	pRenderPipeline->renderStage(XRS_SHADOWS, ((CXLightSun*)m_pLight)->getReflectiveVisibility());
 
 	pCtx->setColorTarget(NULL);
 	pCtx->setColorTarget(NULL, 1);
 	pCtx->setColorTarget(NULL, 2);
 
+	/*
 	if(GetAsyncKeyState('U') < 0)
 	{
 		m_pDevice->saveTextureToFile("sm_depth.dds", m_pDepthMap);
 		m_pDevice->saveTextureToFile("sm_normal.dds", m_pNormalMap);
 		m_pDevice->saveTextureToFile("sm_flux.dds", m_pFluxMap);
 	}
+	*/
 }
 
 void CReflectiveShadowSun::updateFrustum()
 {
-	assert(m_pCamera);
-
-	CXLightSun *pSunLight = (CXLightSun*)m_pLight;
-	float3 vLightDir = pSunLight->getDirection() * LIGHTS_DIR_BASE;
-	float3 vUpDir = pSunLight->getDirection() * float3(1.0f, 0.0f, 0.0f);
-
-	float3 vStart;
-	m_pCamera->getPosition(&vStart);
-	float3 vDir;
-	m_pCamera->getLook(&vDir);
-	vDir = SMVector3Normalize(vDir);
-
-	//! @todo: fix grid center pos!
-	float3 vGridCenter = vStart;
-	//float fGridRadius = sqrtf(16.0f * 16.0f * 3.0f);
-	float fGridRadius = sqrtf(64.0f * 64.0f * 3.0f);
-
-	SMMATRIX mLight(SMMatrixTranspose(SMMATRIX(
-		float4(SMVector3Cross(vUpDir, vLightDir)),
-		float4(vUpDir),
-		float4(vLightDir),
-		float4(0.0f, 0.0f, 0.0f, 1.0f)
-		)));
-	SMMATRIX mLightInv = SMMatrixInverse(NULL, mLight);
-
-	vGridCenter = SMVector3Transform(vGridCenter, mLight);
-	float fStep = (fGridRadius * 2.0f / m_fSize);
-	vGridCenter.x -= fmodf(vGridCenter.x, fStep);
-	vGridCenter.y -= fmodf(vGridCenter.y, fStep);
-
-	vGridCenter = SMVector3Transform(vGridCenter, mLightInv);
-
-	float fMaxDistance = PSSM_LIGHT_FAR;
-
-	m_mProj = SMMatrixOrthographicLH(fGridRadius * 2.0f, fGridRadius * 2.0f, PSSM_LIGHT_NEAR, fMaxDistance);
-	m_mView = SMMatrixLookToLH(vGridCenter - vLightDir * (fMaxDistance /*- fRadius * 2*/ * 0.5f), vLightDir, vUpDir);
+	((CXLightSun*)m_pLight)->getReflectiveVP(&m_mView, &m_mProj);
 }
 
-
 void CReflectiveShadowSun::genLPV(bool isDebug)
 {
 	if(!m_pDevice || !(m_pLight->getRenderType() & LRT_LPV))
diff --git a/source/light/shadow.h b/source/light/shadow.h
index 8c6411b2cf207fbfe8f5bba5f2bfcf4795be7d07..dd2850b062c22ecec635547d3810f209b182270e 100644
--- a/source/light/shadow.h
+++ b/source/light/shadow.h
@@ -182,7 +182,6 @@ private:
 
 //##########################################################################
 
-#define PSSM_MAX_SPLITS 6
 class CShadowPSSM: public IBaseShadowMap
 {
 public:
@@ -215,6 +214,7 @@ private:
 
 	IGXTextureCube *m_pDepthMap = NULL;
 
+#if 0
 	struct Split
 	{
 		SX_ALIGNED_OP_MEM2();
@@ -224,6 +224,7 @@ private:
 		float4x4 mProj;
 	};
 	Split m_splits[PSSM_MAX_SPLITS];
+#endif
 
 	float4x4 m_mScaleBiasMat;
 	float m_fBias = 0.0001f;
@@ -235,10 +236,6 @@ private:
 
 	struct
 	{
-		struct
-		{
-			SMMATRIX mVP[PSSM_MAX_SPLITS];
-		} gs;
 		struct
 		{
 			SMMATRIX mMatrixTextureV[PSSM_MAX_SPLITS];
diff --git a/source/render/RenderableVisibility.cpp b/source/render/RenderableVisibility.cpp
index 5238e9dc5f72bdadad5235d5873bc8b9d5ceb543..c4dbf10e7ec169fc43804b175424efa13ca18fc1 100644
--- a/source/render/RenderableVisibility.cpp
+++ b/source/render/RenderableVisibility.cpp
@@ -17,7 +17,7 @@ CRenderableVisibility::~CRenderableVisibility()
 	mem_delete_a(m_ppVisibilities);
 }
 
-ID CRenderableVisibility::getPluginId()
+ID CRenderableVisibility::getPluginId() const
 {
 	return(m_idPlugin);
 }
@@ -76,6 +76,13 @@ IXRenderableVisibility *CRenderableVisibility::getVisibility(ID id)
 	return(m_ppVisibilities[id]);
 }
 
+const IXRenderableVisibility *CRenderableVisibility::getVisibility(ID id) const
+{
+	assert(ID_VALID(id) && (UINT)id < m_uRenderSystems);
+
+	return(m_ppVisibilities[id]);
+}
+
 void CRenderableVisibility::setVisibility(ID id, IXRenderableVisibility *pVisibility)
 {
 	assert(ID_VALID(id) && (UINT)id < m_uRenderSystems);
@@ -89,3 +96,31 @@ void CRenderableVisibility::setVisibility(ID id, IXRenderableVisibility *pVisibi
 		pVisibility->AddRef();
 	}
 }
+
+void CRenderableVisibility::append(const IXRenderableVisibility *pOther_)
+{
+	assert(pOther_->getPluginId() == getPluginId());
+	const CRenderableVisibility *pOther = (const CRenderableVisibility*)pOther_;
+
+	for(UINT i = 0; i < m_uRenderSystems; ++i)
+	{
+		if(m_ppVisibilities[i])
+		{
+			m_ppVisibilities[i]->append(pOther->getVisibility(i));
+		}
+	}
+}
+
+void CRenderableVisibility::substract(const IXRenderableVisibility *pOther_)
+{
+	assert(pOther_->getPluginId() == getPluginId());
+	const CRenderableVisibility *pOther = (const CRenderableVisibility*)pOther_;
+
+	for(UINT i = 0; i < m_uRenderSystems; ++i)
+	{
+		if(m_ppVisibilities[i])
+		{
+			m_ppVisibilities[i]->substract(pOther->getVisibility(i));
+		}
+	}
+}
diff --git a/source/render/RenderableVisibility.h b/source/render/RenderableVisibility.h
index 7da8d03dd6b1d128b681e0928afb965be03459e7..8dc72ff4983d10a975f32734cd5a44bda7553f2f 100644
--- a/source/render/RenderableVisibility.h
+++ b/source/render/RenderableVisibility.h
@@ -9,17 +9,22 @@ public:
 	CRenderableVisibility(ID idPlugin, UINT uRenderSystems);
 	~CRenderableVisibility();
 
-	ID getPluginId();
+	ID getPluginId() const override;
 
-	void setOcclusionCuller(IXOcclusionCuller *pOcclusionCuller);
+	void setOcclusionCuller(IXOcclusionCuller *pOcclusionCuller) override;
 
-	void updateForCamera(ICamera *pCamera, const IXRenderableVisibility *pReference = NULL);
+	void updateForCamera(ICamera *pCamera, const IXRenderableVisibility *pReference = NULL) override;
 
-	void updateForFrustum(const IFrustum *pFrustum, const IXRenderableVisibility *pReference = NULL);
+	void updateForFrustum(const IFrustum *pFrustum, const IXRenderableVisibility *pReference = NULL) override;
 
 	IXRenderableVisibility *getVisibility(ID id);
+	const IXRenderableVisibility *getVisibility(ID id) const;
 	void setVisibility(ID id, IXRenderableVisibility *pVisibility);
 
+	void append(const IXRenderableVisibility *pOther) override;
+
+	void substract(const IXRenderableVisibility *pOther) override;
+
 protected:
 	IXRenderableVisibility **m_ppVisibilities;
 	UINT m_uRenderSystems;
diff --git a/source/xcommon/IXRenderable.h b/source/xcommon/IXRenderable.h
index b1938bb1cf56e59f951701ed4cde64f036a22494..eea46c646f954de194b4e0351e5cd1120ea2672d 100644
--- a/source/xcommon/IXRenderable.h
+++ b/source/xcommon/IXRenderable.h
@@ -21,7 +21,7 @@ class IFrustum;
 class IXRenderableVisibility: public IXUnknown
 {
 public:
-	virtual ID getPluginId() = 0;
+	virtual ID getPluginId() const = 0;
 
 	//! Установка отсекателя по перекрытию
 	virtual void setOcclusionCuller(IXOcclusionCuller *pOcclusionCuller) = 0;
@@ -36,6 +36,12 @@ public:
 	    Возвращает ID задачи из менеджера задач для отслеживания завершения внутренних задач
 	*/
 //	virtual ID updateForCameraThreaded(ICamera *pCamera) = 0;
+
+	//! Добввление к текущему набору видимости другого набора
+	virtual void append(const IXRenderableVisibility *pOther) = 0;
+
+	//! Вычитание из текущего набора видимости другого набора
+	virtual void substract(const IXRenderableVisibility *pOther) = 0;
 };
 
 struct XTransparentObject