From ceedfbabaf697b39080268855bd2eeff24bff637 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 14 Oct 2023 01:06:10 +0200 Subject: [PATCH] Graphics/Shaders: Move shadow factor to a separate file --- src/Nazara/Graphics/Graphics.cpp | 5 + .../Shaders/Modules/Engine/LightShadow.nzsl | 135 ++++++++++++++++++ .../Shaders/Modules/Math/CookTorrancePBR.nzsl | 24 ++++ .../Resources/Shaders/PhongMaterial.nzsl | 122 ++-------------- 4 files changed, 177 insertions(+), 109 deletions(-) create mode 100644 src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 7f9e54a94..c0a16e323 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -51,6 +51,10 @@ namespace Nz #include }; + const UInt8 r_lightShadowModule[] = { + #include + }; + const UInt8 r_skeletalDataModule[] = { #include }; @@ -422,6 +426,7 @@ namespace Nz RegisterEmbedShaderModule(r_gammaCorrectionPass); RegisterEmbedShaderModule(r_instanceDataModule); RegisterEmbedShaderModule(r_lightDataModule); + RegisterEmbedShaderModule(r_lightShadowModule); RegisterEmbedShaderModule(r_mathColorModule); RegisterEmbedShaderModule(r_mathConstantsModule); RegisterEmbedShaderModule(r_mathCookTorrancePBRModule); diff --git a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl new file mode 100644 index 000000000..c2e189af1 --- /dev/null +++ b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl @@ -0,0 +1,135 @@ +[nzsl_version("1.0")] +module Engine.LightShadow; + +option EnableShadowMapping: bool = true; +option MaxLightCascadeCount: u32 = u32(4); //< FIXME: Fix integral value types + +import DirectionalLight, PointLight, SpotLight from Engine.LightData; + +[export] +fn ComputeDirectionalLightShadow(light: DirectionalLight, shadowmap: depth_sampler2D_array[f32], worldPos: vec3[f32], lambert: f32, viewMatrix: mat4[f32]) -> f32 +{ + let shadowFactor = 1.0; + const if (EnableShadowMapping) + { + if (light.invShadowMapSize.x > 0.0) + { + let fragPosViewSpace = viewMatrix * vec4[f32](worldPos, 1.0); + let depthValue = abs(fragPosViewSpace.z); + + let cascadeIndex = MaxLightCascadeCount; + for index in u32(0) -> light.cascadeCount + { + if (depthValue < light.cascadeDistances[index]) + { + cascadeIndex = index; + break; + } + } + + if (cascadeIndex >= light.cascadeCount) + cascadeIndex = light.cascadeCount - u32(1); + + let lightProjPos = light.viewProjMatrices[cascadeIndex] * vec4[f32](worldPos, 1.0); + let shadowCoords = lightProjPos.xyz / lightProjPos.w; + + // calculate bias (based on depth map resolution and slope) + let bias = max(0.05 * (1.0 - lambert), 0.005); + bias *= 1.0 / (light.cascadeDistances[cascadeIndex] * 0.05); + + shadowFactor = 0.0; + [unroll] + for x in -1 -> 2 + { + [unroll] + for y in -1 -> 2 + { + let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize; + shadowFactor += shadowmap.SampleDepthComp(vec3[f32](coords, f32(cascadeIndex)), shadowCoords.z - bias).r; + } + } + shadowFactor /= 9.0; + } + } + + return shadowFactor; +} + +[export] +fn ComputePointLightShadow(light: PointLight, shadowmap: sampler_cube[f32], dist: f32, lightDir: vec3[f32]) -> f32 +{ + let shadowFactor = 1.0; + const if (EnableShadowMapping) + { + if (light.invShadowMapSize.x > 0.0) + { + shadowFactor = 0.0; + + let sampleDir = vec3[f32](lightDir.x, lightDir.y, -lightDir.z); + + const sampleCount = 4; + const offset = 0.005; + + const invSampleCount = 1.0 / f32(sampleCount); + const start = vec3[f32](offset * 0.5, offset * 0.5, offset * 0.5); + const shadowContribution = 1.0 / f32(sampleCount * sampleCount * sampleCount); + + let bias = 0.05; + + [unroll] + for x in 0 -> sampleCount + { + [unroll] + for y in 0 -> sampleCount + { + [unroll] + for z in 0 -> sampleCount + { + let dirOffset = vec3[f32](f32(x), f32(y), f32(z)) * invSampleCount * offset - start; + let sampleDir = sampleDir + dirOffset; + + let closestDepth = shadowmap.Sample(sampleDir).r; + closestDepth *= light.radius; + + if (closestDepth > dist - bias) + shadowFactor += shadowContribution; + } + } + } + } + } + + return shadowFactor; +} + +[export] +fn ComputeSpotLightShadow(light: SpotLight, shadowmap: depth_sampler2D[f32], worldPos: vec3[f32], lambert: f32) -> f32 +{ + let shadowFactor = 1.0; + const if (EnableShadowMapping) + { + if (light.invShadowMapSize.x > 0.0) + { + let bias = 0.0005 * tan(acos(lambert)); + bias = clamp(bias, 0.0, 0.01); + + let lightProjPos = light.viewProjMatrix * vec4[f32](worldPos, 1.0); + let shadowCoords = lightProjPos.xyz / lightProjPos.w; + + shadowFactor = 0.0; + [unroll] + for x in -1 -> 2 + { + [unroll] + for y in -1 -> 2 + { + let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize; + shadowFactor += shadowmap.SampleDepthComp(coords, shadowCoords.z - bias).r; + } + } + shadowFactor /= 9.0; + } + } + + return shadowFactor; +} \ No newline at end of file diff --git a/src/Nazara/Graphics/Resources/Shaders/Modules/Math/CookTorrancePBR.nzsl b/src/Nazara/Graphics/Resources/Shaders/Modules/Math/CookTorrancePBR.nzsl index a38943ccb..3486567be 100644 --- a/src/Nazara/Graphics/Resources/Shaders/Modules/Math/CookTorrancePBR.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/Modules/Math/CookTorrancePBR.nzsl @@ -48,3 +48,27 @@ fn FresnelSchlick(cosTheta: f32, F0: vec3[f32]) -> vec3[f32] // TODO: Clamp return F0 + (vec3[f32](1.0, 1.0, 1.0) - F0) * pow(min(max(1.0 - cosTheta, 0.0), 1.0), 5.0); } + +[export] +fn ComputeLightRadiance(lightColor: vec3[f32], lightDir: vec3[f32], lightAttenuation: f32, albedoFactor: vec3[f32], eyeVec: vec3[f32], F0: vec3[f32], normal: vec3[f32], metallic: f32, roughness: f32) -> vec3[f32] +{ + let radiance = lightColor * lightAttenuation; + + let halfDir = normalize(lightDir + eyeVec); + + // Cook-Torrance BRDF + let NDF = DistributionGGX(normal, halfDir, roughness); + let G = GeometrySmith(normal, eyeVec, lightDir, roughness); + let F = FresnelSchlick(max(dot(halfDir, eyeVec), 0.0), F0); + + let kS = F; + let diffuse = vec3[f32](1.0, 1.0, 1.0) - kS; + diffuse *= 1.0 - metallic; + + let numerator = NDF * G * F; + let denominator = 4.0 * max(dot(normal, eyeVec), 0.0) * max(dot(normal, lightDir), 0.0); + let specular = numerator / max(denominator, 0.0001); + + let NdotL = max(dot(normal, lightDir), 0.0); + return (diffuse * albedoFactor + specular) * radiance * NdotL; +} diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl index a0bc14ca4..e3864876d 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl @@ -6,6 +6,7 @@ import LightData from Engine.LightData; import SkeletalData from Engine.SkeletalData; import ViewerData from Engine.ViewerData; +import * from Engine.LightShadow; import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear; // Pass-specific options @@ -18,7 +19,6 @@ option HasAlphaTexture: bool = false; option AlphaTest: bool = false; // Phong material options -option EnableShadowMapping: bool = true; option HasEmissiveTexture: bool = false; option HasHeightTexture: bool = false; option HasNormalTexture: bool = false; @@ -40,7 +40,6 @@ option VertexUvLoc: i32 = -1; option VertexJointIndicesLoc: i32 = -1; option VertexJointWeightsLoc: i32 = -1; -option MaxLightCascadeCount: u32 = u32(4); //< FIXME: Fix integral value types option MaxLightCount: u32 = u32(3); //< FIXME: Fix integral value types const HasNormal = (VertexNormalLoc >= 0); @@ -126,6 +125,15 @@ struct FragOut fn LinearizeDepth(depth: f32, zNear: f32, zFar: f32) -> f32 { return zNear * zFar / (zFar + depth * (zNear - zFar)); +} + +// http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/ +fn GetSlopeScaledBias(normal: vec3[f32], lightDir: vec3[f32]) -> f32 +{ + let cosAlpha = clamp(dot(normal, lightDir), 0.0, 1.0); + let sinAlpha = sqrt(1.0 - cosAlpha * cosAlpha); // sin(acos(L*N)) + let tanAlpha = sinAlpha / cosAlpha; // tan(acos(L*N)) + return tanAlpha; } fn ComputeColor(input: VertToFrag) -> vec4[f32] @@ -188,48 +196,7 @@ fn main(input: VertToFrag) -> FragOut let specFactor = max(dot(reflection, eyeVec), 0.0); specFactor = pow(specFactor, settings.Shininess); - let shadowFactor = 1.0; - const if (EnableShadowMapping) - { - if (light.invShadowMapSize.x > 0.0) - { - let fragPosViewSpace = viewerData.viewMatrix * vec4[f32](input.worldPos, 1.0); - let depthValue = abs(fragPosViewSpace.z); - - let cascadeIndex = MaxLightCascadeCount; - for index in u32(0) -> light.cascadeCount - { - if (depthValue < light.cascadeDistances[index]) - { - cascadeIndex = index; - break; - } - } - - if (cascadeIndex >= light.cascadeCount) - cascadeIndex = light.cascadeCount - u32(1); - - let lightProjPos = light.viewProjMatrices[cascadeIndex] * vec4[f32](input.worldPos, 1.0); - let shadowCoords = lightProjPos.xyz / lightProjPos.w; - - // calculate bias (based on depth map resolution and slope) - let bias = max(0.05 * (1.0 - lambert), 0.005); - bias *= 1.0 / (light.cascadeDistances[cascadeIndex] * 0.05); - - shadowFactor = 0.0; - [unroll] - for x in -1 -> 2 - { - [unroll] - for y in -1 -> 2 - { - let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize; - shadowFactor += shadowMapsDirectional[lightIndex].SampleDepthComp(vec3[f32](coords, f32(cascadeIndex)), shadowCoords.z - bias).r; - } - } - shadowFactor /= 9.0; - } - } + let shadowFactor = ComputeDirectionalLightShadow(light, shadowMapsDirectional[lightIndex], input.worldPos, lambert, viewerData.viewMatrix); lightAmbient += shadowFactor * light.color.rgb * light.ambientFactor * settings.AmbientColor.rgb; lightDiffuse += shadowFactor * lambert * light.color.rgb * light.diffuseFactor; @@ -251,46 +218,7 @@ fn main(input: VertToFrag) -> FragOut let specFactor = max(dot(reflection, eyeVec), 0.0); specFactor = pow(specFactor, settings.Shininess); - let shadowFactor = 1.0; - const if (EnableShadowMapping) - { - if (light.invShadowMapSize.x > 0.0) - { - shadowFactor = 0.0; - - let sampleDir = vec3[f32](lightToPosNorm.x, lightToPosNorm.y, -lightToPosNorm.z); - - const sampleCount = 4; - const offset = 0.005; - - const invSampleCount = 1.0 / f32(sampleCount); - const start = vec3[f32](offset * 0.5, offset * 0.5, offset * 0.5); - const shadowContribution = 1.0 / f32(sampleCount * sampleCount * sampleCount); - - let bias = 0.05; - - [unroll] - for x in 0 -> sampleCount - { - [unroll] - for y in 0 -> sampleCount - { - [unroll] - for z in 0 -> sampleCount - { - let dirOffset = vec3[f32](f32(x), f32(y), f32(z)) * invSampleCount * offset - start; - let sampleDir = sampleDir + dirOffset; - - let closestDepth = shadowMapsPoint[lightIndex].Sample(sampleDir).r; - closestDepth *= light.radius; - - if (closestDepth > dist - bias) - shadowFactor += shadowContribution; - } - } - } - } - } + let shadowFactor = ComputePointLightShadow(light, shadowMapsPoint[lightIndex], dist, lightToPosNorm); lightAmbient += attenuationFactor * light.color.rgb * light.ambientFactor * settings.AmbientColor.rgb; lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * light.diffuseFactor; @@ -317,31 +245,7 @@ fn main(input: VertToFrag) -> FragOut let specFactor = max(dot(reflection, eyeVec), 0.0); specFactor = pow(specFactor, settings.Shininess); - let shadowFactor = 1.0; - const if (EnableShadowMapping) - { - if (light.invShadowMapSize.x > 0.0) - { - let bias = 0.0005 * tan(acos(lambert)); - bias = clamp(bias, 0.0, 0.01); - - let lightProjPos = light.viewProjMatrix * vec4[f32](input.worldPos, 1.0); - let shadowCoords = lightProjPos.xyz / lightProjPos.w; - - shadowFactor = 0.0; - [unroll] - for x in -1 -> 2 - { - [unroll] - for y in -1 -> 2 - { - let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize; - shadowFactor += shadowMapsSpot[lightIndex].SampleDepthComp(coords, shadowCoords.z - bias).r; - } - } - shadowFactor /= 9.0; - } - } + let shadowFactor = ComputeSpotLightShadow(light, shadowMapsSpot[lightIndex], input.worldPos, lambert); lightAmbient += attenuationFactor * light.color.rgb * light.ambientFactor * settings.AmbientColor.rgb; lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * light.diffuseFactor;