Advertisement

Stable Cascaded Shadow Maps

Started by June 05, 2020 07:36 PM
1 comment, last by __MONTE2020 4 years, 5 months ago

Hi, I am trying to implement stable cascaded shadow maps but I am still getting shadow shimmering.

Here is my code for non-stable CSM (the part where you calculate all the lightviewprojection matrices):

//this function calculates projection frustums for every split and later uses it to get frustum in world space
std::array<DirectX::XMFLOAT4X4, MAX_CASCADES_NUM> projectionMatrices = RecalculateProjectionMatrices(camera);
	std::array<DirectX::XMMATRIX, MAX_CASCADES_NUM> lightViewProjectionMatrices;


	for (UINT i = 0; i < MAX_CASCADES_NUM; ++i)
	{
		///frustum in world space
		BoundingFrustum frustum(DirectX::XMLoadFloat4x4(&amp;projectionMatrices[i]));
		frustum.Transform(frustum, DirectX::XMMatrixInverse(nullptr, camera.View()));

		///get frustum corners
		std::array<DirectX::XMFLOAT3, frustum.CORNER_COUNT> corners;
		frustum.GetCorners(corners.data());

		///calculate frustum center and radius using bounding sphere
		BoundingSphere frustumSphere;
		BoundingSphere::CreateFromFrustum(frustumSphere, frustum);
		XMVECTOR frustumCenter = XMLoadFloat3(&amp;frustumSphere.Center);
		FLOAT radius = std::ceil(frustumSphere.Radius);


		///calculating light view matrix
		XMVECTOR lightDir = XMVector3Normalize(XMLoadFloat3(&amp;light.direction));
		static const XMVECTOR zero = DirectX::XMVectorSet(0, 0, 0, 1);
		XMVECTOR up = camera.Right(); // XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
		XMVECTOR lightPos = -lightDistanceFactor * radius * lightDir + frustumCenter;

		///NEW view matrix
		XMMATRIX V = DirectX::XMMatrixLookAtLH(lightPos, frustumCenter, up);



		///transform frustum corners in light view space to calculate AABB for orthographic projection
		FLOAT l = std::numeric_limits<float>::max();
		FLOAT b = std::numeric_limits<float>::max();
		FLOAT n = std::numeric_limits<float>::max();
		FLOAT r = std::numeric_limits<float>::min();
		FLOAT t = std::numeric_limits<float>::min();
		FLOAT f = std::numeric_limits<float>::min();

		for (auto&amp; corner : corners)
		{
			//transform corners into light space
			XMStoreFloat3(&amp;corner, DirectX::XMVector3TransformCoord(DirectX::XMLoadFloat3(&amp;corner), V));

			l = std::min(l, corner.x);
			r = std::max(r, corner.x);

			b = std::min(b, corner.y);
			t = std::max(t, corner.y);

			n = std::min(n, corner.z);
			f = std::max(f, corner.z);
		}
		

		XMMATRIX P = DirectX::XMMatrixOrthographicOffCenterLH(l, r, b, t, nearFactor*n, farFactor*f); 

		XMMATRIX lightviewprojection = V * P;

		lightViewProjectionMatrices[i] = lightviewprojection;
	}

	return lightViewProjectionMatrices;

That part works as excepted.

This part should calculate stable CSM lightviewprojection matrices but, while I still get shadows the shimmering doesn't disappear (it actually gets a bit worse):


std::array<DirectX::XMFLOAT4X4, MAX_CASCADES_NUM> projectionMatrices = RecalculateProjectionMatrices(camera);
	std::array<DirectX::XMMATRIX, MAX_CASCADES_NUM> lightViewProjectionMatrices;

for (UINT i = 0; i < MAX_CASCADES_NUM; ++i)
	{
		///frustum in world space
		BoundingFrustum frustum(DirectX::XMLoadFloat4x4(&amp;projectionMatrices[i]));          
		frustum.Transform(frustum, DirectX::XMMatrixInverse(nullptr, camera.View()));

		BoundingSphere frustumSphere;                         
		BoundingSphere::CreateFromFrustum(frustumSphere, frustum);
		XMVECTOR frustumCenter = XMLoadFloat3(&amp;frustumSphere.Center);                              
		FLOAT radius = std::ceil(frustumSphere.Radius * 16.0f) / 16.0f;
		XMVECTOR lightDir = XMVector3Normalize(XMLoadFloat3(&amp;light.direction));
		static const XMVECTOR zero = DirectX::XMVectorSet(0, 0, 0, 1);
		static const XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

		//you can ignore this texel snapping part since the camera rotation also causes shimmering

		//taken from altex tardif site
		//FLOAT texelsperunit = depthMapSize / (2.0f * radius);
		//XMMATRIX lookat = XMMatrixLookAtLH(zero, -lightDir, up);
		//lookat = lookat * XMMatrixScaling(texelsperunit, texelsperunit, texelsperunit);
		//XMMATRIX inverselookat = XMMatrixInverse(nullptr, lookat);
		//frustumCenter = XMVector3TransformCoord(frustumCenter, lookat);
		//XMVectorSetX(frustumCenter, floor(XMVectorGetX(frustumCenter)));
		//XMVectorSetY(frustumCenter, floor(XMVectorGetY(frustumCenter)));
		//frustumCenter = XMVector3TransformCoord(frustumCenter, inverselookat);
		

		
		XMMATRIX V = DirectX::XMMatrixLookAtLH(frustumCenter, frustumCenter + lightDistanceFactor * radius * lightDir, up);
		FLOAT l = -radius;
		FLOAT b = -radius;
		FLOAT n = -nearFactor * radius;
		FLOAT r =  radius;
		FLOAT t =  radius;
		FLOAT f =  farFactor * radius;

		//DirectX::XMFLOAT3 cascadeExtends(2 * radius, 2 * radius, 2 * radius);


		///calculating light view matrix

		XMMATRIX P = DirectX::XMMatrixOrthographicOffCenterLH(l, r, b, t, n, f);

		//this produces shadows (unstable) if this view matrix is used:
		//XMVECTOR lightPos = -lightDistanceFactor * radius * lightDir + frustumCenter;
		//XMMATRIX V = DirectX::XMMatrixLookAtLH(lightPos, frustumCenter, up);
		//auto c = frustumSphere.Center;
		//XMStoreFloat3(&amp;c, DirectX::XMVector3TransformCoord(DirectX::XMLoadFloat3(&amp;c), V));
		//XMMATRIX P = DirectX::XMMatrixOrthographicOffCenterLH(c.x - radius, c.x + radius,
		//	c.y - radius, c.y + radius,
		//	nearFactor*(c.z - radius), farFactor*(c.z + radius));

		lightViewProjectionMatrices[i] = V * P;
	}

I also can visualize my cascades with RGB color and and display depth maps on the screen if you have some debugging advices.

Thanks in advance.

I managed to get them working, I added texel-snapping part from MJP Shadows project, and calculated frustum centroid manually and not using BoundingSphere, I don't know if that also makes a difference, but I don't care to check at this moment (I have some suspicions why they give different results).

This topic is closed to new replies.

Advertisement