结果错误的阴影贴图(HLSL,Shader model 3.0)
Shadow mapping with wrong results (HLSL, Shader model 3.0)
(抱歉我的英语不好。)
我是 Stack Overflow 的新手,正在使用 MS Visual C++ 2015 编译器、Direct3D 9 和 HLSL(着色器模型 3.0)编写 3D 游戏应用程序。
我已经使用 4 个渲染目标纹理实现了延迟渲染逻辑。
我将像素的深度值存储在渲染目标纹理中并创建了阴影贴图。这是结果。 (所有mesh都是黑色的,因为mesh尺寸小,离camera近,远平面距离值为1000.0f。)
The depth texture and the shadow map.
我用阴影映射着色器渲染了一个全屏四边形,并输出了红色的阴影以确认着色器工作正常。
但是,着色器似乎输出了错误的结果。阴影贴图纹理输出在网格表面上重复。
https://www.youtube.com/watch?v=1URGgoCR6Zc
这是绘制四边形的阴影贴图顶点着色器。
struct VsInput {
float4 position : POSITION0;
};
struct VsOutput {
float4 position : POSITION0;
float4 cameraViewRay : TEXCOORD0;
};
float4x4 matInverseCameraViewProjection;
float4 cameraWorldPosition;
float farDistance;
VsOutput vs_main(VsInput input) {
VsOutput output = (VsOutput)0;
output.position = input.position;
output.cameraViewRay = mul(float4(input.position.xy, 1.0f, 1.0f) * farDistance, matInverseCameraViewProjection);
output.cameraViewRay /= output.cameraViewRay.w;
output.cameraViewRay.xyz -= cameraWorldPosition.xyz;
return output;
}
下面是绘制四边形的阴影映射像素着色器。
struct PsInput {
float2 screenPosition : VPOS;
float4 viewRay : TEXCOORD0;
};
struct PsOutput {
float4 color : COLOR0;
};
texture depthMap;
texture shadowMap;
sampler depthMapSampler = sampler_state {
Texture = (depthMap);
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
MipFilter = POINT;
};
sampler shadowMapSampler = sampler_state {
Texture = (shadowMap);
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
MipFilter = POINT;
};
//float4x4 matCameraView;
float4x4 matLightView;
float4x4 matLightProjection;
float4 cameraWorldPosition;
float4 lightWorldPosition;
float2 halfPixel;
float epsilon;
float farDistance;
PsOutput ps_main(PsInput input) {
PsOutput output = (PsOutput)0;
output.color.a = 1.0f;
//Reconstruct the world position using the view-space linear depth value.
float2 textureUv = input.screenPosition * halfPixel * 2.0f - halfPixel;
float viewDepth = tex2D(depthMapSampler, textureUv).r;
float3 eye = input.viewRay.xyz * viewDepth;
float4 worldPosition = float4((eye + cameraWorldPosition.xyz), 1.0f);
//Test if the reconstructed world position has right coordinate values.
//output.color = mul(worldPosition, matCameraView).z / farDistance;
float4 positionInLightView = mul(worldPosition, matLightView);
float lightDepth = positionInLightView.z / farDistance;
float4 positionInLightProjection = mul(positionInLightView, matLightProjection);
positionInLightProjection /= positionInLightProjection.w;
//If-statement doesn't work???
float condition = positionInLightProjection.x >= -1.0f;
condition *= positionInLightProjection.x <= 1.0f;
condition *= positionInLightProjection.y >= -1.0f;
condition *= positionInLightProjection.y <= 1.0f;
condition *= positionInLightProjection.z >= 0.0f;
condition *= positionInLightProjection.z <= 1.0f;
condition *= positionInLightProjection.w > 0.0f;
float2 shadowMapUv = float2(
positionInLightProjection.x * 0.5f + 0.5f,
-positionInLightProjection.y * 0.5f + 0.5f
);
//If-statement doesn't work???
float condition2 = shadowMapUv.x >= 0.0f;
condition2 *= shadowMapUv.x <= 1.0f;
condition2 *= shadowMapUv.y >= 0.0f;
condition2 *= shadowMapUv.y <= 1.0f;
float viewDepthInShadowMap = tex2D(
shadowMapSampler,
shadowMapUv
).r;
output.color.r = lightDepth > viewDepthInShadowMap + epsilon;
output.color.r *= condition;
output.color.r *= condition2;
return output;
}
阴影贴图的 uv 值似乎有一些错误,但我无法弄清楚真正的问题是什么。
非常感谢您的帮助。
编辑:我更新了着色器代码。我决定使用 view-space 线性深度并确认世界位置具有正确的值。真不明白shadow map坐标值为什么会取错...
看来您使用的偏见确实有误。 Google 向上 "Shadow Acne",您应该找到问题的答案。阴影贴图的分辨率也可能是个问题。
我找到了解决方案。
第一个问题是渲染目标纹理的纹理格式错误。我应该使用 D3DFMT_R32F。 (我用过D3DFMT_A8R8G8B8。)
我在我的阴影映射像素着色器中添加了这些线。
//Reconstruct the world position using the view-space linear depth value.
float2 textureUv = input.screenPosition * halfPixel * 2.0f - halfPixel;
float4 viewPosition = float4(input.viewRay.xyz * tex2D(depthMapSampler, textureUv).r, 1.0f);
float4 worldPosition = mul(viewPosition, matInverseCameraView);
...
//If-statement doesn't work???
float condition = positionInLightProjection.x >= -1.0f;
condition *= positionInLightProjection.x <= 1.0f;
condition *= positionInLightProjection.y >= -1.0f;
condition *= positionInLightProjection.y <= 1.0f;
condition *= positionInLightProjection.z >= 0.0f;
condition *= positionInLightProjection.z <= 1.0f;
condition *= viewPosition.z < farDistance;
最后一行是关键,解决了我的第二个问题。 'farDistance' 是相机视锥体的远平面距离。我仍在努力理解为什么需要这样做。
您可以使用 saturate
来限制 positionInLightProjection
并将其与不饱和变量进行比较。这样您就可以验证 positionInLightProjection
在 0..1
.
内
if ((saturate(positionInLightProjection.x) == positionInLightProjection.x) && (saturate(positionInLightProjection.y) == positionInLightProjection.y)) {
// we are in the view of light
// todo: compare depth values from shadow map and current scene depth
} else {
// this is shadow for sure!
}
(抱歉我的英语不好。)
我是 Stack Overflow 的新手,正在使用 MS Visual C++ 2015 编译器、Direct3D 9 和 HLSL(着色器模型 3.0)编写 3D 游戏应用程序。
我已经使用 4 个渲染目标纹理实现了延迟渲染逻辑。 我将像素的深度值存储在渲染目标纹理中并创建了阴影贴图。这是结果。 (所有mesh都是黑色的,因为mesh尺寸小,离camera近,远平面距离值为1000.0f。)
The depth texture and the shadow map.
我用阴影映射着色器渲染了一个全屏四边形,并输出了红色的阴影以确认着色器工作正常。 但是,着色器似乎输出了错误的结果。阴影贴图纹理输出在网格表面上重复。
https://www.youtube.com/watch?v=1URGgoCR6Zc
这是绘制四边形的阴影贴图顶点着色器。
struct VsInput {
float4 position : POSITION0;
};
struct VsOutput {
float4 position : POSITION0;
float4 cameraViewRay : TEXCOORD0;
};
float4x4 matInverseCameraViewProjection;
float4 cameraWorldPosition;
float farDistance;
VsOutput vs_main(VsInput input) {
VsOutput output = (VsOutput)0;
output.position = input.position;
output.cameraViewRay = mul(float4(input.position.xy, 1.0f, 1.0f) * farDistance, matInverseCameraViewProjection);
output.cameraViewRay /= output.cameraViewRay.w;
output.cameraViewRay.xyz -= cameraWorldPosition.xyz;
return output;
}
下面是绘制四边形的阴影映射像素着色器。
struct PsInput {
float2 screenPosition : VPOS;
float4 viewRay : TEXCOORD0;
};
struct PsOutput {
float4 color : COLOR0;
};
texture depthMap;
texture shadowMap;
sampler depthMapSampler = sampler_state {
Texture = (depthMap);
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
MipFilter = POINT;
};
sampler shadowMapSampler = sampler_state {
Texture = (shadowMap);
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
MipFilter = POINT;
};
//float4x4 matCameraView;
float4x4 matLightView;
float4x4 matLightProjection;
float4 cameraWorldPosition;
float4 lightWorldPosition;
float2 halfPixel;
float epsilon;
float farDistance;
PsOutput ps_main(PsInput input) {
PsOutput output = (PsOutput)0;
output.color.a = 1.0f;
//Reconstruct the world position using the view-space linear depth value.
float2 textureUv = input.screenPosition * halfPixel * 2.0f - halfPixel;
float viewDepth = tex2D(depthMapSampler, textureUv).r;
float3 eye = input.viewRay.xyz * viewDepth;
float4 worldPosition = float4((eye + cameraWorldPosition.xyz), 1.0f);
//Test if the reconstructed world position has right coordinate values.
//output.color = mul(worldPosition, matCameraView).z / farDistance;
float4 positionInLightView = mul(worldPosition, matLightView);
float lightDepth = positionInLightView.z / farDistance;
float4 positionInLightProjection = mul(positionInLightView, matLightProjection);
positionInLightProjection /= positionInLightProjection.w;
//If-statement doesn't work???
float condition = positionInLightProjection.x >= -1.0f;
condition *= positionInLightProjection.x <= 1.0f;
condition *= positionInLightProjection.y >= -1.0f;
condition *= positionInLightProjection.y <= 1.0f;
condition *= positionInLightProjection.z >= 0.0f;
condition *= positionInLightProjection.z <= 1.0f;
condition *= positionInLightProjection.w > 0.0f;
float2 shadowMapUv = float2(
positionInLightProjection.x * 0.5f + 0.5f,
-positionInLightProjection.y * 0.5f + 0.5f
);
//If-statement doesn't work???
float condition2 = shadowMapUv.x >= 0.0f;
condition2 *= shadowMapUv.x <= 1.0f;
condition2 *= shadowMapUv.y >= 0.0f;
condition2 *= shadowMapUv.y <= 1.0f;
float viewDepthInShadowMap = tex2D(
shadowMapSampler,
shadowMapUv
).r;
output.color.r = lightDepth > viewDepthInShadowMap + epsilon;
output.color.r *= condition;
output.color.r *= condition2;
return output;
}
阴影贴图的 uv 值似乎有一些错误,但我无法弄清楚真正的问题是什么。
非常感谢您的帮助。
编辑:我更新了着色器代码。我决定使用 view-space 线性深度并确认世界位置具有正确的值。真不明白shadow map坐标值为什么会取错...
看来您使用的偏见确实有误。 Google 向上 "Shadow Acne",您应该找到问题的答案。阴影贴图的分辨率也可能是个问题。
我找到了解决方案。
第一个问题是渲染目标纹理的纹理格式错误。我应该使用 D3DFMT_R32F。 (我用过D3DFMT_A8R8G8B8。)
我在我的阴影映射像素着色器中添加了这些线。
//Reconstruct the world position using the view-space linear depth value.
float2 textureUv = input.screenPosition * halfPixel * 2.0f - halfPixel;
float4 viewPosition = float4(input.viewRay.xyz * tex2D(depthMapSampler, textureUv).r, 1.0f);
float4 worldPosition = mul(viewPosition, matInverseCameraView);
...
//If-statement doesn't work???
float condition = positionInLightProjection.x >= -1.0f;
condition *= positionInLightProjection.x <= 1.0f;
condition *= positionInLightProjection.y >= -1.0f;
condition *= positionInLightProjection.y <= 1.0f;
condition *= positionInLightProjection.z >= 0.0f;
condition *= positionInLightProjection.z <= 1.0f;
condition *= viewPosition.z < farDistance;
最后一行是关键,解决了我的第二个问题。 'farDistance' 是相机视锥体的远平面距离。我仍在努力理解为什么需要这样做。
您可以使用 saturate
来限制 positionInLightProjection
并将其与不饱和变量进行比较。这样您就可以验证 positionInLightProjection
在 0..1
.
if ((saturate(positionInLightProjection.x) == positionInLightProjection.x) && (saturate(positionInLightProjection.y) == positionInLightProjection.y)) {
// we are in the view of light
// todo: compare depth values from shadow map and current scene depth
} else {
// this is shadow for sure!
}