Quote: "@GG Yes I can do a C++ version for you. Not a problem."
Thanks. I look forward to it.
I'm stuck at the moment trying to get the PCF ("Percentage Closer Filtering") idea working correctly. I'm using a 2x2 sample of the shadow map and trying to use PCF to give smooth edges to the shadows.
The method is almost working - I'm using the PCF method given in the MS DX9 SDK shadow map shader. I'm getting nice smooth blocks around the edges - but they are oriented the wrong way and I just can't see why.
Here's a typical close-up of a shadow edge (I'm using a white texture for the plain to make the shadow edges clearer):
and here's the corresponding shader code:
// Green Gandalf's basic shadow mapping shader v3
// Created 19 April 2008, edited 10 October 2009.
// Uses ideas and some code from:
// MS DX9 SDK shadow mapping demo "ShadowMap.fx"
// nVidia shadow mapping demo "shadRPortHW.fx"
// EVOLVED's shadow mapping demo "ShadowMapping.fx".
float4x4 wvp : WorldViewProjection;
float4x4 mw : World;
float4x4 texMat = { 0.5, 0.0, 0.0, 0.5,
0.0,-0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0};
float4x4 lightProjMatrix;
float4 filterSamples[4] = {-0.5, -0.5, 0.0, 0.0,
0.5, -0.5, 0.0, 0.0,
-0.5, 0.5, 0.0, 0.0,
0.5, 0.5, 0.0, 0.0};
float shadowSamples[4] = {0.0, 0.0, 0.0, 0.0};
float filterWidth = 0.001953125;
float4 ambient = {0.1, 0.1, 0.1, 1.0};
float4 lightCol = {1.0, 1.0, 1.0, 1.0};
float3 lightDir = {0.0, -1.0, 0.0};
float lightProjScale = 0.002; // reciprocal of max likely depth (default 500)
texture baseTexture < string ResourceName=""; >;
sampler baseSample = sampler_state
{ texture = <baseTexture>;
minFilter = linear;
magFilter = linear;
mipFilter = linear;
addressU = wrap;
addressV = wrap;
};
texture shadowMap < string ResourceName=""; >;
sampler depthSample = sampler_state
{ texture = <shadowMap>;
minFilter = linear;
magFilter = linear;
mipFilter = linear;
addressU = clamp;
addressV = clamp;
};
struct VSInput
{ float4 pos : position;
float3 normal : normal;
float2 UV : texcoord0;
};
struct VSOutDepth
{ float4 pos : position;
float depth : texcoord0;
};
struct VSOutScene
{ float4 pos : position;
float2 UV : texcoord0;
float4 lightProj : texcoord1;
float3 normal : texcoord2;
};
struct PSOutput { float4 col : color; };
VSOutDepth VSDepth( VSInput In, VSOutDepth Out )
{ float4 sPos = mul(In.pos, wvp);
Out.pos = sPos;
Out.depth = sPos.z * lightProjScale; // try to aim for values in range 0.0 to 1.0
return Out;
}
VSOutScene VSScene( VSInput In, VSOutScene Out )
{ Out.pos = mul(In.pos, wvp);
Out.UV = In.UV;
float4 wPos = mul(In.pos, mw);
float4 lightProj = mul(wPos, lightProjMatrix);
Out.lightProj = mul(texMat, lightProj); // convert to coords for projective texture lookup
Out.normal = mul(In.normal, (float3x3) mw );
return Out;
}
PSOutput PSDepth( VSOutDepth In, PSOutput Out)
{ Out.col = In.depth;
return Out;
}
PSOutput PSScene( VSOutScene In, PSOutput Out)
{ float4 base = tex2D(baseSample, In.UV);
float depth = In.lightProj.z * lightProjScale - 0.1;
float2 lerps = frac(In.lightProj.xy);
for (int i = 0; i < 4; i++)
{ shadowSamples[i] = tex2Dproj(depthSample, In.lightProj + filterSamples[i] * filterWidth * In.lightProj.w).r < depth
? 1.0 : 0.0;
}
float shadow = 1.0 - lerp( lerp( shadowSamples[0], shadowSamples[1], lerps.x ),
lerp( shadowSamples[2], shadowSamples[3], lerps.x ),
lerps.y );
float diffuse = saturate(dot(-lightDir, normalize(In.normal)));
float4 light = saturate(lightCol * diffuse * shadow + ambient);
Out.col = base * light;
return Out;
}
technique shadow
{ pass p1
{ vertexShader = compile vs_2_0 VSDepth();
pixelShader = compile ps_2_0 PSDepth();
}
}
technique scene
{ pass p1
{ vertexShader = compile vs_2_0 VSScene();
pixelShader = compile ps_2_0 PSScene();
}
}
The PCF part of the code is this:
float4 filterSamples[4] = {-0.5, -0.5, 0.0, 0.0,
0.5, -0.5, 0.0, 0.0,
-0.5, 0.5, 0.0, 0.0,
0.5, 0.5, 0.0, 0.0};
.
(etc)
.
float2 lerps = frac(In.lightProj.xy);
for (int i = 0; i < 4; i++)
{ shadowSamples[i] = tex2Dproj(depthSample, In.lightProj + filterSamples[i] * filterWidth * In.lightProj.w).r < depth
? 1.0 : 0.0;
}
float shadow = 1.0 - lerp( lerp( shadowSamples[0], shadowSamples[1], lerps.x ),
lerp( shadowSamples[2], shadowSamples[3], lerps.x ),
lerps.y );
I've tried all sorts of variations e.g. swapping lerps.x and lerps.y, array elements 0 and 2 with 1 and 3, etc, to no avail - I just keep getting different wrongly oriented versions. I'm probably doing something silly but just can't see it.
Edit
You might need the dba code as well:
` Green Gandalf's Basic Shadow Mapping Demo v3
` Uses a shader which attempts to sort out the problem of the jagged edges.
` Uses some code and ideas from EVOLVED's shadow mapping demo.
` Created 29 April 2009, modified 10 October 2009.
set display mode desktop width(), desktop height(), 32
sync on : sync rate 60 : sync
` setup main camera
backdrop on
color backdrop rgb(32,196,32)
autocam off
position camera 0, 200, -200
point camera 0, 0, 0
global cx#
global cy#
cx# = camera angle x()
cy# = camera angle y()
null = make matrix4(1)
null = make matrix4(2)
null = make matrix4(3)
null = make vector4(4)
` load textures
load image "Media/rockS.png", 1
`load image "Media/StoneWall02S.bmp", 2
load image "Media/white.png", 2
` load effect
load effect "Media/ShadowMappingGG v3.fx", 1, 0
set vector4 4, 0.2, 0.2, 0.2, 0.0
set effect constant vector 1, "ambient", 4
filterWidth# = 0.001953125
` create light object
make object sphere 1, 5
set object light 1, 0
lightX# = -56.0 : lightY# = 200.0 : lightZ# = -50.0
position object 1, lightX#, lightY#, lightZ#
lightAngleX# = 56.0 : lightAngleY# = 75.0 : lightAngleZ#=0.0
` light camera
make camera 1
color backdrop 1, rgb(255, 255, 255)
set camera to image 1, 4, 512, 512
set camera fov 1, 120
position camera 1, lightX#, lightY#, lightZ#
rotate camera 1, lightAngleX#, lightAngleY#, lightAngleZ#
` create floor
make object plain 2, 500.0, 500.0
xrotate object 2, -90
position object 2, 0.0, 0.0, 0.0
texture object 2, 0, 2
texture object 2, 1, 4
scale object texture 2, 0, 3, 3
set object effect 2, 1
` create moving objects
image = 1
for obj = 3 to 8
make object sphere obj, 50.0, 40, 40
texture object obj, 0, image
texture object obj, 1, 4
set object effect obj, 1
image = 3 - image
next obj
rotate = 1
` find light projection matrix using light camera
set current camera 1
view matrix4 1 : projection matrix4 2
multiply matrix4 3, 1, 2
set effect constant matrix 1, "lightProjMatrix", 3
` now get the light direction
move camera 1, 1
lightDirX# = camera position x(1) - lightX#
lightDirY# = camera position y(1) - lightY#
lightDirZ# = camera position z(1) - lightZ#
set vector4 4, lightDirX#, lightDirY#, lightDirZ#, 0.0
normalize vector4 4, 4
set effect constant vector 1, "lightDir", 4
move camera 1, -1
set current camera 0
repeat
center text screen width()/2, 20, "Press 1 for Shadow Map, m to toggle object movement, +/- to change filter width"
center text screen width()/2, 50, " Filter width = " + str$(filterWidth#)
center text screen width()/2, 80, "FPS "+str$(screen fps())
`check input
key$ = inkey$()
if key$ = "m"
if keyNow = 0
keyNow = 1
rotate = 1 - rotate
endif
else
if key$ = "="
inc filterWidth#, 0.00390625
else
if key$ = "-"
dec filterWidth#, 0.00390625
if filterWidth# < 0.0 then filterWidth# = 0.0
else
keyNow = 0
endif
endif
endif
` move objects
if rotate=1
inc angle#, 0.5
for obj = 3 to 8
position object obj, sin(angle# * obj + 30.0 * obj) * 55.0, sin(angle# + 20.0 * obj) * 55.0 + 80.0, sin(angle# * obj + 35.0 * obj) * 35.0
next obj
endif
`update cameras
positionCamera()
set effect technique 1, "shadow"
sync mask 2
fastsync
if key$ = "1" then paste image 4, 0, 0
` main scene
set effect technique 1, "scene"
set effect constant float 1, "filterWidth", filterWidth#
sync mask 1
sync
until spacekey()
end
function positionCamera()
control camera using arrowkeys 0, 1, 0
cx# = cx# + mousemovey(): cy# = cy# + mousemovex()
rotate camera cx#, cy#, 0
endfunction