Files
XCEngine/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl

171 lines
5.4 KiB
HLSL

// XC_BUILTIN_FORWARD_LIT_D3D12_PS
Texture2D gBaseColorTexture : register(t0);
SamplerState gLinearSampler : register(s0);
Texture2D gShadowMapTexture : register(t1);
SamplerState gShadowMapSampler : register(s1);
cbuffer PerObjectConstants : register(b0) {
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
float4x4 gNormalMatrix;
};
static const int XC_MAX_ADDITIONAL_LIGHTS = 8;
struct AdditionalLightData {
float4 colorAndIntensity;
float4 positionAndRange;
float4 directionAndType;
float4 spotAnglesAndFlags;
};
cbuffer LightingConstants : register(b1) {
float4 gMainLightDirectionAndIntensity;
float4 gMainLightColorAndFlags;
float4 gLightingParams;
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
};
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) {
#ifndef XC_MAIN_LIGHT_SHADOWS
return 1.0f;
#else
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);
#endif
}
float ComputeRangeAttenuation(float distanceSq, float range) {
if (range <= 0.0f) {
return 0.0f;
}
const float clampedRange = max(range, 0.0001f);
const float rangeSq = clampedRange * clampedRange;
if (distanceSq >= rangeSq) {
return 0.0f;
}
const float distance = sqrt(max(distanceSq, 0.0f));
const float normalized = saturate(1.0f - distance / clampedRange);
return normalized * normalized;
}
float ComputeSpotAttenuation(AdditionalLightData light, float3 directionToLightWS) {
const float cosOuter = light.spotAnglesAndFlags.x;
const float cosInner = light.spotAnglesAndFlags.y;
const float3 spotAxisToLightWS = normalize(light.directionAndType.xyz);
const float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
return saturate((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4f));
}
float3 EvaluateAdditionalLight(AdditionalLightData light, float3 normalWS, float3 positionWS) {
const float lightType = light.directionAndType.w;
const float3 lightColor = light.colorAndIntensity.rgb;
const float lightIntensity = light.colorAndIntensity.w;
float3 directionToLightWS = float3(0.0f, 0.0f, 0.0f);
float attenuation = 1.0f;
if (lightType < 0.5f) {
directionToLightWS = normalize(light.directionAndType.xyz);
} else {
const float3 lightVectorWS = light.positionAndRange.xyz - positionWS;
const float distanceSq = dot(lightVectorWS, lightVectorWS);
if (distanceSq <= 1e-6f) {
return 0.0f.xxx;
}
directionToLightWS = lightVectorWS * rsqrt(distanceSq);
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
if (lightType > 1.5f) {
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
if (attenuation <= 0.0f) {
return 0.0f.xxx;
}
}
}
const float diffuse = saturate(dot(normalWS, directionToLightWS));
if (diffuse <= 0.0f) {
return 0.0f.xxx;
}
return lightColor * (diffuse * lightIntensity * attenuation);
}
float4 MainPS(PSInput input) : SV_TARGET {
float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor;
const int additionalLightCount = min((int)gLightingParams.x, XC_MAX_ADDITIONAL_LIGHTS);
if (gMainLightColorAndFlags.a < 0.5f && additionalLightCount == 0) {
return baseColor;
}
float3 normalWS = normalize(input.normalWS);
float3 lighting = gLightingParams.yyy;
if (gMainLightColorAndFlags.a >= 0.5f) {
float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz);
float diffuse = saturate(dot(normalWS, directionToLightWS));
float shadowAttenuation = diffuse > 0.0f
? ComputeShadowAttenuation(input.positionWS)
: 1.0f;
lighting +=
gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation);
}
[unroll]
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
if (lightIndex >= additionalLightCount) {
break;
}
lighting += EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, input.positionWS);
}
return float4(baseColor.rgb * lighting, baseColor.a);
}