Explorar el Código

Feature: Shadow rendering is now supported for off-screen rendering (like light or refl. probes)
Bugfix: Fixed a crash when radial or spot light shadow was being culled
Bugfix: Shadows will properly not be rendered when they are disabled on a specific view
Bugfix: Fixes a crash when rendering cascaded shadow maps for multiple views

BearishSun hace 8 años
padre
commit
399db41841

+ 1 - 1
Source/RenderBeast/Include/BsRenderBeast.h

@@ -154,7 +154,7 @@ namespace bs
 		 * 
 		 * @note	Core thread only. 
 		 */
-		void renderViews(const RendererViewGroup& viewGroup, const FrameInfo& frameInfo);
+		void renderViews(RendererViewGroup& viewGroup, const FrameInfo& frameInfo);
 
 		/**
 		 * Renders all objects visible by the provided view.

+ 0 - 1
Source/RenderBeast/Include/BsRenderCompositor.h

@@ -311,7 +311,6 @@ namespace ct
 		/** @copydoc RenderCompositorNode::clear */
 		void clear() override;
 
-		SPtr<PooledRenderTexture> mLightOcclusionTex;
 		SPtr<RenderTexture> mRenderTarget;
 	};
 

+ 7 - 0
Source/RenderBeast/Include/BsRendererView.h

@@ -312,6 +312,12 @@ namespace bs { namespace ct
 		 */
 		Vector4 getNDCToUV() const;
 
+		/** Returns an index of this view within the parent view group. */
+		UINT32 getViewIdx() const { return mViewIdx; }
+
+		/** Assigns a view index to the view. To be called by the parent view group when the view is added to it. */
+		void _setViewIdx(UINT32 viewIdx) { mViewIdx = viewIdx; }
+
 		/**
 		 * Extracts the necessary values from the projection matrix that allow you to transform device Z value (range [0, 1]
 		 * into view Z value.
@@ -352,6 +358,7 @@ namespace bs { namespace ct
 		SPtr<GpuParamBlockBuffer> mParamBuffer;
 		VisibilityInfo mVisibility;
 		LightGrid mLightGrid;
+		UINT32 mViewIdx;
 	};
 
 	/** Contains one or multiple RendererView%s that are in some way related. */

+ 19 - 12
Source/RenderBeast/Include/BsShadowRendering.h

@@ -414,14 +414,20 @@ namespace bs { namespace ct
 		{
 			UINT32 lightIdx;
 			UINT32 mapSize;
-			SmallVector<float, 4> fadePercents;
+			SmallVector<float, 6> fadePercents;
 		};
 
 		/** Contains references to all shadows cast by a specific light. */
 		struct LightShadows
 		{
-			UINT32 startIdx;
-			UINT32 numShadows;
+			UINT32 startIdx = 0;
+			UINT32 numShadows = 0;
+		};
+
+		/** Contains references to all shadows cast by a specific light, per view. */
+		struct PerViewLightShadows
+		{
+			SmallVector<LightShadows, 6> viewShadows;
 		};
 	public:
 		ShadowRendering(UINT32 shadowMapSize);
@@ -430,17 +436,18 @@ namespace bs { namespace ct
 		void renderShadowMaps(RendererScene& scene, const RendererViewGroup& viewGroup, const FrameInfo& frameInfo);
 
 		/** 
-		 * Renders shadow occlusion values for the specified light, into the currently bound render target. 
-		 * The system uses shadow maps rendered by renderShadowMaps().
+		 * Renders shadow occlusion values for the specified light, through the provided view, into the currently bound
+		 * render target. The system uses shadow maps rendered by renderShadowMaps().
 		 */
-		void renderShadowOcclusion(const SceneInfo& sceneInfo, UINT32 shadowQuality, const RendererLight& light,
-			UINT32 viewIdx, GBufferTextures gbuffer) const;
+		void renderShadowOcclusion(const RendererView& view, UINT32 shadowQuality, const RendererLight& light, 
+			GBufferTextures gbuffer) const;
 
 		/** Changes the default shadow map size. Will cause all shadow maps to be rebuilt. */
 		void setShadowMapSize(UINT32 size);
 	private:
 		/** Renders cascaded shadow maps for the provided directional light viewed from the provided view. */
-		void renderCascadedShadowMaps(UINT32 viewIdx, UINT32 lightIdx, RendererScene& scene, const FrameInfo& frameInfo);
+		void renderCascadedShadowMaps(const RendererView& view, UINT32 lightIdx, RendererScene& scene, 
+			const FrameInfo& frameInfo);
 
 		/** Renders shadow maps for the provided spot light. */
 		void renderSpotShadowMap(const RendererLight& light, const ShadowMapOptions& options, RendererScene& scene,
@@ -455,15 +462,15 @@ namespace bs { namespace ct
 		 * that can be used for fading out small shadow maps.
 		 * 
 		 * @param[in]	light			Light for which to calculate the shadow map properties. Cannot be a directional light.
-		 * @param[in]	scene			Scene information containing all the views the light can be seen through.
+		 * @param[in]	viewGroup		All the views the shadow will (potentially) be seen through.
 		 * @param[in]	border			Border to reduce the shadow map size by, in pixels.
 		 * @param[out]	size			Optimal size of the shadow map, in pixels.
 		 * @param[out]	fadePercents	Value in range [0, 1] determining how much should the shadow map be faded out. Each
 		 *								entry corresponds to a single view.
 		 * @param[out]	maxFadePercent	Maximum value in the @p fadePercents array.
 		 */
-		void calcShadowMapProperties(const RendererLight& light, RendererScene& scene, UINT32 border, UINT32& size, 
-			SmallVector<float, 4>& fadePercents, float& maxFadePercent) const;
+		void calcShadowMapProperties(const RendererLight& light, const RendererViewGroup& viewGroup, UINT32 border, 
+			UINT32& size, SmallVector<float, 4>& fadePercents, float& maxFadePercent) const;
 
 		/**
 		 * Draws a mesh representing near and far planes at the provided coordinates. The mesh is constructed using
@@ -564,7 +571,7 @@ namespace bs { namespace ct
 
 		Vector<LightShadows> mSpotLightShadows;
 		Vector<LightShadows> mRadialLightShadows;
-		Vector<UINT32> mDirectionalLightShadows;
+		Vector<PerViewLightShadows> mDirectionalLightShadows;
 
 		SPtr<VertexDeclaration> mPositionOnlyVD;
 

+ 6 - 6
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -307,10 +307,6 @@ namespace bs { namespace ct
 		mMainViewGroup->setViews(views.data(), (UINT32)views.size());
 		mMainViewGroup->determineVisibility(sceneInfo);
 
-		// Render shadow maps
-		ShadowRendering& shadowRenderer = mMainViewGroup->getShadowRenderer();
-		shadowRenderer.renderShadowMaps(*mScene, *mMainViewGroup, frameInfo);
-
 		// Update reflection probe array if required
 		updateReflProbeArray();
 
@@ -329,11 +325,15 @@ namespace bs { namespace ct
 		gProfilerCPU().endSample("renderAllCore");
 	}
 
-	void RenderBeast::renderViews(const RendererViewGroup& viewGroup, const FrameInfo& frameInfo)
+	void RenderBeast::renderViews(RendererViewGroup& viewGroup, const FrameInfo& frameInfo)
 	{
 		const SceneInfo& sceneInfo = mScene->getSceneInfo();
 		const VisibilityInfo& visibility = viewGroup.getVisibilityInfo();
 
+		// Render shadow maps
+		ShadowRendering& shadowRenderer = viewGroup.getShadowRenderer();
+		shadowRenderer.renderShadowMaps(*mScene, viewGroup, frameInfo);
+
 		// Update various buffers required by each renderable
 		UINT32 numRenderables = (UINT32)sceneInfo.renderables.size();
 		for (UINT32 i = 0; i < numRenderables; i++)
@@ -579,7 +579,7 @@ namespace bs { namespace ct
 
 		SPtr<RenderSettings> settings = bs_shared_ptr_new<RenderSettings>();
 		settings->enableHDR = hdr;
-		settings->enableShadows = false; // Note: If I ever change this I need to make sure that shadow map rendering is aware of this view (currently it is only aware of main camera views)
+		settings->enableShadows = true;
 		settings->enableIndirectLighting = false;
 		settings->screenSpaceReflections.enabled = false;
 		settings->ambientOcclusion.enabled = false;

+ 32 - 36
Source/RenderBeast/Source/BsRenderCompositor.cpp

@@ -508,6 +508,13 @@ namespace bs { namespace ct
 
 	void RCNodeStandardDeferredLighting::render(const RenderCompositorNodeInputs& inputs)
 	{
+		RCNodeTiledDeferredLighting* tileDeferredNode = static_cast<RCNodeTiledDeferredLighting*>(inputs.inputNodes[0]);
+		output = tileDeferredNode->output;
+
+		// If shadows are disabled we handle all lights through tiled deferred
+		if (!inputs.view.getRenderSettings().enableShadows)
+			return;
+
 		GpuResourcePool& resPool = GpuResourcePool::instance();
 		const RendererViewProperties& viewProps = inputs.view.getProperties();
 
@@ -515,20 +522,17 @@ namespace bs { namespace ct
 		UINT32 height = viewProps.viewRect.height;
 		UINT32 numSamples = viewProps.numSamples;
 
-		RCNodeTiledDeferredLighting* tileDeferredNode = static_cast<RCNodeTiledDeferredLighting*>(inputs.inputNodes[0]);
-		output = tileDeferredNode->output;
-
 		RCNodeGBuffer* gbufferNode = static_cast<RCNodeGBuffer*>(inputs.inputNodes[1]);
 		RCNodeSceneDepth* sceneDepthNode = static_cast<RCNodeSceneDepth*>(inputs.inputNodes[2]);
 
 		// Allocate light occlusion
-		mLightOcclusionTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_R8, width,
+		SPtr<PooledRenderTexture> lightOcclusionTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_R8, width,
 			height, TU_RENDERTARGET, numSamples, false));
 
 		bool rebuildRT = false;
 		if (mRenderTarget != nullptr)
 		{
-			rebuildRT |= mRenderTarget->getColorTexture(0) != mLightOcclusionTex->texture;
+			rebuildRT |= mRenderTarget->getColorTexture(0) != lightOcclusionTex->texture;
 			rebuildRT |= mRenderTarget->getDepthStencilTexture() != sceneDepthNode->depthTex->texture;
 		}
 		else
@@ -537,7 +541,7 @@ namespace bs { namespace ct
 		if (rebuildRT)
 		{
 			RENDER_TEXTURE_DESC lightOcclusionRTDesc;
-			lightOcclusionRTDesc.colorSurfaces[0].texture = mLightOcclusionTex->texture;
+			lightOcclusionRTDesc.colorSurfaces[0].texture = lightOcclusionTex->texture;
 			lightOcclusionRTDesc.colorSurfaces[0].face = 0;
 			lightOcclusionRTDesc.colorSurfaces[0].numFaces = 1;
 			lightOcclusionRTDesc.colorSurfaces[0].mipLevel = 0;
@@ -559,46 +563,38 @@ namespace bs { namespace ct
 		const VisibleLightData& lightData = inputs.viewGroup.getVisibleLightData();
 		const ShadowRendering& shadowRenderer = inputs.viewGroup.getShadowRenderer();
 
-		Camera* sceneCamera = inputs.view.getSceneCamera();
-
-		// Note: Currently skipping shadow rendering for any views that aren't part of the scene (like temporary ones)
-		if (sceneCamera != nullptr)
+		RenderAPI& rapi = RenderAPI::instance();
+		for (UINT32 i = 0; i < (UINT32)LightType::Count; i++)
 		{
-			RenderAPI& rapi = RenderAPI::instance();
-			UINT32 viewIdx = sceneCamera->getRendererId();
-			for (UINT32 i = 0; i < (UINT32)LightType::Count; i++)
-			{
-				LightType lightType = (LightType)i;
+			LightType lightType = (LightType)i;
 
-				auto& lights = lightData.getLights(lightType);
-				UINT32 count = lightData.getNumShadowedLights(lightType);
-				UINT32 offset = lightData.getNumUnshadowedLights(lightType);
+			auto& lights = lightData.getLights(lightType);
+			UINT32 count = lightData.getNumShadowedLights(lightType);
+			UINT32 offset = lightData.getNumUnshadowedLights(lightType);
 
-				for (UINT32 j = 0; j < count; j++)
-				{
-					rapi.setRenderTarget(mRenderTarget, FBT_DEPTH, RT_DEPTH_STENCIL);
+			for (UINT32 j = 0; j < count; j++)
+			{
+				rapi.setRenderTarget(mRenderTarget, FBT_DEPTH, RT_DEPTH_STENCIL);
 
-					Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
-					rapi.setViewport(area);
+				Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
+				rapi.setViewport(area);
 
-					rapi.clearViewport(FBT_COLOR, Color::ZERO);
+				rapi.clearViewport(FBT_COLOR, Color::ZERO);
 
-					UINT32 lightIdx = offset + j;
-					const RendererLight& light = *lights[lightIdx];
-					shadowRenderer.renderShadowOcclusion(inputs.scene, inputs.options.shadowFilteringQuality,
-						light, viewIdx, gbuffer);
+				UINT32 lightIdx = offset + j;
+				const RendererLight& light = *lights[lightIdx];
+				shadowRenderer.renderShadowOcclusion(inputs.view, inputs.options.shadowFilteringQuality, light, gbuffer);
 
-					rapi.setRenderTarget(output->renderTarget, FBT_DEPTH | FBT_STENCIL, RT_COLOR0 | RT_DEPTH_STENCIL);
-					StandardDeferred::instance().renderLight(lightType, light, inputs.view, gbuffer,
-						mLightOcclusionTex->texture);
-				}
+				rapi.setRenderTarget(output->renderTarget, FBT_DEPTH | FBT_STENCIL, RT_COLOR0 | RT_DEPTH_STENCIL);
+				StandardDeferred::instance().renderLight(lightType, light, inputs.view, gbuffer,
+					lightOcclusionTex->texture);
 			}
-
-			// Makes sure light accumulation can be read by following passes
-			rapi.setRenderTarget(nullptr);
 		}
 
-		resPool.release(mLightOcclusionTex);
+		// Makes sure light accumulation can be read by following passes
+		rapi.setRenderTarget(nullptr);
+
+		resPool.release(lightOcclusionTex);
 	}
 
 	void RCNodeStandardDeferredLighting::clear()

+ 5 - 2
Source/RenderBeast/Source/BsRendererView.cpp

@@ -84,13 +84,13 @@ namespace bs { namespace ct
 	}
 
 	RendererView::RendererView()
-		:mRenderSettingsHash(0)
+		: mCamera(nullptr), mRenderSettingsHash(0), mViewIdx(-1)
 	{
 		mParamBuffer = gPerCameraParamDef.createBuffer();
 	}
 
 	RendererView::RendererView(const RENDERER_VIEW_DESC& desc)
-		: mProperties(desc), mTargetDesc(desc.target), mCamera(desc.sceneCamera), mRenderSettingsHash(0)
+		: mProperties(desc), mTargetDesc(desc.target), mCamera(desc.sceneCamera), mRenderSettingsHash(0), mViewIdx(-1)
 	{
 		mParamBuffer = gPerCameraParamDef.createBuffer();
 		mProperties.prevViewProjTransform = mProperties.viewProjTransform;
@@ -505,7 +505,10 @@ namespace bs { namespace ct
 		mViews.clear();
 
 		for (UINT32 i = 0; i < numViews; i++)
+		{
 			mViews.push_back(views[i]);
+			views[i]->_setViewIdx(i);
+		}
 	}
 
 	void RendererViewGroup::determineVisibility(const SceneInfo& sceneInfo)

+ 74 - 55
Source/RenderBeast/Source/BsShadowRendering.cpp

@@ -615,6 +615,8 @@ namespace bs { namespace ct
 		for (UINT32 i = 0; i < (UINT32)sceneInfo.spotLights.size(); ++i)
 		{
 			const RendererLight& light = sceneInfo.spotLights[i];
+			mSpotLightShadows[i].startIdx = shadowInfoCount;
+			mSpotLightShadows[i].numShadows = 0;
 
 			// Note: I'm using visibility across all views, while I could be using visibility for every view individually,
 			// if I kept that information somewhere
@@ -625,22 +627,21 @@ namespace bs { namespace ct
 			options.lightIdx = i;
 
 			float maxFadePercent;
-			calcShadowMapProperties(light, scene, SHADOW_MAP_BORDER, options.mapSize, options.fadePercents, maxFadePercent);
+			calcShadowMapProperties(light, viewGroup, SHADOW_MAP_BORDER, options.mapSize, options.fadePercents, maxFadePercent);
 
 			// Don't render shadow maps that will end up nearly completely faded out
 			if (maxFadePercent < 0.005f)
 				continue;
 
 			mSpotLightShadowOptions.push_back(options);
-			mSpotLightShadows[i].startIdx = shadowInfoCount;
-			mSpotLightShadows[i].numShadows = 0;
-
 			shadowInfoCount++; // For now, always a single fully dynamic shadow for a single light, but that may change
 		}
 
 		for (UINT32 i = 0; i < (UINT32)sceneInfo.radialLights.size(); ++i)
 		{
 			const RendererLight& light = sceneInfo.radialLights[i];
+			mRadialLightShadows[i].startIdx = shadowInfoCount;
+			mRadialLightShadows[i].numShadows = 0;
 
 			// Note: I'm using visibility across all views, while I could be using visibility for every view individually,
 			// if I kept that information somewhere
@@ -651,15 +652,13 @@ namespace bs { namespace ct
 			options.lightIdx = i;
 
 			float maxFadePercent;
-			calcShadowMapProperties(light, scene, 0, options.mapSize, options.fadePercents, maxFadePercent);
+			calcShadowMapProperties(light, viewGroup, 0, options.mapSize, options.fadePercents, maxFadePercent);
 
 			// Don't render shadow maps that will end up nearly completely faded out
 			if (maxFadePercent < 0.005f)
 				continue;
 
 			mRadialLightShadowOptions.push_back(options);
-			mRadialLightShadows[i].startIdx = shadowInfoCount;
-			mRadialLightShadows[i].numShadows = 0;
 
 			shadowInfoCount++; // For now, always a single fully dynamic shadow for a single light, but that may change
 		}
@@ -706,8 +705,11 @@ namespace bs { namespace ct
 			if (!light.internal->getCastsShadow())
 				return;
 
-			for (UINT32 j = 0; j < (UINT32)sceneInfo.views.size(); ++j)
-				renderCascadedShadowMaps(j, i, scene, frameInfo);
+			UINT32 numViews = viewGroup.getNumViews();
+			mDirectionalLightShadows[i].viewShadows.resize(numViews);
+
+			for (UINT32 j = 0; j < numViews; ++j)
+				renderCascadedShadowMaps(*viewGroup.getView(j), i, scene, frameInfo);
 		}
 
 		for(auto& entry : mSpotLightShadowOptions)
@@ -806,19 +808,18 @@ namespace bs { namespace ct
 		return shadowMapTfrm * mixedToShadow;
 	}
 
-	void ShadowRendering::renderShadowOcclusion(const SceneInfo& sceneInfo, UINT32 shadowQuality, 
-		const RendererLight& rendererLight, UINT32 viewIdx, GBufferTextures gbuffer) const
+	void ShadowRendering::renderShadowOcclusion(const RendererView& view, UINT32 shadowQuality, 
+		const RendererLight& rendererLight, GBufferTextures gbuffer) const
 	{
 		const Light* light = rendererLight.internal;
 		UINT32 lightIdx = light->getRendererId();
 
-		RendererView* view = sceneInfo.views[viewIdx];
-		auto viewProps = view->getProperties();
+		auto viewProps = view.getProperties();
 
 		const Matrix4& viewP = viewProps.projTransform;
 		Matrix4 viewInvVP = viewProps.viewProjTransform.inverse();
 
-		SPtr<GpuParamBlockBuffer> perViewBuffer = view->getPerViewBuffer();
+		SPtr<GpuParamBlockBuffer> perViewBuffer = view.getPerViewBuffer();
 
 		RenderAPI& rapi = RenderAPI::instance();
 		const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
@@ -827,6 +828,7 @@ namespace bs { namespace ct
 		SPtr<GpuParamBlockBuffer> shadowParamBuffer = gShadowProjectParamsDef.createBuffer();
 		SPtr<GpuParamBlockBuffer> shadowOmniParamBuffer = gShadowProjectOmniParamsDef.createBuffer();
 
+		UINT32 viewIdx = view.getViewIdx();
 		Vector<const ShadowInfo*> shadowInfos;
 
 		if(light->getType() == LightType::Radial)
@@ -890,13 +892,17 @@ namespace bs { namespace ct
 			}
 			else // Directional
 			{
-				UINT32 mapIdx = mDirectionalLightShadows[lightIdx];
-				const ShadowCascadedMap& cascadedMap = mCascadedShadowMaps[mapIdx];
+				const LightShadows& shadows = mDirectionalLightShadows[lightIdx].viewShadows[viewIdx];
+				if (shadows.numShadows > 0)
+				{
+					UINT32 mapIdx = shadows.startIdx;
+					const ShadowCascadedMap& cascadedMap = mCascadedShadowMaps[mapIdx];
 
-				// Render cascades in far to near order.
-				// Note: If rendering other non-cascade maps they should be rendered after cascades.
-				for (INT32 i = NUM_CASCADE_SPLITS - 1; i >= 0; i--)
-					shadowInfos.push_back(&cascadedMap.getShadowInfo(i));
+					// Render cascades in far to near order.
+					// Note: If rendering other non-cascade maps they should be rendered after cascades.
+					for (INT32 i = NUM_CASCADE_SPLITS - 1; i >= 0; i--)
+						shadowInfos.push_back(&cascadedMap.getShadowInfo(i));
+				}
 			}
 
 			for(auto& shadowInfo : shadowInfos)
@@ -997,21 +1003,29 @@ namespace bs { namespace ct
 		}
 	}
 
-	void ShadowRendering::renderCascadedShadowMaps(UINT32 viewIdx, UINT32 lightIdx, RendererScene& scene, 
+	void ShadowRendering::renderCascadedShadowMaps(const RendererView& view, UINT32 lightIdx, RendererScene& scene, 
 		const FrameInfo& frameInfo)
 	{
+		UINT32 viewIdx = view.getViewIdx();
+		LightShadows& lightShadows = mDirectionalLightShadows[lightIdx].viewShadows[viewIdx];
+
+		if (!view.getRenderSettings().enableShadows)
+		{
+			lightShadows.startIdx = -1;
+			lightShadows.numShadows = 0;
+			return;
+		}
+
 		// Note: Currently I'm using spherical bounds for the cascaded frustum which might result in non-optimal usage
 		// of the shadow map. A different approach would be to generate a bounding box and then both adjust the aspect
 		// ratio (and therefore dimensions) of the shadow map, as well as rotate the camera so the visible area best fits
 		// in the map. It remains to be seen if this is viable.
 		const SceneInfo& sceneInfo = scene.getSceneInfo();
 
-		const RendererView* view = sceneInfo.views[viewIdx];
 		const RendererLight& rendererLight = sceneInfo.directionalLights[lightIdx];
 		Light* light = rendererLight.internal;
 
 		RenderAPI& rapi = RenderAPI::instance();
-		const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
 
 		Vector3 lightDir = -light->getRotation().zAxis();
 		SPtr<GpuParamBlockBuffer> shadowParamsBuffer = gShadowParamsDef.createBuffer();
@@ -1055,7 +1069,7 @@ namespace bs { namespace ct
 		for (int i = 0; i < NUM_CASCADE_SPLITS; ++i)
 		{
 			Sphere frustumBounds;
-			ConvexVolume cascadeCullVolume = getCSMSplitFrustum(*view, -lightDir, i, NUM_CASCADE_SPLITS, frustumBounds);
+			ConvexVolume cascadeCullVolume = getCSMSplitFrustum(view, -lightDir, i, NUM_CASCADE_SPLITS, frustumBounds);
 
 			// Move the light at the boundary of the subject frustum, so we don't waste depth range
 			Vector3 frustumCenterViewSpace = viewMat.multiply(frustumBounds.getCenter());
@@ -1077,8 +1091,8 @@ namespace bs { namespace ct
 			shadowInfo.shadowVPTransform = proj * offsetViewMat;
 
 			// Determine split range
-			float splitNear = getCSMSplitDistance(*view, i, NUM_CASCADE_SPLITS);
-			float splitFar = getCSMSplitDistance(*view, i + 1, NUM_CASCADE_SPLITS);
+			float splitNear = getCSMSplitDistance(view, i, NUM_CASCADE_SPLITS);
+			float splitFar = getCSMSplitDistance(view, i + 1, NUM_CASCADE_SPLITS);
 
 			shadowInfo.depthNear = splitNear;
 			shadowInfo.depthFade = splitFar;
@@ -1126,7 +1140,8 @@ namespace bs { namespace ct
 			shadowMap.setShadowInfo(i, shadowInfo);
 		}
 
-		mDirectionalLightShadows[lightIdx] = shadowInfo.textureIdx;
+		lightShadows.startIdx = shadowInfo.textureIdx;
+		lightShadows.numShadows = 1;
 	}
 
 	void ShadowRendering::renderSpotShadowMap(const RendererLight& rendererLight, const ShadowMapOptions& options,
@@ -1419,48 +1434,52 @@ namespace bs { namespace ct
 		lightShadows.numShadows++;
 	}
 
-	void ShadowRendering::calcShadowMapProperties(const RendererLight& light, RendererScene& scene, UINT32 border,
-		UINT32& size, SmallVector<float, 4>& fadePercents, float& maxFadePercent) const
+	void ShadowRendering::calcShadowMapProperties(const RendererLight& light, const RendererViewGroup& viewGroup, 
+		UINT32 border, UINT32& size, SmallVector<float, 4>& fadePercents, float& maxFadePercent) const
 	{
 		const static float SHADOW_TEXELS_PER_PIXEL = 1.0f;
 
-		const SceneInfo& sceneInfo = scene.getSceneInfo();
-
 		// Find a view in which the light has the largest radius
 		float maxMapSize = 0.0f;
 		maxFadePercent = 0.0f;
-		for (int i = 0; i < (int)sceneInfo.views.size(); ++i)
+		for (int i = 0; i < (int)viewGroup.getNumViews(); ++i)
 		{
-			const RendererViewProperties& viewProps = sceneInfo.views[i]->getProperties();
+			const RendererView& view = *viewGroup.getView(i);
+			const RendererViewProperties& viewProps = view.getProperties();
+			const RenderSettings& viewSettings = view.getRenderSettings();
+
+			if(!viewSettings.enableShadows)
+				fadePercents.push_back(0.0f);
+			{
+				// Approximation for screen space sphere radius: screenSize * 0.5 * cot(fov) * radius / Z, where FOV is the 
+				// largest one
+				//// First get sphere depth
+				const Matrix4& viewVP = viewProps.viewProjTransform;
+				float depth = viewVP.multiply(Vector4(light.internal->getPosition(), 1.0f)).w;
 
-			// Approximation for screen space sphere radius: screenSize * 0.5 * cot(fov) * radius / Z, where FOV is the 
-			// largest one
-			//// First get sphere depth
-			const Matrix4& viewVP = viewProps.viewProjTransform;
-			float depth = viewVP.multiply(Vector4(light.internal->getPosition(), 1.0f)).w;
+				// This is just 1/tan(fov), for both horz. and vert. FOV
+				float viewScaleX = viewProps.projTransform[0][0];
+				float viewScaleY = viewProps.projTransform[1][1];
 
-			// This is just 1/tan(fov), for both horz. and vert. FOV
-			float viewScaleX = viewProps.projTransform[0][0];
-			float viewScaleY = viewProps.projTransform[1][1];
+				float screenScaleX = viewScaleX * viewProps.viewRect.width * 0.5f;
+				float screenScaleY = viewScaleY * viewProps.viewRect.height * 0.5f;
 
-			float screenScaleX = viewScaleX * viewProps.viewRect.width * 0.5f;
-			float screenScaleY = viewScaleY * viewProps.viewRect.height * 0.5f;
+				float screenScale = std::max(screenScaleX, screenScaleY);
 
-			float screenScale = std::max(screenScaleX, screenScaleY);
+				//// Calc radius (clamp if too close to avoid massive numbers)
+				float radiusNDC = light.internal->getBounds().getRadius() / std::max(depth, 1.0f);
 
-			//// Calc radius (clamp if too close to avoid massive numbers)
-			float radiusNDC = light.internal->getBounds().getRadius() / std::max(depth, 1.0f);
+				//// Radius of light bounds in percent of the view surface, multiplied by screen size in pixels
+				float radiusScreen = radiusNDC * screenScale;
 
-			//// Radius of light bounds in percent of the view surface, multiplied by screen size in pixels
-			float radiusScreen = radiusNDC * screenScale;
+				float optimalMapSize = SHADOW_TEXELS_PER_PIXEL * radiusScreen;
+				maxMapSize = std::max(maxMapSize, optimalMapSize);
 
-			float optimalMapSize = SHADOW_TEXELS_PER_PIXEL * radiusScreen;
-			maxMapSize = std::max(maxMapSize, optimalMapSize);
-			
-			// Determine if the shadow should fade out
-			float fadePercent = Math::lerp01(optimalMapSize, (float)MIN_SHADOW_MAP_SIZE, (float)SHADOW_MAP_FADE_SIZE);
-			fadePercents.push_back(fadePercent);
-			maxFadePercent = std::max(maxFadePercent, fadePercent);
+				// Determine if the shadow should fade out
+				float fadePercent = Math::lerp01(optimalMapSize, (float)MIN_SHADOW_MAP_SIZE, (float)SHADOW_MAP_FADE_SIZE);
+				fadePercents.push_back(fadePercent);
+				maxFadePercent = std::max(maxFadePercent, fadePercent);
+			}
 		}
 
 		// If light fully (or nearly fully) covers the screen, use full shadow map resolution, otherwise