Shadow map generation shader
cbuffer c_buffer
{
float4x4 LightViewProj;
}
struct VS_OUT
{
float4 Pos : SV_POSITION;
};
VS_OUT VShader(float4 position : POSITION, float4 normal : NORMAL)
{
VS_OUT output;
output.Pos = mul(LightViewProj, position);
return output;
}
void PShader(VS_OUT input) {}
Shadow mapping shader
cbuffer c_buffer
{
float4x4 World;
float4x4 WorldViewProj;
float4x4 LightViewProj;
float4 LightDirection;
float4 Ambient;
float4 Eye;
}
cbuffer material
{
float4 Diffuse;
float4 Specular;
float Shininess;
}
Texture2D shadowMap;
SamplerState pointSampler; // I'm not setting this in the application, i'm using the default sampler
struct VS_OUT
{
float4 Pos : SV_POSITION;
float4 lpos : TEXCOORD0;
float3 Nor : NORMAL;
float3 Half : TEXCOORD2;
};
VS_OUT VShader(float4 position : POSITION, float4 normal : NORMAL)
{
VS_OUT output = (VS_OUT)0;
output.Pos = mul(WorldViewProj, position);
output.Nor = normalize(mul(normal, World)).xyz;
// Store worldspace position projected to light clip space
output.lpos = mul(LightViewProj, position);
float3 view = normalize(Eye - mul(World, position)).xyz;
output.Half = normalize(LightDirection.xyz + view);
return output;
}
float4 PShader(VS_OUT input) : SV_TARGET
{
//re-homogenize position after interpolation
input.lpos.xyz /= input.lpos.w;
//if position is not visible to the light - dont illuminate it
//results in hard light frustum
if( input.lpos.x < -1.0f || input.lpos.x > 1.0f ||
input.lpos.y < -1.0f || input.lpos.y > 1.0f ||
input.lpos.z < 0.0f || input.lpos.z > 1.0f ) return Ambient;
//transform clip space coords to texture space coords (-1:1 to 0:1)
input.lpos.x = input.lpos.x/2 + 0.5;
input.lpos.y = input.lpos.y/-2 + 0.5;
//sample shadow map - point sampler
float shadowMapDepth = shadowMap.Sample(pointSampler, input.lpos.xy).r;
//if clip space z value greater than shadow map value then pixel is in shadow
if (shadowMapDepth < input.lpos.z) return Ambient;
//otherwise calculate ilumination at fragment
input.Nor = normalize(input.Nor);
input.Half = normalize(input.Half);
float4 TotalDiffuse = Diffuse * saturate(dot(input.Nor, LightDirection.xyz));
float4 TotalSpecular = Specular * pow(saturate(dot(input.Nor, input.Half)), Shininess);
return Ambient + TotalDiffuse + TotalSpecular;
}
How i generate shadow map resources
//=====================================================================================================
// Create shadow map resources
//=====================================================================================================
void Framework::CreateShadowMap()
{
// Create shadow map texture desc
D3D11_TEXTURE2D_DESC td;
ZeroMemory(&td, sizeof(td));
td.Width = mClientSize.cx;
td.Height = mClientSize.cy;
td.MipLevels = 1;
td.ArraySize = 1;
td.Format = DXGI_FORMAT_R32_TYPELESS;
td.SampleDesc.Count = 1;
td.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
// Create the depth stencil view desc
D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
ZeroMemory(&dsvd, sizeof(dsvd));
dsvd.Format = DXGI_FORMAT_D32_FLOAT;
dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
// Create shader resource view desc
D3D11_SHADER_RESOURCE_VIEW_DESC srvd;
ZeroMemory(&srvd, sizeof(srvd));
srvd.Format = DXGI_FORMAT_R32_FLOAT;
srvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvd.Texture2D.MipLevels = td.MipLevels;
// Create texture and depth/resource views
mShadowMap = mRenderer->CreateTexture2D(td);
mShadowMapDepthView = mRenderer->CreateDepthStencilView(mShadowMap, dsvd);
mShadowMapSRView = mRenderer->CreateShaderResourceView(mShadowMap, srvd);
}
And finally the rendering..
//=====================================================================================================
// Render stuff
//=====================================================================================================
bool Framework::OnRenderStart()
{
D3DXMATRIX view, proj, world, rotX;
D3DXMatrixIdentity(&world);
D3DXMatrixRotationX(&rotX, D3DXToRadian(90));
world = rotX;
// create a projection matrix
D3DXMatrixPerspectiveFovLH(&proj,
(FLOAT)D3DXToRadian(45),
(FLOAT)mClientSize.cx / (FLOAT)mClientSize.cy,
1.0f,
1000.0f);
// View matrix
D3DXMATRIX lView, lProj;
D3DXMatrixLookAtLH(&lView, &mLight.Position, &mLight.Direction, &D3DXVECTOR3(0, 1, 0));
// Compute the light projection matrix
D3DXMatrixOrthoLH(&lProj, 45.0f, 45.0f, 1.0f, 1024.0f);
// Compute the light-view-projection matrix
D3DXMATRIX LightViewProj = world * lView * lProj;
// Shadow pass
mRenderer->SetRenderTargets(0, 0, mShadowMapDepthView);
mRenderer->ClearDepthStencilView(mShadowMapDepthView);
mEffectDepth->SetShaderMatrix("LightViewProj", LightViewProj);
for (int i = 0; i < mVisibleMeshes.size(); i++)
{
mRenderer->DrawIndexed(mEffectDepth,
mVisibleMeshes->mVertexBuffer,
mVisibleMeshes->mIndexBuffer,
mVisibleMeshes->mInputLayout,
mVisibleMeshes->IndexCount,
0,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
// Render
mRenderer->SetRenderTargets(1, &mRenderTarget, mZBuffer);
mRenderer->ClearRenderTargetView(mRenderTarget);
mRenderer->ClearDepthStencilView(mZBuffer);
mEffect->SetShaderMatrix("World", world);
mEffect->SetShaderMatrix("WorldViewProj", world * mCamera.GetViewMatrix(view) * proj);
mEffect->SetShaderVector("Ambient", D3DXVECTOR3(0.2f, 0.2f, 0.2f));
mEffect->SetShaderVector("Eye", mCamera.GetPosition());
mEffect->SetShaderVector("LightDirection", mLight.Direction);
mEffect->SetShaderMatrix("LightViewProj", LightViewProj);
mEffect->SetShaderTexture("shadowMap", mShadowMapSRView);
if (mVisibleMeshes[0]->mMaterial)
{
mVisibleMeshes[0]->mEffect->SetShaderVector("Diffuse", mVisibleMeshes[0]->mMaterial->Diffuse);
mVisibleMeshes[0]->mEffect->SetShaderVector("Specular", mVisibleMeshes[0]->mMaterial->Specular);
mVisibleMeshes[0]->mEffect->SetShaderFloat("Shininess", mVisibleMeshes[0]->mMaterial->Shininess);
}
for (int i = 0; i < mVisibleMeshes.size(); i++)
{
mRenderer->DrawIndexed(mVisibleMeshes->mEffect,
mVisibleMeshes->mVertexBuffer,
mVisibleMeshes->mIndexBuffer,
mVisibleMeshes->mInputLayout,
mVisibleMeshes->IndexCount,
0,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
ID3D11ShaderResourceView *const pSRV[1] = {NULL};
mRenderer->mImmediateContext->PSSetShaderResources(0, 1, pSRV);
return true;
}