Ogre Version: 1.12.13
Operating System: Windows 10
Render System: Direct3D9
Hey!
I am having some problems regarding shadow-casting spotlights in Direct3D9 that has existed since 1.11.2.
Shadows are sometimes visible on surfaces that should really not have shadows.
To fix this, shadow bias is used, but in Direct3D9, this bias act quite strange for spotlight shadows.
Here is a video showing the problem (first part shows the problem, the second part at 0:09 shows the fix with my very ugly solution):
I am using my own written shaders for shadow casting and shadow receiving, and both versions of shaders looks exactly the same on Direct3D9 and Direct3D11 (apart from Direct3D11 is edited to use "Texture2D" and "SamplerState" instead of just "sampler" of course).
The problem does not exist in Direct3D11, only in Direct3D9.
The problem does not exist for my shadow-casting directional light either, only for spotlights.
I have tried different attenuation (which means far plane distance) and setSpotlightNearClipDistance with the spotlight, but it never changes the results (apart from not rendering the shadows as far of course).
My code is basically exactly the same as the one here:
https://wiki.ogre3d.org/Depth+Shadow+Mapping
I have also tried with the code when using LINEAR_RANGE, but it does not help.
I am not using a custom shadow camera or anything like that.
My current very ugly solution to this problem in Direct3D9 is to make the shadow bias 70 times higher for spotlights in the shaders (also shown in the video).
There are still some artifacts but it is working pretty much 99% of the time, and the shadows are strangely enough not that far off (since shadow bias makes them "skip" the start of the shadows).
Doing this on the directional light makes shadows be cut off pretty horribly at the start since the bias is too large, so I am wondering why it is working differently there compared to spotlights.
On Direct3D11 the shadow bias for the directional light and the spotlight is exactly the same and it works correctly there without having to make it 70 times higher for spotlights.
Here are the minimal shaders that makes it reproducable:
ShadowCaster:
Code: Select all
uniform float4x4 worldViewProj;
uniform float4 texelOffsets;
struct VS_OUTPUT
{
float2 oDepth : TEXCOORD0;
};
VS_OUTPUT main_vs(float4 position : POSITION,
out float4 oPosition : POSITION)
{
VS_OUTPUT Out;
oPosition = mul(worldViewProj, position);
oPosition.xy += texelOffsets.zw * oPosition.w;
Out.oDepth.x = oPosition.z;
Out.oDepth.y = oPosition.w;
return Out;
}
float4 main_ps(float2 depth : TEXCOORD0) : COLOR0
{
float finalDepth = depth.x / depth.y;
float4 color = float4(finalDepth, 1, 1, 1);
return color;
}
ShadowReceiver:
Code: Select all
float4x4 worldMatrix;
float4x4 texViewProj1;
float4x4 modelViewProj;
struct VS_OUTPUT
{
float3 oVertexPos : TEXCOORD0;
float4 oShadowUV1 : TEXCOORD1;
};
VS_OUTPUT main_vs( float4 position : POSITION,
out float4 oPosition : POSITION )
{
VS_OUTPUT Out;
float3 worldPos = mul(worldMatrix, position).xyz;
oPosition = mul(modelViewProj, position);
Out.oVertexPos = worldPos;
Out.oShadowUV1 = mul(texViewProj1, float4(Out.oVertexPos, 1));
return Out;
}
float computeShadow(sampler targetShadowMap, float4 targetShadowUV)
{
targetShadowUV.xyz = targetShadowUV.xyz / targetShadowUV.w;
float shadowBias = 0.000005;
shadowBias += shadowBias * 70.0; // We need this on Direct3D9 for spotlights for some reason
float shadowTotalOpacity = 0.0;
float shadowSample = tex2D(targetShadowMap, targetShadowUV.xy).x;
if(shadowSample + shadowBias < targetShadowUV.z)
shadowTotalOpacity += 1.0;
return shadowTotalOpacity;
}
float computeSpotlight( float3 LightToVertexNorm, float3 lightdirection, float3 spotlightparams )
{
float cosSpotLightAngle = saturate(dot(LightToVertexNorm, lightdirection));
return pow(saturate(cosSpotLightAngle - spotlightparams.y) / (spotlightparams.x - spotlightparams.y), spotlightparams.z);
}
float4 lightPosition1;
float4 lightSpotParams1;
float4 lightDirection1;
sampler ShadowMap1 : register(s6); // Looks strange, but it is at that spot in the material
float4 main_ps(float3 position : TEXCOORD0,
float4 shadowUV1 : TEXCOORD1) : COLOR0
{
float4 color = float4(1,1,1,1);
float shadowOpacity = 1.0 - computeShadow(ShadowMap1, shadowUV1);
color.xyz *= shadowOpacity;
color.xyz *= computeSpotlight(normalize((position - lightPosition1.xyz)), lightDirection1.xyz, lightSpotParams1.xyz);
return color;
}
And here is the initialization code for the shadows:
Code: Select all
// Set the shadow values
app->m_SceneManager->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE_INTEGRATED);
if(tmpCasterMaterial) // This is the DepthShadowmapCaster shader material
app->m_SceneManager->setShadowTextureCasterMaterial(tmpCasterMaterial);
app->m_SceneManager->setShadowTextureSelfShadow(true);
int tmpNumberOfShadowTextures = 2;
// Create the shadow textures
app->m_SceneManager->setShadowTextureCount(tmpNumberOfShadowTextures);
int tmpShadowTextureSize = 2048;
for (int i = 0; i < tmpNumberOfShadowTextures; i++)
{
if (i > 0)
tmpShadowTextureSize = 512;
app->m_SceneManager->setShadowTextureConfig(i, tmpShadowTextureSize, tmpShadowTextureSize, PF_FLOAT32_R);
}
app->m_SceneManager->setShadowUseInfiniteFarPlane(false);
app->m_SceneManager->setShadowCasterRenderBackFaces(false);
app->m_SceneManager->setShadowFarDistance(12.0f);
How can the result be completely different when both Direct3D9 and Direct3D11 are using the same shaders and same initialization (well, apart from the necessary Direct3D11 texture fetch changes in the shaders)?