Add forward shadow receiving support

This commit is contained in:
2026-04-04 23:01:34 +08:00
parent 353d129613
commit 96a44da2cb
22 changed files with 889 additions and 89 deletions

View File

@@ -1,6 +1,7 @@
// XC_BUILTIN_FORWARD_LIT_OPENGL_PS
#version 430
layout(binding = 1) uniform sampler2D uBaseColorTexture;
layout(binding = 2) uniform sampler2D uShadowMapTexture;
layout(std140, binding = 1) uniform PerObjectConstants {
mat4 gProjectionMatrix;
@@ -15,11 +16,44 @@ layout(std140, binding = 2) uniform MaterialConstants {
vec4 gBaseColorFactor;
};
layout(std140, binding = 3) uniform ShadowReceiverConstants {
mat4 gWorldToShadowMatrix;
vec4 gShadowBiasAndTexelSize;
vec4 gShadowOptions;
};
in vec3 vNormalWS;
in vec2 vTexCoord;
in vec3 vPositionWS;
layout(location = 0) out vec4 fragColor;
float ComputeShadowAttenuation(vec3 positionWS) {
if (gShadowOptions.x < 0.5) {
return 1.0;
}
vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0);
if (shadowClip.w <= 0.0) {
return 1.0;
}
vec3 shadowNdc = shadowClip.xyz / shadowClip.w;
vec2 shadowUv = vec2(
shadowNdc.x * 0.5 + 0.5,
shadowNdc.y * -0.5 + 0.5);
if (shadowUv.x < 0.0 || shadowUv.x > 1.0 ||
shadowUv.y < 0.0 || shadowUv.y > 1.0 ||
shadowNdc.z < 0.0 || shadowNdc.z > 1.0) {
return 1.0;
}
float shadowDepth = texture(uShadowMapTexture, shadowUv).r;
float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0);
return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength);
}
void main() {
vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor;
if (gMainLightColorAndFlags.w < 0.5) {
@@ -30,7 +64,10 @@ void main() {
vec3 normalWS = normalize(vNormalWS);
vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
float shadowAttenuation = diffuse > 0.0
? ComputeShadowAttenuation(vPositionWS)
: 1.0;
vec3 lighting = vec3(0.28) +
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w);
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
fragColor = vec4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -2,6 +2,8 @@
#version 450
layout(set = 3, binding = 0) uniform texture2D uBaseColorTexture;
layout(set = 4, binding = 0) uniform sampler uLinearSampler;
layout(set = 6, binding = 0) uniform texture2D uShadowMapTexture;
layout(set = 7, binding = 0) uniform sampler uShadowMapSampler;
layout(set = 1, binding = 0, std140) uniform PerObjectConstants {
mat4 gProjectionMatrix;
@@ -16,11 +18,44 @@ layout(set = 2, binding = 0, std140) uniform MaterialConstants {
vec4 gBaseColorFactor;
};
layout(set = 5, binding = 0, std140) uniform ShadowReceiverConstants {
mat4 gWorldToShadowMatrix;
vec4 gShadowBiasAndTexelSize;
vec4 gShadowOptions;
};
layout(location = 0) in vec3 vNormalWS;
layout(location = 1) in vec2 vTexCoord;
layout(location = 2) in vec3 vPositionWS;
layout(location = 0) out vec4 fragColor;
float ComputeShadowAttenuation(vec3 positionWS) {
if (gShadowOptions.x < 0.5) {
return 1.0;
}
vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0);
if (shadowClip.w <= 0.0) {
return 1.0;
}
vec3 shadowNdc = shadowClip.xyz / shadowClip.w;
vec2 shadowUv = vec2(
shadowNdc.x * 0.5 + 0.5,
shadowNdc.y * -0.5 + 0.5);
if (shadowUv.x < 0.0 || shadowUv.x > 1.0 ||
shadowUv.y < 0.0 || shadowUv.y > 1.0 ||
shadowNdc.z < 0.0 || shadowNdc.z > 1.0) {
return 1.0;
}
float shadowDepth = texture(sampler2D(uShadowMapTexture, uShadowMapSampler), shadowUv).r;
float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0);
return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength);
}
void main() {
vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor;
if (gMainLightColorAndFlags.w < 0.5) {
@@ -31,7 +66,10 @@ void main() {
vec3 normalWS = normalize(vNormalWS);
vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = max(dot(normalWS, directionToLightWS), 0.0);
float shadowAttenuation = diffuse > 0.0
? ComputeShadowAttenuation(vPositionWS)
: 1.0;
vec3 lighting = vec3(0.28) +
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w);
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
fragColor = vec4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -1,6 +1,8 @@
// XC_BUILTIN_FORWARD_LIT_D3D12_PS
Texture2D gBaseColorTexture : register(t1);
SamplerState gLinearSampler : register(s1);
Texture2D gShadowMapTexture : register(t2);
SamplerState gShadowMapSampler : register(s2);
cbuffer PerObjectConstants : register(b1) {
float4x4 gProjectionMatrix;
@@ -15,12 +17,45 @@ cbuffer MaterialConstants : register(b2) {
float4 gBaseColorFactor;
};
cbuffer ShadowReceiverConstants : register(b3) {
float4x4 gWorldToShadowMatrix;
float4 gShadowBiasAndTexelSize;
float4 gShadowOptions;
};
struct PSInput {
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
float ComputeShadowAttenuation(float3 positionWS) {
if (gShadowOptions.x < 0.5f) {
return 1.0f;
}
float4 shadowClip = mul(gWorldToShadowMatrix, float4(positionWS, 1.0f));
if (shadowClip.w <= 0.0f) {
return 1.0f;
}
float3 shadowNdc = shadowClip.xyz / shadowClip.w;
float2 shadowUv = float2(
shadowNdc.x * 0.5f + 0.5f,
shadowNdc.y * -0.5f + 0.5f);
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
shadowNdc.z < 0.0f || shadowNdc.z > 1.0f) {
return 1.0f;
}
const float shadowDepth = gShadowMapTexture.Sample(gShadowMapSampler, shadowUv).r;
const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
const float shadowStrength = saturate(gShadowBiasAndTexelSize.w);
return receiverDepth <= shadowDepth ? 1.0f : (1.0f - shadowStrength);
}
float4 MainPS(PSInput input) : SV_TARGET {
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
if (gMainLightColorAndFlags.a < 0.5f) {
@@ -30,7 +65,10 @@ float4 MainPS(PSInput input) : SV_TARGET {
float3 normalWS = normalize(input.normalWS);
float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = saturate(dot(normalWS, directionToLightWS));
float shadowAttenuation = diffuse > 0.0f
? ComputeShadowAttenuation(input.positionWS)
: 1.0f;
float3 lighting = float3(0.28f, 0.28f, 0.28f) +
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w);
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
return float4(baseColor.rgb * lighting, baseColor.a);
}

View File

@@ -17,6 +17,9 @@ Shader "Builtin Forward Lit"
MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)]
BaseColorTexture (Texture2D, 3, 0) [Semantic(BaseColorTexture)]
LinearClampSampler (Sampler, 4, 0) [Semantic(LinearClampSampler)]
ShadowReceiverConstants (ConstantBuffer, 5, 0) [Semantic(ShadowReceiver)]
ShadowMapTexture (Texture2D, 6, 0) [Semantic(ShadowMapTexture)]
ShadowMapSampler (Sampler, 7, 0) [Semantic(ShadowMapSampler)]
}
HLSLPROGRAM
#pragma vertex MainVS

View File

@@ -15,6 +15,7 @@ layout(std140, binding = 1) uniform PerObjectConstants {
out vec3 vNormalWS;
out vec2 vTexCoord;
out vec3 vPositionWS;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
@@ -22,4 +23,5 @@ void main() {
gl_Position = gProjectionMatrix * positionVS;
vNormalWS = mat3(gNormalMatrix) * aNormal;
vTexCoord = aTexCoord;
vPositionWS = positionWS.xyz;
}

View File

@@ -15,6 +15,7 @@ layout(set = 1, binding = 0, std140) uniform PerObjectConstants {
layout(location = 0) out vec3 vNormalWS;
layout(location = 1) out vec2 vTexCoord;
layout(location = 2) out vec3 vPositionWS;
void main() {
vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0);
@@ -22,4 +23,5 @@ void main() {
gl_Position = gProjectionMatrix * positionVS;
vNormalWS = mat3(gNormalMatrix) * aNormal;
vTexCoord = aTexCoord;
vPositionWS = positionWS.xyz;
}

View File

@@ -1,7 +1,4 @@
// XC_BUILTIN_FORWARD_LIT_D3D12_VS
Texture2D gBaseColorTexture : register(t1);
SamplerState gLinearSampler : register(s1);
cbuffer PerObjectConstants : register(b1) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
@@ -11,10 +8,6 @@ cbuffer PerObjectConstants : register(b1) {
float4 gMainLightColorAndFlags;
};
cbuffer MaterialConstants : register(b2) {
float4 gBaseColorFactor;
};
struct VSInput {
float3 position : POSITION;
float3 normal : NORMAL;
@@ -25,6 +18,7 @@ struct PSInput {
float4 position : SV_POSITION;
float3 normalWS : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 positionWS : TEXCOORD2;
};
PSInput MainVS(VSInput input) {
@@ -34,5 +28,6 @@ PSInput MainVS(VSInput input) {
output.position = mul(gProjectionMatrix, positionVS);
output.normalWS = mul((float3x3)gNormalMatrix, input.normal);
output.texcoord = input.texcoord;
output.positionWS = positionWS.xyz;
return output;
}