Jump to content

  • Log In with Google      Sign In   
  • Create Account


#Actuallipsryme

Posted 07 January 2013 - 07:43 AM

Well so I've finally managed to get the correct projection working.
My problem now is that either the shadow map is not correct or the shader code is not sampling the cascades like it should.
From my observation it looks like it's only sampling from the first cascade and the rest is just black I guess.

I've captured a 30sec video showcasing it:




Here's my most recent code:

CPU side calculation:

void Shadows::RenderShadows(FSQ* &fsq,
							std::vector<Primitive*> primitiveList,
							std::vector<ModelObject*> modelList,
							ID3D11SamplerState* &pointSampler,
							LightManager* &lightManager)
{
	ID3D11DeviceContext* context = this->renderer->GetDeviceContext();
	ID3D11DepthStencilView* shadowMapDSV = lightManager->GetDominantDirectionalLight()->GetShadowMapDSV();
	context->ClearDepthStencilView(shadowMapDSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);


	// Set RenderTarget
	ID3D11RenderTargetView* nullRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = { NULL };
	context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, nullRenderTargets, shadowMapDSV);

	// Set DepthStencil to write enabled
	context->OMSetDepthStencilState(this->writeDepthStencilState, 0);


	// Set Shader
	context->VSSetShader(this->generateShadowMapShader->GetVS(), 0, 0);
	context->PSSetShader(NULL, 0, 0);


	// Set ColorWriteDisabled Blendstate
	float blendFactor[4] = {1, 1, 1, 1};
	context->OMSetBlendState(this->ColorWriteDisabledBlendState, blendFactor, 0xFFFFFFFF);


	//*****************************************************
	// DIRECTIONAL LIGHT SOURCE (CASCADED SHADOW MAPPING)
	//*****************************************************
	DirectionalLight* dominantLight = lightManager->GetDominantDirectionalLight();

	// Cascade offsets
	const XMFLOAT2 Offsets[4] = {
		XMFLOAT2(0.0f, 0.0f),
		XMFLOAT2(0.5f, 0.0f),
		XMFLOAT2(0.5f, 0.5f),
		XMFLOAT2(0.0f, 0.5f)
	};

	const float sMapSize = static_cast<float>(dominantLight->GetShadowMapSize());
	const unsigned int NumCascades = dominantLight->GetNumberCascades();

	// Render meshes to each cascade
	for(UINT cascadeIdx = 0; cascadeIdx < NumCascades; ++cascadeIdx)
	{
		// Set viewport
		D3D11_VIEWPORT viewport;
		viewport.TopLeftX = Offsets[cascadeIdx].x * sMapSize * 2;
		viewport.TopLeftY = Offsets[cascadeIdx].y * sMapSize * 2;
		viewport.Width = static_cast<float>(sMapSize);
		viewport.Height = viewport.Width;
		viewport.MinDepth = 0.0f;
		viewport.MaxDepth = 1.0f;
		context->RSSetViewports(1, &viewport);

		// Compute the cascaded shadow transformations
		dominantLight->CalculateLightProjection(renderer->GetEngine()->GetCamera(), cascadeIdx);


		// Render Shadow Map
		dominantLight->RenderShadowMap(context, primitiveList, modelList);
	}





	// Set input and output off
	ID3D11ShaderResourceView* NullResource = NULL;
	ID3D11RenderTargetView* NullRT = NULL;
	context->OMSetRenderTargets(1, &NullRT, NULL);

	// Set the blend state for opaque objects
	context->OMSetBlendState(0, 0, 0xFFFFFFFF);

	// Set DepthStencil to read only
	context->OMSetDepthStencilState(this->readOnlyDepthStencilState, 0);

	// Set back to original
	context->RSSetViewports(1, &this->renderer->graphicsDesc.viewport);

	// Clear Shadow Accumulation target with white
	ID3D11RenderTargetView* shadowAccRTV = this->shadowAccTarget->GetRenderTargetView();
	context->ClearRenderTargetView(shadowAccRTV, D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f));

	// Set Render Target
	context->OMSetRenderTargets(1, &shadowAccRTV, NULL);

	// Set Shader
	context->VSSetShader(this->accumulateShadowsShader->GetVS(), 0, 0);
	context->PSSetShader(this->accumulateShadowsShader->GetPS(), 0, 0);



	//*************************************************
	// DIRECTIONAL LIGHT SOURCE
	//*************************************************


	// Set Sampler & SRV
	ID3D11ShaderResourceView* shadowMapSRV = dominantLight->GetShadowMapSRV();
	ID3D11SamplerState* shadowMapSampler = dominantLight->GetShadowMapSampler();
	context->PSSetShaderResources(0, 1, &shadowMapSRV);
	context->PSSetSamplers(0, 1, &shadowMapSampler);
	
	ID3D11ShaderResourceView* depthTargetSRV = this->renderer->GetDepthBufferTarget();
	context->PSSetShaderResources(1, 1, &depthTargetSRV);
	context->PSSetSamplers(1, 1, &pointSampler);


	// Set constant buffer
	D3D11_MAPPED_SUBRESOURCE mappedResource;

	// Lock the constant buffer so it can be written to
	context->Map(cbShadowProjection, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);

	// Get a pointer to the data in the constant buffer.
	cbShadowProjectionProperties* pTransformData = (cbShadowProjectionProperties*)mappedResource.pData;


	// Copy the matrices into the constant buffer
	XMVECTOR det;
	XMMATRIX view = this->renderer->GetEngine()->GetCamera()->GetViewMatrixNonTransposed();
	view = XMMatrixInverse(&det, view);
	XMMATRIX shadowMatrices[4];
	for(int i = 0; i < 4; i++)
	{
		// Set Shadow matrices
		//-----------------------------
		shadowMatrices[i] = XMLoadFloat4x4(&dominantLight->GetShadowMatrix(i));

		// Premultiply inverse view with the light's view projection
		shadowMatrices[i] = XMMatrixMultiplyTranspose(view, shadowMatrices[i]);

		XMStoreFloat4x4(&pTransformData->ShadowMatrix[i], shadowMatrices[i]);

		// Set Cascade Splits
		//----------------------
		pTransformData->cascadeSplits[i] = dominantLight->GetShadowCascadeSplit(i);
	}

	XMMATRIX Projection = this->renderer->GetEngine()->GetCamera()->GetProjectionMatrixNonTransposed();
	Projection = XMMatrixInverse(&det, Projection);
	Projection = XMMatrixTranspose(Projection);
	XMStoreFloat4x4(&pTransformData->InverseProjection, Projection);

	pTransformData->shadowMapSize = XMFLOAT2(sMapSize, sMapSize);

	float nearClip = this->renderer->GetEngine()->GetCamera()->GetNearClip();
	float farClip = this->renderer->GetEngine()->GetCamera()->GetFarClip();

	pTransformData->ProjectionA = farClip / (farClip - nearClip);
	pTransformData->ProjectionB = (-farClip * nearClip) / (farClip - nearClip);


	// Unlock the constant buffer
	context->Unmap(this->cbShadowProjection, 0);


	// Set Constant buffer
	context->VSSetConstantBuffers(0, 1, &this->cbShadowProjection);
	context->PSSetConstantBuffers(0, 1, &this->cbShadowProjection);

	// Draw Fullscreen-Quad
	fsq->Draw(context);


	// Set input and output off
	context->PSSetShaderResources(0, 1, &NullResource);
	context->PSSetShaderResources(1, 1, &NullResource);
	context->OMSetRenderTargets(1, &NullRT, NULL);

}
void DirectionalLight::CalculateLightProjection(BaseCamera* camera, UINT &cascadeIdx)
{
	static const float ShadowDist = 0.4f;
	static const float Backup = -1.0f;
	static const float CascadeSplits[4] = { 0.125f, 0.25f, 0.5f, 1.0f };
	static const float Bias = 0.005f;
	static const float nearClip = 1.0f;


	// Get the 8 points of the view frustum in world space
	XMVECTOR frustumCornerWS[8] = 
	{
		XMVectorSet(-1.0f,  1.0f,  0.0f,  1.0f),
		XMVectorSet( 1.0f,  1.0f,  0.0f,  1.0f),
		XMVectorSet( 1.0f, -1.0f,  0.0f,  1.0f),
		XMVectorSet(-1.0f, -1.0f,  0.0f,  1.0f),
		XMVectorSet(-1.0f,  1.0f,  1.0f,  1.0f),
		XMVectorSet( 1.0f,  1.0f,  1.0f,  1.0f),
		XMVectorSet( 1.0f, -1.0f,  1.0f,  1.0f),
		XMVectorSet(-1.0f, -1.0f,  1.0f,  1.0f),
	};

	float prevSplitDist = cascadeIdx == 0 ? 0.0f : CascadeSplits[cascadeIdx - 1] * ShadowDist;
	float splitDist = CascadeSplits[cascadeIdx] * ShadowDist;

	XMVECTOR det;
	XMMATRIX CamViewProj = camera->GetViewProjectionNonTransposed();
	XMMATRIX invViewProj = XMMatrixInverse(&det, CamViewProj);
	for(UINT i = 0; i < 8; ++i)
	{
		frustumCornerWS[i] = XMVector3TransformCoord(frustumCornerWS[i], invViewProj);
	}

	// Scale by the shadow view distance
	for(UINT i = 0; i < 4; ++i)
	{
		XMVECTOR cornerRay = XMVectorSubtract(frustumCornerWS[i + 4], frustumCornerWS[i]);
		XMVECTOR nearCornerRay = XMVectorScale(cornerRay, prevSplitDist);
		XMVECTOR farCornerRay = XMVectorScale(cornerRay, splitDist);
		frustumCornerWS[i + 4] = XMVectorAdd(frustumCornerWS[i], farCornerRay);
		frustumCornerWS[i] = XMVectorAdd(frustumCornerWS[i], nearCornerRay);
	}

	// Calculate the centroid of the view frustum
	XMVECTOR sphereCenterVec = XMVectorZero();
	for(UINT i = 0; i < 8; ++i)
	{
		sphereCenterVec = XMVectorAdd(sphereCenterVec, frustumCornerWS[i]);
	}
	sphereCenterVec = XMVectorScale(sphereCenterVec, 1.0f / 8.0f);

	// Calculate the radius of a bounding sphere
	XMVECTOR sphereRadiusVec = XMVectorZero();
	for(UINT i = 0; i < 8; ++i)
	{
		XMVECTOR dist = XMVector3Length(XMVectorSubtract(frustumCornerWS[i], sphereCenterVec));
		sphereRadiusVec = XMVectorMax(sphereRadiusVec, dist);
	}		

	sphereRadiusVec = XMVectorRound(sphereRadiusVec);
	const float sphereRadius = XMVectorGetX(sphereRadiusVec);
	const float backupDist = sphereRadius + nearClip + Backup;

	// Get Position of the shadow camera
	XMVECTOR shadowCameraPosVec = sphereCenterVec;
	XMVECTOR backupDirVec = XMVectorNegate(XMLoadFloat3(&this->direction));
	backupDirVec = XMVectorScale(backupDirVec, backupDist);
	shadowCameraPosVec = XMVectorAdd(shadowCameraPosVec, backupDirVec);
	XMVECTOR upVec = XMVectorSet(0.0f, 1.0f, 0.0f, 1.0f);

	// Come up witha new orthographic camera for the shadow caster
	XMMATRIX shadowProjection = XMMatrixOrthographicOffCenterLH(-sphereRadius, sphereRadius,
														        -sphereRadius, sphereRadius,
														         nearClip, backupDist + sphereRadius);

	XMMATRIX shadowView = XMMatrixLookAtLH(shadowCameraPosVec, sphereCenterVec, upVec);
	XMMATRIX shadowViewProjection = XMMatrixMultiply(shadowView, shadowProjection);
	
	// Create the rounding matrix, by projecting the world-space origin and determining
	// the fractional offset in texel space
	XMMATRIX shadowMatrix = shadowViewProjection;
	XMVECTOR shadowOrigin = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
	shadowOrigin = XMVector4Transform(shadowOrigin, shadowMatrix);
	shadowOrigin = XMVectorScale(shadowOrigin, this->shadowMapSize / 2.0f);

	XMVECTOR roundedOrigin = XMVectorRound(shadowOrigin);
	XMVECTOR roundOffset = XMVectorSubtract(roundedOrigin, shadowOrigin);
	roundOffset = XMVectorScale(roundOffset, 2.0f / this->shadowMapSize);
	roundOffset = XMVectorSetZ(roundOffset, 0.0f);
	roundOffset = XMVectorSetW(roundOffset, 0.0f);

	shadowProjection.r[3] = XMVectorAdd(shadowProjection.r[3], roundOffset);
	shadowViewProjection = XMMatrixMultiply(shadowView, shadowProjection);
	XMStoreFloat4x4(&this->ViewProjection, shadowViewProjection);
	shadowMatrix = shadowViewProjection;


	// Apply the scale / offset / bias matrix, which transforms from [-1,1]
	// post-projection space to [0,1] UV space
	const float bias = Bias;
	XMMATRIX texScaleBias;
	texScaleBias.r[0] = XMVectorSet(0.5f,  0.0f,  0.0f,  0.0f);
	texScaleBias.r[1] = XMVectorSet(0.0f, -0.5f,  0.0f,  0.0f);
	texScaleBias.r[2] = XMVectorSet(0.0f,  0.0f,  1.0f,  0.0f);
	texScaleBias.r[3] = XMVectorSet(0.5f,  0.5f, -bias,  1.0f);
	shadowMatrix = XMMatrixMultiply(shadowMatrix, texScaleBias);

	// Apply the cascade offset / scale matrix, which applies the offset and scale needed to
	// convert the UV coordinate into the proper coordinate for the cascade being sampled in
	// the atlas

	// Cascade offsets
	const XMFLOAT2 Offsets[4] = {
		XMFLOAT2(0.0f, 0.0f),
		XMFLOAT2(0.5f, 0.0f),
		XMFLOAT2(0.5f, 0.5f),
		XMFLOAT2(0.0f, 0.5f)
	};

	XMFLOAT4 offset(Offsets[cascadeIdx].x, Offsets[cascadeIdx].y, 0.0f, 1.0f);
	XMMATRIX cascadeOffsetMatrix = XMMatrixScaling(0.5f, 0.5f, 1.0f);
	cascadeOffsetMatrix.r[3] = XMLoadFloat4(&offset);
	shadowMatrix = XMMatrixMultiply(shadowMatrix, cascadeOffsetMatrix);
	
	// Store the shadow matrix
	XMStoreFloat4x4(&this->ShadowMatrices[cascadeIdx], shadowMatrix);

	// Store the split distance in terms of view space depth
	const float clipDist = camera->GetFarClip() - camera->GetNearClip();
	this->cascadeSplits[cascadeIdx] = camera->GetNearClip() + splitDist * clipDist;
} 

 

GPU side computation:

static const uint NumCascades = 4;


struct VSI
{
	float4 Position : POSITION0;
	float2 UV		: TEXCOORD0;
};


struct VSO
{
	float4 Position			: SV_POSITION;
	float2 UV				: TEXCOORD0;
	float3 ViewRay			: TEXCOORD1;
};





cbuffer cbShadowProjection : register(b0)
{
	float4x4 InverseProjection;
	float4x4 ShadowMatrices[4];
	float4 CascadeSplits;

	float ProjectionA;
	float ProjectionB;
	float2 shadowMapSize;
};







// Image texture
Texture2D ShadowMap : register(t0);
SamplerComparisonState ShadowMapSampler : register(s0);

Texture2D DepthTarget : register(t1);
SamplerState PointSampler : register(s1);








VSO VS(VSI input)
{
    VSO output = (VSO)0;

    output.Position = input.Position;
	output.UV = input.UV;

	float3 positionVS = mul(input.Position, InverseProjection);
	output.ViewRay = float3(positionVS.xy / positionVS.z, 1.0f);


    return output;
}





//--------------------------------------------------------------------------------------
// Samples the shadow map cascades based on the world-space position, using edge-tap
// smoothing PCF for filtering
//--------------------------------------------------------------------------------------
float SampleShadowCascade(in float3 positionVS, in uint cascadeIdx)
{
	float4x4 shadowMatrix = ShadowMatrices[cascadeIdx];
	float3 shadowPosition = mul(float4(positionVS, 1.0f), shadowMatrix).xyz;
	float2 shadowTexCoord = shadowPosition.xy;
	float shadowDepth = shadowPosition.z;

	// Edge tap smoothing
	const int Radius = 2;
	const float ShadowMapSize = 2048.0f * 2;
	const int NumSamples = (Radius * 2 + 1) * (Radius * 2 + 1);

	float2 fracs = frac(shadowTexCoord.xy * ShadowMapSize);
	float leftEdge = 1.0f - fracs.x;
	float rightEdge = fracs.x;
	float topEdge = 1.0f - fracs.y;
	float bottomEdge = fracs.y;

	float shadowVisibility = 0.0f;

	[unroll(NumSamples)]
	for (int y = -Radius; y <= Radius; y++)
	{
		[unroll(NumSamples)]
		for (int x = -Radius; x <= Radius; x++)
		{
			float2 offset = float2(x, y) * (1.0f / ShadowMapSize);
			float2 sampleCoord = shadowTexCoord + offset;
			float sample = ShadowMap.SampleCmp(ShadowMapSampler, sampleCoord, shadowDepth).x;

			float xWeight = 1;
			float yWeight = 1;

			if(x == -Radius)
				xWeight = leftEdge;
			else if(x == Radius)
				xWeight = rightEdge;

			if(y == -Radius)
				yWeight = topEdge;
			else if(y == Radius)
				yWeight = bottomEdge;

			shadowVisibility += sample * xWeight * yWeight;
		}
	}

	shadowVisibility  /= NumSamples;
	shadowVisibility *= 1.5f;

	return shadowVisibility;
}




//--------------------------------------------------------------------------------------
// Computes the visibility term by performing the shadow test
//--------------------------------------------------------------------------------------
float3 ShadowVisibility(in float3 positionVS)
{
	float3 shadowVisibility = 1.0f;
	uint cascadeIdx = 0;
	float depthVS = positionVS.z;

	// Figure out which cascade to sample from
	[unroll]
	for(uint i = 0; i < NumCascades - 1; ++i)
	{
		[flatten]
		if(depthVS > CascadeSplits[i])
			cascadeIdx = i + 1;
	}

	shadowVisibility = SampleShadowCascade(positionVS, cascadeIdx);

	// Sample the next cascade, and blend between the two results to
	// smooth the transition
	const float BlendThreshold = 0.1f;
	float nextSplit = CascadeSplits[cascadeIdx];
	float splitSize = i == 0 ? nextSplit : nextSplit - CascadeSplits[cascadeIdx - 1];
	float splitDist = (nextSplit - depthVS) / splitSize;

	float nextSplitVisibility = SampleShadowCascade(positionVS, cascadeIdx + 1);
	float lerpAmt = smoothstep(0.0f, BlendThreshold, splitDist);
	shadowVisibility = lerp(nextSplitVisibility, shadowVisibility, lerpAmt);

	if(cascadeIdx == 0)
	{
		shadowVisibility *= float3(1.0f, 0.0f, 0.0f);
	}
	else if(cascadeIdx == 1)
	{
		shadowVisibility *= float3(0.0f, 1.0f, 0.0f);
	}
	else if(cascadeIdx == 2)
	{
		shadowVisibility *= float3(0.0f, 0.0f, 1.0f);
	}
	else if(cascadeIdx == 3)
	{
		shadowVisibility *= float3(1.0f, 1.0f, 0.0f);
	}
	


	return shadowVisibility;
}





float4 PS(VSO input) : SV_TARGET0
{
	float4 output = float4(0.0f, 0.0f, 0.0f, 1.0f);

	// Reconstruct view space position from depth buffer
	float3 ViewRay = input.ViewRay.xyz;
	float depth = DepthTarget.Sample(PointSampler, input.UV).r;
	float linearDepth = ProjectionB / (depth - ProjectionA);
	float3 PositionVS = ViewRay * linearDepth;

	float3 shadowVisibility = ShadowVisibility(PositionVS);

	return float4(shadowVisibility, 1.0f);
}

 

 

I'm so close to having it working now, any idea what' I'm doing wrong ?
I still don't quite understand how the parameters "ShadowDist" and "Backup" have to be set.

 


#9lipsryme

Posted 04 January 2013 - 10:30 AM

Well so I've finally managed to get the correct projection working.
My problem now is that either the shadow map is not correct or the shader code is not sampling the cascades like it should.
From my observation it looks like it's only sampling from the first cascade and the rest is just black I guess.

I've captured a 30sec video showcasing it:




Here's my most recent code:

CPU side calculation:

void Shadows::RenderShadows(FSQ* &fsq,
							std::vector<Primitive*> primitiveList,
							std::vector<ModelObject*> modelList,
							ID3D11SamplerState* &pointSampler,
							LightManager* &lightManager)
{
	ID3D11DeviceContext* context = this->renderer->GetDeviceContext();
	ID3D11DepthStencilView* shadowMapDSV = lightManager->GetDominantDirectionalLight()->GetShadowMapDSV();
	context->ClearDepthStencilView(shadowMapDSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);


	// Set RenderTarget
	ID3D11RenderTargetView* nullRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = { NULL };
	context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, nullRenderTargets, shadowMapDSV);

	// Set DepthStencil to write enabled
	context->OMSetDepthStencilState(this->writeDepthStencilState, 0);


	// Set Shader
	context->VSSetShader(this->generateShadowMapShader->GetVS(), 0, 0);
	context->PSSetShader(NULL, 0, 0);


	// Set ColorWriteDisabled Blendstate
	float blendFactor[4] = {1, 1, 1, 1};
	context->OMSetBlendState(this->ColorWriteDisabledBlendState, blendFactor, 0xFFFFFFFF);


	//*****************************************************
	// DIRECTIONAL LIGHT SOURCE (CASCADED SHADOW MAPPING)
	//*****************************************************
	DirectionalLight* dominantLight = lightManager->GetDominantDirectionalLight();

	// Cascade offsets
	const XMFLOAT2 Offsets[4] = {
		XMFLOAT2(0.0f, 0.0f),
		XMFLOAT2(0.5f, 0.0f),
		XMFLOAT2(0.5f, 0.5f),
		XMFLOAT2(0.0f, 0.5f)
	};

	const float sMapSize = static_cast<float>(dominantLight->GetShadowMapSize());
	const unsigned int NumCascades = dominantLight->GetNumberCascades();

	// Render meshes to each cascade
	for(UINT cascadeIdx = 0; cascadeIdx < NumCascades; ++cascadeIdx)
	{
		// Set viewport
		D3D11_VIEWPORT viewport;
		viewport.TopLeftX = Offsets[cascadeIdx].x * sMapSize * 2;
		viewport.TopLeftY = Offsets[cascadeIdx].y * sMapSize * 2;
		viewport.Width = static_cast<float>(sMapSize);
		viewport.Height = viewport.Width;
		viewport.MinDepth = 0.0f;
		viewport.MaxDepth = 1.0f;
		context->RSSetViewports(1, &viewport);

		// Compute the cascaded shadow transformations
		dominantLight->CalculateLightProjection(renderer->GetEngine()->GetCamera(), cascadeIdx);


		// Render Shadow Map
		dominantLight->RenderShadowMap(context, primitiveList, modelList);
	}





	// Set input and output off
	ID3D11ShaderResourceView* NullResource = NULL;
	ID3D11RenderTargetView* NullRT = NULL;
	context->OMSetRenderTargets(1, &NullRT, NULL);

	// Set the blend state for opaque objects
	context->OMSetBlendState(0, 0, 0xFFFFFFFF);

	// Set DepthStencil to read only
	context->OMSetDepthStencilState(this->readOnlyDepthStencilState, 0);

	// Set back to original
	context->RSSetViewports(1, &this->renderer->graphicsDesc.viewport);

	// Clear Shadow Accumulation target with white
	ID3D11RenderTargetView* shadowAccRTV = this->shadowAccTarget->GetRenderTargetView();
	context->ClearRenderTargetView(shadowAccRTV, D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f));

	// Set Render Target
	context->OMSetRenderTargets(1, &shadowAccRTV, NULL);

	// Set Shader
	context->VSSetShader(this->accumulateShadowsShader->GetVS(), 0, 0);
	context->PSSetShader(this->accumulateShadowsShader->GetPS(), 0, 0);



	//*************************************************
	// DIRECTIONAL LIGHT SOURCE
	//*************************************************


	// Set Sampler & SRV
	ID3D11ShaderResourceView* shadowMapSRV = dominantLight->GetShadowMapSRV();
	ID3D11SamplerState* shadowMapSampler = dominantLight->GetShadowMapSampler();
	context->PSSetShaderResources(0, 1, &shadowMapSRV);
	context->PSSetSamplers(0, 1, &shadowMapSampler);
	
	ID3D11ShaderResourceView* depthTargetSRV = this->renderer->GetDepthBufferTarget();
	context->PSSetShaderResources(1, 1, &depthTargetSRV);
	context->PSSetSamplers(1, 1, &pointSampler);


	// Set constant buffer
	D3D11_MAPPED_SUBRESOURCE mappedResource;

	// Lock the constant buffer so it can be written to
	context->Map(cbShadowProjection, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);

	// Get a pointer to the data in the constant buffer.
	cbShadowProjectionProperties* pTransformData = (cbShadowProjectionProperties*)mappedResource.pData;


	// Copy the matrices into the constant buffer
	XMVECTOR det;
	XMMATRIX view = this->renderer->GetEngine()->GetCamera()->GetViewMatrixNonTransposed();
	view = XMMatrixInverse(&det, view);
	XMMATRIX shadowMatrices[4];
	for(int i = 0; i < 4; i++)
	{
		// Set Shadow matrices
		//-----------------------------
		shadowMatrices[i] = XMLoadFloat4x4(&dominantLight->GetShadowMatrix(i));

		// Premultiply inverse view with the light's view projection
		shadowMatrices[i] = XMMatrixMultiplyTranspose(view, shadowMatrices[i]);

		XMStoreFloat4x4(&pTransformData->ShadowMatrix[i], shadowMatrices[i]);

		// Set Cascade Splits
		//----------------------
		pTransformData->cascadeSplits[i] = dominantLight->GetShadowCascadeSplit(i);
	}

	XMMATRIX Projection = this->renderer->GetEngine()->GetCamera()->GetProjectionMatrixNonTransposed();
	Projection = XMMatrixInverse(&det, Projection);
	Projection = XMMatrixTranspose(Projection);
	XMStoreFloat4x4(&pTransformData->InverseProjection, Projection);

	pTransformData->shadowMapSize = XMFLOAT2(sMapSize, sMapSize);

	float nearClip = this->renderer->GetEngine()->GetCamera()->GetNearClip();
	float farClip = this->renderer->GetEngine()->GetCamera()->GetFarClip();

	pTransformData->ProjectionA = farClip / (farClip - nearClip);
	pTransformData->ProjectionB = (-farClip * nearClip) / (farClip - nearClip);


	// Unlock the constant buffer
	context->Unmap(this->cbShadowProjection, 0);


	// Set Constant buffer
	context->VSSetConstantBuffers(0, 1, &this->cbShadowProjection);
	context->PSSetConstantBuffers(0, 1, &this->cbShadowProjection);

	// Draw Fullscreen-Quad
	fsq->Draw(context);


	// Set input and output off
	context->PSSetShaderResources(0, 1, &NullResource);
	context->PSSetShaderResources(1, 1, &NullResource);
	context->OMSetRenderTargets(1, &NullRT, NULL);

}
void DirectionalLight::CalculateLightProjection(BaseCamera* camera, UINT &cascadeIdx)
{
	static const float ShadowDist = 0.4f;
	static const float Backup = -1.0f;
	static const float CascadeSplits[4] = { 0.125f, 0.25f, 0.5f, 1.0f };
	static const float Bias = 0.005f;
	static const float nearClip = 1.0f;


	// Get the 8 points of the view frustum in world space
	XMVECTOR frustumCornerWS[8] = 
	{
		XMVectorSet(-1.0f,  1.0f,  0.0f,  1.0f),
		XMVectorSet( 1.0f,  1.0f,  0.0f,  1.0f),
		XMVectorSet( 1.0f, -1.0f,  0.0f,  1.0f),
		XMVectorSet(-1.0f, -1.0f,  0.0f,  1.0f),
		XMVectorSet(-1.0f,  1.0f,  1.0f,  1.0f),
		XMVectorSet( 1.0f,  1.0f,  1.0f,  1.0f),
		XMVectorSet( 1.0f, -1.0f,  1.0f,  1.0f),
		XMVectorSet(-1.0f, -1.0f,  1.0f,  1.0f),
	};

	float prevSplitDist = cascadeIdx == 0 ? 0.0f : CascadeSplits[cascadeIdx - 1] * ShadowDist;
	float splitDist = CascadeSplits[cascadeIdx] * ShadowDist;

	XMVECTOR det;
	XMMATRIX CamViewProj = camera->GetViewProjectionNonTransposed();
	XMMATRIX invViewProj = XMMatrixInverse(&det, CamViewProj);
	for(UINT i = 0; i < 8; ++i)
	{
		frustumCornerWS[i] = XMVector3TransformCoord(frustumCornerWS[i], invViewProj);
	}

	// Scale by the shadow view distance
	for(UINT i = 0; i < 4; ++i)
	{
		XMVECTOR cornerRay = XMVectorSubtract(frustumCornerWS[i + 4], frustumCornerWS[i]);
		XMVECTOR nearCornerRay = XMVectorScale(cornerRay, prevSplitDist);
		XMVECTOR farCornerRay = XMVectorScale(cornerRay, splitDist);
		frustumCornerWS[i + 4] = XMVectorAdd(frustumCornerWS[i], farCornerRay);
		frustumCornerWS[i] = XMVectorAdd(frustumCornerWS[i], nearCornerRay);
	}

	// Calculate the centroid of the view frustum
	XMVECTOR sphereCenterVec = XMVectorZero();
	for(UINT i = 0; i < 8; ++i)
	{
		sphereCenterVec = XMVectorAdd(sphereCenterVec, frustumCornerWS[i]);
	}
	sphereCenterVec = XMVectorScale(sphereCenterVec, 1.0f / 8.0f);

	// Calculate the radius of a bounding sphere
	XMVECTOR sphereRadiusVec = XMVectorZero();
	for(UINT i = 0; i < 8; ++i)
	{
		XMVECTOR dist = XMVector3Length(XMVectorSubtract(frustumCornerWS[i], sphereCenterVec));
		sphereRadiusVec = XMVectorMax(sphereRadiusVec, dist);
	}		

	sphereRadiusVec = XMVectorRound(sphereRadiusVec);
	const float sphereRadius = XMVectorGetX(sphereRadiusVec);
	const float backupDist = sphereRadius + nearClip + Backup;

	// Get Position of the shadow camera
	XMVECTOR shadowCameraPosVec = sphereCenterVec;
	XMVECTOR backupDirVec = XMVectorNegate(XMLoadFloat3(&this->direction));
	backupDirVec = XMVectorScale(backupDirVec, backupDist);
	shadowCameraPosVec = XMVectorAdd(shadowCameraPosVec, backupDirVec);
	XMVECTOR upVec = XMVectorSet(0.0f, 1.0f, 0.0f, 1.0f);

	// Come up witha new orthographic camera for the shadow caster
	XMMATRIX shadowProjection = XMMatrixOrthographicOffCenterLH(-sphereRadius, sphereRadius,
														        -sphereRadius, sphereRadius,
														         nearClip, backupDist + sphereRadius);

	XMMATRIX shadowView = XMMatrixLookAtLH(shadowCameraPosVec, sphereCenterVec, upVec);
	XMMATRIX shadowViewProjection = XMMatrixMultiply(shadowView, shadowProjection);
	
	// Create the rounding matrix, by projecting the world-space origin and determining
	// the fractional offset in texel space
	XMMATRIX shadowMatrix = shadowViewProjection;
	XMVECTOR shadowOrigin = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
	shadowOrigin = XMVector4Transform(shadowOrigin, shadowMatrix);
	shadowOrigin = XMVectorScale(shadowOrigin, this->shadowMapSize / 2.0f);

	XMVECTOR roundedOrigin = XMVectorRound(shadowOrigin);
	XMVECTOR roundOffset = XMVectorSubtract(roundedOrigin, shadowOrigin);
	roundOffset = XMVectorScale(roundOffset, 2.0f / this->shadowMapSize);
	roundOffset = XMVectorSetZ(roundOffset, 0.0f);
	roundOffset = XMVectorSetW(roundOffset, 0.0f);

	shadowProjection.r[3] = XMVectorAdd(shadowProjection.r[3], roundOffset);
	shadowViewProjection = XMMatrixMultiply(shadowView, shadowProjection);
	XMStoreFloat4x4(&this->ViewProjection, shadowViewProjection);
	shadowMatrix = shadowViewProjection;


	// Apply the scale / offset / bias matrix, which transforms from [-1,1]
	// post-projection space to [0,1] UV space
	const float bias = Bias;
	XMMATRIX texScaleBias;
	texScaleBias.r[0] = XMVectorSet(0.5f,  0.0f,  0.0f,  0.0f);
	texScaleBias.r[1] = XMVectorSet(0.0f, -0.5f,  0.0f,  0.0f);
	texScaleBias.r[2] = XMVectorSet(0.0f,  0.0f,  1.0f,  0.0f);
	texScaleBias.r[3] = XMVectorSet(0.5f,  0.5f, -bias,  1.0f);
	shadowMatrix = XMMatrixMultiply(shadowMatrix, texScaleBias);

	// Apply the cascade offset / scale matrix, which applies the offset and scale needed to
	// convert the UV coordinate into the proper coordinate for the cascade being sampled in
	// the atlas

	// Cascade offsets
	const XMFLOAT2 Offsets[4] = {
		XMFLOAT2(0.0f, 0.0f),
		XMFLOAT2(0.5f, 0.0f),
		XMFLOAT2(0.5f, 0.5f),
		XMFLOAT2(0.0f, 0.5f)
	};

	XMFLOAT4 offset(Offsets[cascadeIdx].x, Offsets[cascadeIdx].y, 0.0f, 1.0f);
	XMMATRIX cascadeOffsetMatrix = XMMatrixScaling(0.5f, 0.5f, 1.0f);
	cascadeOffsetMatrix.r[3] = XMLoadFloat4(&offset);
	shadowMatrix = XMMatrixMultiply(shadowMatrix, cascadeOffsetMatrix);
	
	// Store the shadow matrix
	XMStoreFloat4x4(&this->ShadowMatrices[cascadeIdx], shadowMatrix);

	// Store the split distance in terms of view space depth
	const float clipDist = camera->GetFarClip() - camera->GetNearClip();
	this->cascadeSplits[cascadeIdx] = camera->GetNearClip() + splitDist * clipDist;
} 

 

GPU side computation:

static const uint NumCascades = 4;


struct VSI
{
	float4 Position : POSITION0;
	float2 UV		: TEXCOORD0;
};


struct VSO
{
	float4 Position			: SV_POSITION;
	float2 UV				: TEXCOORD0;
	float3 ViewRay			: TEXCOORD1;
};





cbuffer cbShadowProjection : register(b0)
{
	float4x4 InverseProjection;
	float4x4 ShadowMatrices[4];
	float4 CascadeSplits;

	float ProjectionA;
	float ProjectionB;
	float2 shadowMapSize;
};







// Image texture
Texture2D ShadowMap : register(t0);
SamplerComparisonState ShadowMapSampler : register(s0);

Texture2D DepthTarget : register(t1);
SamplerState PointSampler : register(s1);








VSO VS(VSI input)
{
    VSO output = (VSO)0;

    output.Position = input.Position;
	output.UV = input.UV;

	float3 positionVS = mul(input.Position, InverseProjection);
	output.ViewRay = float3(positionVS.xy / positionVS.z, 1.0f);


    return output;
}





//--------------------------------------------------------------------------------------
// Samples the shadow map cascades based on the world-space position, using edge-tap
// smoothing PCF for filtering
//--------------------------------------------------------------------------------------
float SampleShadowCascade(in float3 positionVS, in uint cascadeIdx)
{
	float4x4 shadowMatrix = ShadowMatrices[cascadeIdx];
	float3 shadowPosition = mul(float4(positionVS, 1.0f), shadowMatrix).xyz;
	float2 shadowTexCoord = shadowPosition.xy;
	float shadowDepth = shadowPosition.z;

	// Edge tap smoothing
	const int Radius = 2;
	const float ShadowMapSize = 2048.0f * 2;
	const int NumSamples = (Radius * 2 + 1) * (Radius * 2 + 1);

	float2 fracs = frac(shadowTexCoord.xy * ShadowMapSize);
	float leftEdge = 1.0f - fracs.x;
	float rightEdge = fracs.x;
	float topEdge = 1.0f - fracs.y;
	float bottomEdge = fracs.y;

	float shadowVisibility = 0.0f;

	[unroll(NumSamples)]
	for (int y = -Radius; y <= Radius; y++)
	{
		[unroll(NumSamples)]
		for (int x = -Radius; x <= Radius; x++)
		{
			float2 offset = float2(x, y) * (1.0f / ShadowMapSize);
			float2 sampleCoord = shadowTexCoord + offset;
			float sample = ShadowMap.SampleCmp(ShadowMapSampler, sampleCoord, shadowDepth).x;

			float xWeight = 1;
			float yWeight = 1;

			if(x == -Radius)
				xWeight = leftEdge;
			else if(x == Radius)
				xWeight = rightEdge;

			if(y == -Radius)
				yWeight = topEdge;
			else if(y == Radius)
				yWeight = bottomEdge;

			shadowVisibility += sample * xWeight * yWeight;
		}
	}

	shadowVisibility  /= NumSamples;
	shadowVisibility *= 1.5f;

	return shadowVisibility;
}




//--------------------------------------------------------------------------------------
// Computes the visibility term by performing the shadow test
//--------------------------------------------------------------------------------------
float3 ShadowVisibility(in float3 positionVS)
{
	float3 shadowVisibility = 1.0f;
	uint cascadeIdx = 0;
	float depthVS = positionVS.z;

	// Figure out which cascade to sample from
	[unroll]
	for(uint i = 0; i < NumCascades - 1; ++i)
	{
		[flatten]
		if(depthVS > CascadeSplits[i])
			cascadeIdx = i + 1;
	}

	shadowVisibility = SampleShadowCascade(positionVS, cascadeIdx);

	// Sample the next cascade, and blend between the two results to
	// smooth the transition
	const float BlendThreshold = 0.1f;
	float nextSplit = CascadeSplits[cascadeIdx];
	float splitSize = i == 0 ? nextSplit : nextSplit - CascadeSplits[cascadeIdx - 1];
	float splitDist = (nextSplit - depthVS) / splitSize;

	float nextSplitVisibility = SampleShadowCascade(positionVS, cascadeIdx + 1);
	float lerpAmt = smoothstep(0.0f, BlendThreshold, splitDist);
	shadowVisibility = lerp(nextSplitVisibility, shadowVisibility, lerpAmt);

	if(cascadeIdx == 0)
	{
		shadowVisibility *= float3(1.0f, 0.0f, 0.0f);
	}
	else if(cascadeIdx == 1)
	{
		shadowVisibility *= float3(0.0f, 1.0f, 0.0f);
	}
	else if(cascadeIdx == 2)
	{
		shadowVisibility *= float3(0.0f, 0.0f, 1.0f);
	}
	else if(cascadeIdx == 3)
	{
		shadowVisibility *= float3(1.0f, 1.0f, 0.0f);
	}
	


	return shadowVisibility;
}





float4 PS(VSO input) : SV_TARGET0
{
	float4 output = float4(0.0f, 0.0f, 0.0f, 1.0f);

	// Reconstruct view space position from depth buffer
	float3 ViewRay = input.ViewRay.xyz;
	float depth = DepthTarget.Sample(PointSampler, input.UV).r;
	float linearDepth = ProjectionB / (depth - ProjectionA);
	float3 PositionVS = ViewRay * linearDepth;

	float3 shadowVisibility = ShadowVisibility(PositionVS);

	return float4(shadowVisibility, 1.0f);
}

 

 

I'm so close to having it working now, any idea what' I'm doing wrong ?
I still don't quite understand how the parameters "ShadowDist" and "Backup" have to be set.

 


#8lipsryme

Posted 04 January 2013 - 10:28 AM

<UPDATED 04. Jan. 2013>

Well so I've finally managed to get the correct projection working.

My problem now is that either the shadow map is not correct or the shader code is not sampling the cascades like it should.

From my observation it looks like it's only sampling from the first cascade and the rest is just black I guess.

I've captured a 30sec video showing it quickly:

<still processing>

 

Here's my most recent code:

 

CPU side calculation:

void DirectionalLight::CalculateLightProjection(BaseCamera* camera, UINT &cascadeIdx)
{
	static const float ShadowDist = 0.4f;
	static const float Backup = -1.0f;
	static const float CascadeSplits[4] = { 0.125f, 0.25f, 0.5f, 1.0f };
	static const float Bias = 0.005f;
	static const float nearClip = 1.0f;


	// Get the 8 points of the view frustum in world space
	XMVECTOR frustumCornerWS[8] = 
	{
		XMVectorSet(-1.0f,  1.0f,  0.0f,  1.0f),
		XMVectorSet( 1.0f,  1.0f,  0.0f,  1.0f),
		XMVectorSet( 1.0f, -1.0f,  0.0f,  1.0f),
		XMVectorSet(-1.0f, -1.0f,  0.0f,  1.0f),
		XMVectorSet(-1.0f,  1.0f,  1.0f,  1.0f),
		XMVectorSet( 1.0f,  1.0f,  1.0f,  1.0f),
		XMVectorSet( 1.0f, -1.0f,  1.0f,  1.0f),
		XMVectorSet(-1.0f, -1.0f,  1.0f,  1.0f),
	};

	float prevSplitDist = cascadeIdx == 0 ? 0.0f : CascadeSplits[cascadeIdx - 1] * ShadowDist;
	float splitDist = CascadeSplits[cascadeIdx] * ShadowDist;

	XMVECTOR det;
	XMMATRIX CamViewProj = camera->GetViewProjectionNonTransposed();
	XMMATRIX invViewProj = XMMatrixInverse(&det, CamViewProj);
	for(UINT i = 0; i < 8; ++i)
	{
		frustumCornerWS[i] = XMVector3TransformCoord(frustumCornerWS[i], invViewProj);
	}

	// Scale by the shadow view distance
	for(UINT i = 0; i < 4; ++i)
	{
		XMVECTOR cornerRay = XMVectorSubtract(frustumCornerWS[i + 4], frustumCornerWS[i]);
		XMVECTOR nearCornerRay = XMVectorScale(cornerRay, prevSplitDist);
		XMVECTOR farCornerRay = XMVectorScale(cornerRay, splitDist);
		frustumCornerWS[i + 4] = XMVectorAdd(frustumCornerWS[i], farCornerRay);
		frustumCornerWS[i] = XMVectorAdd(frustumCornerWS[i], nearCornerRay);
	}

	// Calculate the centroid of the view frustum
	XMVECTOR sphereCenterVec = XMVectorZero();
	for(UINT i = 0; i < 8; ++i)
	{
		sphereCenterVec = XMVectorAdd(sphereCenterVec, frustumCornerWS[i]);
	}
	sphereCenterVec = XMVectorScale(sphereCenterVec, 1.0f / 8.0f);

	// Calculate the radius of a bounding sphere
	XMVECTOR sphereRadiusVec = XMVectorZero();
	for(UINT i = 0; i < 8; ++i)
	{
		XMVECTOR dist = XMVector3Length(XMVectorSubtract(frustumCornerWS[i], sphereCenterVec));
		sphereRadiusVec = XMVectorMax(sphereRadiusVec, dist);
	}		

	sphereRadiusVec = XMVectorRound(sphereRadiusVec);
	const float sphereRadius = XMVectorGetX(sphereRadiusVec);
	const float backupDist = sphereRadius + nearClip + Backup;

	// Get Position of the shadow camera
	XMVECTOR shadowCameraPosVec = sphereCenterVec;
	XMVECTOR backupDirVec = XMVectorNegate(XMLoadFloat3(&this->direction));
	backupDirVec = XMVectorScale(backupDirVec, backupDist);
	shadowCameraPosVec = XMVectorAdd(shadowCameraPosVec, backupDirVec);
	XMVECTOR upVec = XMVectorSet(0.0f, 1.0f, 0.0f, 1.0f);

	// Come up witha new orthographic camera for the shadow caster
	XMMATRIX shadowProjection = XMMatrixOrthographicOffCenterLH(-sphereRadius, sphereRadius,
														        -sphereRadius, sphereRadius,
														         nearClip, backupDist + sphereRadius);

	XMMATRIX shadowView = XMMatrixLookAtLH(shadowCameraPosVec, sphereCenterVec, upVec);
	XMMATRIX shadowViewProjection = XMMatrixMultiply(shadowView, shadowProjection);
	
	// Create the rounding matrix, by projecting the world-space origin and determining
	// the fractional offset in texel space
	XMMATRIX shadowMatrix = shadowViewProjection;
	XMVECTOR shadowOrigin = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
	shadowOrigin = XMVector4Transform(shadowOrigin, shadowMatrix);
	shadowOrigin = XMVectorScale(shadowOrigin, this->shadowMapSize / 2.0f);

	XMVECTOR roundedOrigin = XMVectorRound(shadowOrigin);
	XMVECTOR roundOffset = XMVectorSubtract(roundedOrigin, shadowOrigin);
	roundOffset = XMVectorScale(roundOffset, 2.0f / this->shadowMapSize);
	roundOffset = XMVectorSetZ(roundOffset, 0.0f);
	roundOffset = XMVectorSetW(roundOffset, 0.0f);

	shadowProjection.r[3] = XMVectorAdd(shadowProjection.r[3], roundOffset);
	shadowViewProjection = XMMatrixMultiply(shadowView, shadowProjection);
	XMStoreFloat4x4(&this->ViewProjection, shadowViewProjection);
	shadowMatrix = shadowViewProjection;


	// Apply the scale / offset / bias matrix, which transforms from [-1,1]
	// post-projection space to [0,1] UV space
	const float bias = Bias;
	XMMATRIX texScaleBias;
	texScaleBias.r[0] = XMVectorSet(0.5f,  0.0f,  0.0f,  0.0f);
	texScaleBias.r[1] = XMVectorSet(0.0f, -0.5f,  0.0f,  0.0f);
	texScaleBias.r[2] = XMVectorSet(0.0f,  0.0f,  1.0f,  0.0f);
	texScaleBias.r[3] = XMVectorSet(0.5f,  0.5f, -bias,  1.0f);
	shadowMatrix = XMMatrixMultiply(shadowMatrix, texScaleBias);

	// Apply the cascade offset / scale matrix, which applies the offset and scale needed to
	// convert the UV coordinate into the proper coordinate for the cascade being sampled in
	// the atlas

	// Cascade offsets
	const XMFLOAT2 Offsets[4] = {
		XMFLOAT2(0.0f, 0.0f),
		XMFLOAT2(0.5f, 0.0f),
		XMFLOAT2(0.5f, 0.5f),
		XMFLOAT2(0.0f, 0.5f)
	};

	XMFLOAT4 offset(Offsets[cascadeIdx].x, Offsets[cascadeIdx].y, 0.0f, 1.0f);
	XMMATRIX cascadeOffsetMatrix = XMMatrixScaling(0.5f, 0.5f, 1.0f);
	cascadeOffsetMatrix.r[3] = XMLoadFloat4(&offset);
	shadowMatrix = XMMatrixMultiply(shadowMatrix, cascadeOffsetMatrix);
	
	// Store the shadow matrix
	XMStoreFloat4x4(&this->ShadowMatrices[cascadeIdx], shadowMatrix);

	// Store the split distance in terms of view space depth
	const float clipDist = camera->GetFarClip() - camera->GetNearClip();
	this->cascadeSplits[cascadeIdx] = camera->GetNearClip() + splitDist * clipDist;
}
void Shadows::RenderShadows(FSQ* &fsq,
							std::vector<Primitive*> primitiveList,
							std::vector<ModelObject*> modelList,
							ID3D11SamplerState* &pointSampler,
							LightManager* &lightManager)
{
	ID3D11DeviceContext* context = this->renderer->GetDeviceContext();
	ID3D11DepthStencilView* shadowMapDSV = lightManager->GetDominantDirectionalLight()->GetShadowMapDSV();
	context->ClearDepthStencilView(shadowMapDSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);


	// Set RenderTarget
	ID3D11RenderTargetView* nullRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = { NULL };
	context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, nullRenderTargets, shadowMapDSV);

	// Set DepthStencil to write enabled
	context->OMSetDepthStencilState(this->writeDepthStencilState, 0);


	// Set Shader
	context->VSSetShader(this->generateShadowMapShader->GetVS(), 0, 0);
	context->PSSetShader(NULL, 0, 0);


	// Set ColorWriteDisabled Blendstate
	float blendFactor[4] = {1, 1, 1, 1};
	context->OMSetBlendState(this->ColorWriteDisabledBlendState, blendFactor, 0xFFFFFFFF);


	//*****************************************************
	// DIRECTIONAL LIGHT SOURCE (CASCADED SHADOW MAPPING)
	//*****************************************************
	DirectionalLight* dominantLight = lightManager->GetDominantDirectionalLight();

	// Cascade offsets
	const XMFLOAT2 Offsets[4] = {
		XMFLOAT2(0.0f, 0.0f),
		XMFLOAT2(0.5f, 0.0f),
		XMFLOAT2(0.5f, 0.5f),
		XMFLOAT2(0.0f, 0.5f)
	};

	const float sMapSize = static_cast<float>(dominantLight->GetShadowMapSize());
	const unsigned int NumCascades = dominantLight->GetNumberCascades();

	// Render meshes to each cascade
	for(UINT cascadeIdx = 0; cascadeIdx < NumCascades; ++cascadeIdx)
	{
		// Set viewport
		D3D11_VIEWPORT viewport;
		viewport.TopLeftX = Offsets[cascadeIdx].x * sMapSize * 2;
		viewport.TopLeftY = Offsets[cascadeIdx].y * sMapSize * 2;
		viewport.Width = static_cast<float>(sMapSize);
		viewport.Height = viewport.Width;
		viewport.MinDepth = 0.0f;
		viewport.MaxDepth = 1.0f;
		context->RSSetViewports(1, &viewport);

		// Compute the cascaded shadow transformations
		dominantLight->CalculateLightProjection(renderer->GetEngine()->GetCamera(), cascadeIdx);


		// Render Shadow Map
		dominantLight->RenderShadowMap(context, primitiveList, modelList);
	}





	// Set input and output off
	ID3D11ShaderResourceView* NullResource = NULL;
	ID3D11RenderTargetView* NullRT = NULL;
	context->OMSetRenderTargets(1, &NullRT, NULL);

	// Set the blend state for opaque objects
	context->OMSetBlendState(0, 0, 0xFFFFFFFF);

	// Set DepthStencil to read only
	context->OMSetDepthStencilState(this->readOnlyDepthStencilState, 0);

	// Set back to original
	context->RSSetViewports(1, &this->renderer->graphicsDesc.viewport);

	// Clear Shadow Accumulation target with white
	ID3D11RenderTargetView* shadowAccRTV = this->shadowAccTarget->GetRenderTargetView();
	context->ClearRenderTargetView(shadowAccRTV, D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f));

	// Set Render Target
	context->OMSetRenderTargets(1, &shadowAccRTV, NULL);

	// Set Shader
	context->VSSetShader(this->accumulateShadowsShader->GetVS(), 0, 0);
	context->PSSetShader(this->accumulateShadowsShader->GetPS(), 0, 0);



	//*************************************************
	// DIRECTIONAL LIGHT SOURCE
	//*************************************************


	// Set Sampler & SRV
	ID3D11ShaderResourceView* shadowMapSRV = dominantLight->GetShadowMapSRV();
	ID3D11SamplerState* shadowMapSampler = dominantLight->GetShadowMapSampler();
	context->PSSetShaderResources(0, 1, &shadowMapSRV);
	context->PSSetSamplers(0, 1, &shadowMapSampler);
	
	ID3D11ShaderResourceView* depthTargetSRV = this->renderer->GetDepthBufferTarget();
	context->PSSetShaderResources(1, 1, &depthTargetSRV);
	context->PSSetSamplers(1, 1, &pointSampler);


	// Set constant buffer
	D3D11_MAPPED_SUBRESOURCE mappedResource;

	// Lock the constant buffer so it can be written to
	context->Map(cbShadowProjection, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);

	// Get a pointer to the data in the constant buffer.
	cbShadowProjectionProperties* pTransformData = (cbShadowProjectionProperties*)mappedResource.pData;


	// Copy the matrices into the constant buffer
	XMVECTOR det;
	XMMATRIX view = this->renderer->GetEngine()->GetCamera()->GetViewMatrixNonTransposed();
	view = XMMatrixInverse(&det, view);
	XMMATRIX shadowMatrices[4];
	for(int i = 0; i < 4; i++)
	{
		// Set Shadow matrices
		//-----------------------------
		shadowMatrices[i] = XMLoadFloat4x4(&dominantLight->GetShadowMatrix(i));

		// Premultiply inverse view with the light's view projection
		shadowMatrices[i] = XMMatrixMultiplyTranspose(view, shadowMatrices[i]);

		XMStoreFloat4x4(&pTransformData->ShadowMatrix[i], shadowMatrices[i]);

		// Set Cascade Splits
		//----------------------
		pTransformData->cascadeSplits[i] = dominantLight->GetShadowCascadeSplit(i);
	}

	XMMATRIX Projection = this->renderer->GetEngine()->GetCamera()->GetProjectionMatrixNonTransposed();
	Projection = XMMatrixInverse(&det, Projection);
	Projection = XMMatrixTranspose(Projection);
	XMStoreFloat4x4(&pTransformData->InverseProjection, Projection);

	pTransformData->shadowMapSize = XMFLOAT2(sMapSize, sMapSize);

	float nearClip = this->renderer->GetEngine()->GetCamera()->GetNearClip();
	float farClip = this->renderer->GetEngine()->GetCamera()->GetFarClip();

	pTransformData->ProjectionA = farClip / (farClip - nearClip);
	pTransformData->ProjectionB = (-farClip * nearClip) / (farClip - nearClip);


	// Unlock the constant buffer
	context->Unmap(this->cbShadowProjection, 0);


	// Set Constant buffer
	context->VSSetConstantBuffers(0, 1, &this->cbShadowProjection);
	context->PSSetConstantBuffers(0, 1, &this->cbShadowProjection);

	// Draw Fullscreen-Quad
	fsq->Draw(context);


	// Set input and output off
	context->PSSetShaderResources(0, 1, &NullResource);
	context->PSSetShaderResources(1, 1, &NullResource);
	context->OMSetRenderTargets(1, &NullRT, NULL);

}

 

GPU side computation:

static const uint NumCascades = 4;


struct VSI
{
	float4 Position : POSITION0;
	float2 UV		: TEXCOORD0;
};


struct VSO
{
	float4 Position			: SV_POSITION;
	float2 UV				: TEXCOORD0;
	float3 ViewRay			: TEXCOORD1;
};





cbuffer cbShadowProjection : register(b0)
{
	float4x4 InverseProjection;
	float4x4 ShadowMatrices[4];
	float4 CascadeSplits;

	float ProjectionA;
	float ProjectionB;
	float2 shadowMapSize;
};







// Image texture
Texture2D ShadowMap : register(t0);
SamplerComparisonState ShadowMapSampler : register(s0);

Texture2D DepthTarget : register(t1);
SamplerState PointSampler : register(s1);








VSO VS(VSI input)
{
    VSO output = (VSO)0;

    output.Position = input.Position;
	output.UV = input.UV;

	float3 positionVS = mul(input.Position, InverseProjection);
	output.ViewRay = float3(positionVS.xy / positionVS.z, 1.0f);


    return output;
}





//--------------------------------------------------------------------------------------
// Samples the shadow map cascades based on the world-space position, using edge-tap
// smoothing PCF for filtering
//--------------------------------------------------------------------------------------
float SampleShadowCascade(in float3 positionVS, in uint cascadeIdx)
{
	float4x4 shadowMatrix = ShadowMatrices[cascadeIdx];
	float3 shadowPosition = mul(float4(positionVS, 1.0f), shadowMatrix).xyz;
	float2 shadowTexCoord = shadowPosition.xy;
	float shadowDepth = shadowPosition.z;

	// Edge tap smoothing
	const int Radius = 2;
	const float ShadowMapSize = 2048.0f * 2;
	const int NumSamples = (Radius * 2 + 1) * (Radius * 2 + 1);

	float2 fracs = frac(shadowTexCoord.xy * ShadowMapSize);
	float leftEdge = 1.0f - fracs.x;
	float rightEdge = fracs.x;
	float topEdge = 1.0f - fracs.y;
	float bottomEdge = fracs.y;

	float shadowVisibility = 0.0f;

	[unroll(NumSamples)]
	for (int y = -Radius; y <= Radius; y++)
	{
		[unroll(NumSamples)]
		for (int x = -Radius; x <= Radius; x++)
		{
			float2 offset = float2(x, y) * (1.0f / ShadowMapSize);
			float2 sampleCoord = shadowTexCoord + offset;
			float sample = ShadowMap.SampleCmp(ShadowMapSampler, sampleCoord, shadowDepth).x;

			float xWeight = 1;
			float yWeight = 1;

			if(x == -Radius)
				xWeight = leftEdge;
			else if(x == Radius)
				xWeight = rightEdge;

			if(y == -Radius)
				yWeight = topEdge;
			else if(y == Radius)
				yWeight = bottomEdge;

			shadowVisibility += sample * xWeight * yWeight;
		}
	}

	shadowVisibility  /= NumSamples;
	shadowVisibility *= 1.5f;

	return shadowVisibility;
}




//--------------------------------------------------------------------------------------
// Computes the visibility term by performing the shadow test
//--------------------------------------------------------------------------------------
float3 ShadowVisibility(in float3 positionVS)
{
	float3 shadowVisibility = 1.0f;
	uint cascadeIdx = 0;
	float depthVS = positionVS.z;

	// Figure out which cascade to sample from
	[unroll]
	for(uint i = 0; i < NumCascades - 1; ++i)
	{
		[flatten]
		if(depthVS > CascadeSplits[i])
			cascadeIdx = i + 1;
	}

	shadowVisibility = SampleShadowCascade(positionVS, cascadeIdx);

	// Sample the next cascade, and blend between the two results to
	// smooth the transition
	const float BlendThreshold = 0.1f;
	float nextSplit = CascadeSplits[cascadeIdx];
	float splitSize = i == 0 ? nextSplit : nextSplit - CascadeSplits[cascadeIdx - 1];
	float splitDist = (nextSplit - depthVS) / splitSize;

	float nextSplitVisibility = SampleShadowCascade(positionVS, cascadeIdx + 1);
	float lerpAmt = smoothstep(0.0f, BlendThreshold, splitDist);
	shadowVisibility = lerp(nextSplitVisibility, shadowVisibility, lerpAmt);

	if(cascadeIdx == 0)
	{
		shadowVisibility *= float3(1.0f, 0.0f, 0.0f);
	}
	else if(cascadeIdx == 1)
	{
		shadowVisibility *= float3(0.0f, 1.0f, 0.0f);
	}
	else if(cascadeIdx == 2)
	{
		shadowVisibility *= float3(0.0f, 0.0f, 1.0f);
	}
	else if(cascadeIdx == 3)
	{
		shadowVisibility *= float3(1.0f, 1.0f, 0.0f);
	}
	


	return shadowVisibility;
}





float4 PS(VSO input) : SV_TARGET0
{
	float4 output = float4(0.0f, 0.0f, 0.0f, 1.0f);

	// Reconstruct view space position from depth buffer
	float3 ViewRay = input.ViewRay.xyz;
	float depth = DepthTarget.Sample(PointSampler, input.UV).r;
	float linearDepth = ProjectionB / (depth - ProjectionA);
	float3 PositionVS = ViewRay * linearDepth;

	float3 shadowVisibility = ShadowVisibility(PositionVS);

	return float4(shadowVisibility, 1.0f);
}

 

 

I'm so close to having it working now, any idea what' I'm doing wrong ?

I still don't quite understand how the parameters "ShadowDist" and "Backup" have to be set.


#7lipsryme

Posted 04 January 2013 - 10:19 AM

<UPDATED 04. Jan. 2013>

see last post

#6lipsryme

Posted 04 January 2013 - 10:19 AM

<UPDATED 04. Jan. 2013>

#5lipsryme

Posted 29 November 2012 - 07:42 AM

Hey guys I've got a problem with the rendering of the shadowed parts to a texture (deferred).
As I got my code from a non-deferred sample I'm having a hard time converting this to a deferred approach.
In the sample code he is somehow calculating the correct sample coordinate from the position in world-space and the view-space depth.
Now what I did was try to reconstruct the view space position using my depth buffer and then using it's Z component as the view-space depth.
I think this parts works. Then I try to transform this to world space somehow which doesn't seem to work as I'm just getting a black screen.

Here's my shader code at the moment:

Vertex shader:
VSO VS(VSI input)
{
    VSO output = (VSO)0;
    output.Position = float4(input.Position.xyz, 1.0f);
    output.UV = input.UV;
    output.PositionVS = mul(input.Position, InvProjection).xyz;
    output.PositionVS = float3(output.Position.xy / output.Position.z, 1.0f);
    return output;
}

Update: If I just pass "output.PositionVS = mul(input.Position, InvProjection).xyz" then I'm getting something....well..."something"
screenshot: http://cl.ly/image/27201D051m2S

SampleShadowCascade:
float SampleShadowCascade(in float3 positionWS, in uint cascadeIdx)
{
    float4x4 shadowMatrix = ShadowMatrices[cascadeIdx];
    float3 shadowPosition = mul(float4(positionWS, 1.0f), shadowMatrix).xyz;
    float2 shadowTexCoord = shadowPosition.xy;
    float shadowDepth = shadowPosition.z;

    // Edge tap smoothing
    const int Radius = 2;
    const int NumSamples = (Radius *2 + 1) * (Radius * 2 + 1);

    float2 fracs = frac(shadowTexCoord.xy * ShadowMapSize);
    float leftEdge = 1.0f - fracs.x;
    float rightEdge = fracs.x;
    float topEdge = 1.0f - fracs.y;
    float bottomEdge = fracs.y;

    float shadowVisibility = 0.0f;

    [unroll(NumSamples)]
    for(int y = -Radius; y <= Radius; y++)
    {
      [unroll(NumSamples)]
      {
        for(int x = -Radius; x <= Radius; x++)
        {
          float2 offset = float2(x, y) * (1.0f / ShadowMapSize);
          float2 sampleCoord = shadowTexCoord + offset;
          float currentSample = ShadowMap.SampleCmp(ShadowMapSampler, sampleCoord, shadowDepth).x;

          float xWeight = 1;
          float yWeight = 1;

          if(x == -Radius)
          {
	     xWeight = leftEdge;
          }
          else if(x == Radius)
          {
	     xWeight = rightEdge;
          }
          if(y == -Radius)
          {
	    yWeight = topEdge;
          }
          else if(y == Radius)
          {
	     yWeight = bottomEdge;
          }
          shadowVisibility += currentSample * xWeight * yWeight;
        }
       }
       shadowVisibility /= NumSamples;
       shadowVisibility *= 1.5f;
    }
    return shadowVisibility;
}


ShadowVisibility:
float3 ShadowVisibility(in float3 positionWS, in float depthVS)
{
    float3 shadowVisibility = 1.0f;
    uint cascadeIdx = 0;

    // Figure out which cascade to sample from
    [unroll]
    for(uint i = 0; i < NumCascades - 1; ++i)
    {
      [flatten]
      if(depthVS > CascadeSplits[i])
      {
       cascadeIdx = i + 1;
      }
    }
    shadowVisibility = SampleShadowCascade(positionWS, cascadeIdx);

    // Sample the next cascade, and blend between the two results to
    // smooth the transition
    const float BlendThreshold = 0.1f;
    float nextSplit = CascadeSplits[cascadeIdx];
    float splitSize = i == 0 ? nextSplit : nextSplit - CascadeSplits[cascadeIdx - 1];
    float splitDist = (nextSplit - depthVS) / splitSize;

    float nextSplitVisibility = SampleShadowCascade(positionWS, cascadeIdx + 1);
    float lerpAmt = smoothstep(0.0f, BlendThreshold, splitDist);

    shadowVisibility = lerp(nextSplitVisibility, shadowVisibility, lerpAmt);

    return shadowVisibility;
}


Pixel shader:
float LinearDepth(in float zw)
{
    return Projection._43 / (zw - Projection._33) / farClip;
}


float4 PS(VSO input) : SV_TARGET0
{
    float4 output = float4(0.0f, 0.0f, 0.0f, 1.0f);

    float depth = DepthTarget.Sample(PointSampler, input.UV).r;
    float linearDepth = LinearDepth(depth);

    float3 viewRay = input.PositionVS.xyz;

    float3 PositionVS = viewRay * linearDepth;
    float depthVS = PositionVS.z;

    viewRay = normalize(viewRay);
    float viewZDist = dot(CamForward, viewRay);
    float3 PositionWS = CamPosition + viewRay * (linearDepth / viewZDist);

    float3 shadowVisibility = ShadowVisibility(PositionWS.xyz, depthVS);

    return float4(shadowVisibility, 1.0f);
}

PARTNERS