I’ve added cascaded shadows to my pipeline. The entire course of appears to works high quality as proven within the image. However I ought to get hold of a progressive “zoom” from the primary cascade (purple) to the final (blue) and right here the zoom is nearly the identical. Beneath the code I take advantage of coming from https://github.com/SaschaWillems/Vulkan/blob/grasp/examples/shadowmappingcascade/shadowmappingcascade.cpp. I’ve modified the unique one to make use of DX11 XMVECTOR features.
The lightview setting
gLightView = XMMatrixLookAtLH(XMVectorSet(-800, 800, 800, 1), XMVectorSet(0, 0, 0, 1), XMVectorSet(0, 1, 0, 0));
CalculateShadowFrustrums();
The CalculateShadowFrustrums is as follows:
void CalculateShadowFrustrums()
{
float cascadeSplits[3];
float nearClip = NearPlane;
float farClip = FarPlane;
float clipRange = farClip - nearClip;
// Calculate cut up depths based mostly on view digital camera frustum
// Primarily based on technique introduced in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html
BOOL bUseNVidia = false;
if (bUseNVidia)
{
float minZ = nearClip;
float maxZ = nearClip + clipRange;
float ratio = maxZ / minZ;
float vary = maxZ - minZ;
float cascadeSplitLambda = 0.90f;
for (int i = 0; i < 3; i++)
{
float p = (i + 1) / 3.0f;
float log = minZ * pow(ratio, p);
float uniform = minZ + vary * p;
float d = cascadeSplitLambda * (log - uniform) + uniform;
cascadeSplits[i] = (d - nearClip) / clipRange;
}
}
else //guide settings
{
cascadeSplits[0] = 0.10f;
cascadeSplits[1] = 0.25f;
cascadeSplits[2] = 1.0f;
}
float lastSplitDist = 0.0;
XMVECTOR transformedfrustumCorners[8];
XMVECTOR lightDir = XMVector3Normalize(XMVectorSet(800, -800, -800, 0));//relative mild distance
XMVECTOR UpDir = XMVectorSet(0, 1, 0, 0);
// Calculate orthographic projection matrix for every cascade
for (int i = 0; i < 3; i++)//I've 3 cascades
{
float splitDist = cascadeSplits[i];
XMVECTOR Det;
// Venture frustum corners into world house
XMMATRIX invCam = XMMatrixInverse(&Det, gVP);//gVP = scene View*Projection
for (int j = 0; j < 8; j++)
{
transformedfrustumCorners[j] = XMVector3TransformCoord(frustumCorners[j], invCam);//consists of divide / w
}
for (int j = 0; j < 4; j++)
{
XMVECTOR dist = transformedfrustumCorners[j + 4] - transformedfrustumCorners[j];
transformedfrustumCorners[j + 4] = transformedfrustumCorners[j] + (dist * splitDist);
transformedfrustumCorners[j] = transformedfrustumCorners[j] + (dist * lastSplitDist);
}
// Get frustum middle
XMVECTOR frustumCenter = XMVectorSet(0, 0, 0, 0);
for (int j = 0; j < 8; j++)
{
frustumCenter += transformedfrustumCorners[j];
}
frustumCenter /= 8.0f;
//I favor to move the XMVECTOR to a XMFLOAT4 for direct member entry as I've SSE_INTRINSIC enabled
XMFLOAT4 tfrustumCenter;
XMStoreFloat4(&tfrustumCenter, frustumCenter);
tfrustumCenter.x = floorf(tfrustumCenter.x);
tfrustumCenter.y = floorf(tfrustumCenter.y);
//tfrustumCenter.z = floorf(tfrustumCenter.z);
//put XMFLOAT4 again to XMVECTOR
frustumCenter = XMLoadFloat4(&tfrustumCenter);
float radius = 0.0f;
for (int j = 0; j < 8; j++)
{
XMVECTOR distance = XMVector3Length(transformedfrustumCorners[j] - frustumCenter);
float d = XMVectorGetX(distance);
radius = fmaxf(radius, d);
}
radius = ceilf(radius * 16.0f) / 16.0f;
XMVECTOR maxExtents = XMVectorSet(radius, radius, radius, 1);
XMVECTOR minExtents = -maxExtents;
XMFLOAT4 minE, maxE;
XMStoreFloat4(&minE, minExtents);
XMStoreFloat4(&maxE, maxExtents);
XMVECTOR Eye = frustumCenter - lightDir * (-minE.z);
gSRVDSVRTVShadowCascade.SView[i] = XMMatrixLookAtLH(Eye, frustumCenter, UpDir);
gSRVDSVRTVShadowCascade.SProj[i] = XMMatrixOrthographicOffCenterLH(minE.x, maxE.x, minE.y, maxE.y, 0.0f, maxE.z - minE.z);
// Retailer cut up distance and matrix in cascade
gSRVDSVRTVShadowCascade.Depths[i] = (NearPlane + splitDist * clipRange);
gSRVDSVRTVShadowCascade.SViewProj[i] = gSRVDSVRTVShadowCascade.SView[i] * gSRVDSVRTVShadowCascade.SProj[i];
lastSplitDist = cascadeSplits[i];
}
}
Values obtained for gSRVDSVRTVShadowCascade.Depths with the preliminary guide cascadeSplits settings match components of the close to/far planes in scene house
Depth0 round 250
Depth1 round 650
Depth2 = farplane
Within the vertex/pixel shader
cbuffer cbShadowCascade : register(b7)
{
matrix View;//used to get the place in scene view house for Enter.VPos
matrix LightViewProjection[3];//the three viewproj cascaded matrices
float4 Cascade;//xyz are the three depth limits; w!=3 if cascade not used
}
struct PS_INPUT
{
…
float4 Shadow[3] : TEXCOORD1;
};
PS_INPUT VS_Forward(VS_INPUT Enter)
{
….
Output.Shadow1[0] = mul(Output.Pos, LightViewProjection[0]);//if used as common depth map
Output.VPos = 0;
Output.Shadow1[1] = 0;
Output.Shadow1[2] = 0;
if ( Cascade.w==3)
{
Output.VPos = mul(Output.Pos, View).xyz;
Output.Shadow1[1] = mul(Output.Pos, LightViewProjection[1]);
Output.Shadow1[2] = mul(Output.Pos, LightViewProjection[2]);
}
Within the shadowing a part of the pixel shader
float pVz = Enter.VPos.z; //place in scene view house to be in comparison with the scene view house cascade.depth Cascade[0-2] set within the fixed buffer.
uint CIndex = (pVz<=Cascade[0])?0:(pVz<=Cascade[1])?1:(pVz<=Cascade[2])?2:3;
//Non-compulsory. To see the three cascades. Pink if not in cascade
LColor+=float3(( CIndex == 0) ||( CIndex == 3),( CIndex == 1) ,( CIndex == 2)||( CIndex == 3) );
if (CIndex<3)
{
float4 ShadowProj = Enter.Shadow[CIndex];
ShadowProj.xyz = mad(float3(0.5f,-0.5f, 1), ShadowProj.xyz/ShadowProj.w, float3(0.5f, 0.5f, 0));
//I regulate the bias based mostly on the cascade however the consequence just isn't good because the projection just isn't as anticipated
Bias = (CIndex==0)?0.0025f: (CIndex==1)?0.0019f:0.00175f;
Shadow = SampleShadowMapFixedSizePCF(CIndex, ShadowProj.z - Bias, ShadowProj.xy);//this perform makes use of the optimized gathercmp mounted sized PCF to blur the shadow.
Another strategies are reworking the frustrum to world after which to mild house however I see no modifications within the shadow rendering.
I am absolutely lacking one thing.