From 42710ec6f07121c7fbefeabdb70db628e1cb574f Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 22 Mar 2025 10:00:45 -0500 Subject: [PATCH] specular review cleanup of various calcs --- .../game/core/rendering/shaders/brdf.hlsl | 87 +++++++---- .../game/core/rendering/shaders/gl/brdf.glsl | 92 +++++++---- .../core/rendering/shaders/gl/lighting.glsl | 139 ++++++++++------- .../game/core/rendering/shaders/lighting.hlsl | 143 ++++++++++-------- .../advanced/gl/reflectionProbeArrayP.glsl | 29 ++-- .../advanced/reflectionProbeArrayP.hlsl | 44 +++--- 6 files changed, 317 insertions(+), 217 deletions(-) diff --git a/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl b/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl index e36762e6e..b1c8ce78a 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl @@ -36,41 +36,76 @@ // Charles de Rousiers - Electronic Arts Frostbite // SIGGRAPH 2014 -float3 F_Schlick(float3 f0, float f90, float u) +float3 F_Schlick(float3 F0, float cosTheta) { - return f0 + (f90 - f0) * pow(1.f - u, 5.f); + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } float3 FresnelSchlickRoughness(float cosTheta, float3 F0, float roughness) { - float3 ret = float3(0.0, 0.0, 0.0); - float powTheta = pow(1.0 - cosTheta, 5.0); - float invRough = float(1.0 - roughness); + // Compute the Fresnel-Schlick term for the given cosine of the angle + float powTheta = pow(1.0 - cosTheta, 5.0); + + // Adjust the reflectance based on roughness: The roughness scales the contrast of the Fresnel term. + float3 fresnel = F0 + (float3(1.0, 1.0, 1.0) - F0) * powTheta; + + // Modulate the fresnel term by roughness, reducing the effect at higher roughness + fresnel *= (1.0 - roughness); // Adjust based on roughness - ret.x = F0.x + (max(invRough, F0.x) - F0.x) * powTheta; - ret.y = F0.y + (max(invRough, F0.y) - F0.y) * powTheta; - ret.z = F0.z + (max(invRough, F0.z) - F0.z) * powTheta; - - return ret; + return fresnel; } -float V_SmithGGXCorrelated(float NdotL, float NdotV, float alphaRoughnessSq) -{ - float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); - float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); - - float GGX = GGXV + GGXL; - if (GGX > 0.0f) - { - return 0.5f / GGX; - } - return 0.f; +float GeometrySchlickGGX(float NdotV, float roughness) { + float r = (roughness + 1.0); + float k = (r * r) / 8.0; + return NdotV / (NdotV * (1.0 - k) + k); } -float D_GGX(float NdotH, float alphaRoughnessSq) +float V_SmithGGXCorrelated(float NdotL, float NdotV, float roughness) { + float r = roughness * roughness; + float ggx1 = NdotV / (NdotV * (1.0 - r) + r); + float ggx2 = NdotL / (NdotL * (1.0 - r) + r); + return ggx1 * ggx2; +} + +float D_GGX(float NdotH, float roughnessSq) { - float f = (NdotH * alphaRoughnessSq - NdotH) * NdotH + 1; - return alphaRoughnessSq / (M_PI_F * f * f); + float f = (NdotH * NdotH * (roughnessSq - 1.0) + 1.0); + return roughnessSq / (M_PI_F * f * f); +} + +// The Cook-Torrance BRDF for specular reflection +float3 CookTorrance(float3 normal, float3 viewDir, float roughness, float3 F) +{ + float3 H = normalize(viewDir + reflect(-viewDir, normal)); + float NdotH = max(dot(normal, H), 0.0001); + float VdotH = max(dot(viewDir, H), 0.0001); + float NdotV = clamp( dot(normal, viewDir), 0.0009765625f,0.9990234375f); + float NdotL = NdotH; // Approximate light angle + + // Normal Distribution Function (GGX) + float D = D_GGX(NdotH, roughness); + + // Geometry Term (Smith GGX) + float G = V_SmithGGXCorrelated(NdotL, NdotV, roughness); + + // Final BRDF (Rebalanced Energy) + return (F * D * G) / max(4.0 * NdotV * NdotL, 0.0001); +} + + +float3 OrenNayarDiffuse(float3 albedo, float3 N, float3 V, float3 L, float roughnessSq) { + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + + float alpha2 = roughnessSq * roughnessSq; + float A = 1.0 + (alpha2 / (alpha2 + 0.33)); + float B = 0.45 * (alpha2 / (alpha2 + 0.09)); + + float alpha = max(NdotL, NdotV); + float beta = min(NdotL, NdotV); + + return albedo * (A + B * max(0.0, dot(V - N * NdotV, L - N * NdotL)) * alpha * beta) * M_1OVER_PI_F; } float3 Fr_DisneyDiffuse(float3 F0, float NdotV, float NdotL, float LdotH, float linearRoughness) @@ -78,8 +113,8 @@ float3 Fr_DisneyDiffuse(float3 F0, float NdotV, float NdotL, float LdotH, float float energyBias = lerp (0 , 0.5 , linearRoughness ); float energyFactor = lerp (1.0 , 1.0 / 1.51 , linearRoughness ); float fd90 = energyBias + 2.0 * LdotH * LdotH * linearRoughness ; - float3 lightScatter = F_Schlick( F0 , fd90 , NdotL ); - float3 viewScatter = F_Schlick(F0 , fd90 , NdotV ); + float3 lightScatter = F_Schlick( F0 , fd90 ); + float3 viewScatter = F_Schlick(F0 , fd90); return lightScatter * viewScatter * energyFactor ; } diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl index 9d76de406..ad1166284 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl @@ -30,52 +30,86 @@ // Charles de Rousiers - Electronic Arts Frostbite // SIGGRAPH 2014 -vec3 F_Schlick(vec3 f0, float f90, float u) +vec3 F_Schlick(vec3 F0, float cosTheta) { - return f0 + (f90 - f0) * pow(1.f - u, 5.f); + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } vec3 FresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) { - vec3 ret = vec3(0.0, 0.0, 0.0); - float powTheta = pow(1.0 - cosTheta, 5.0); - float invRough = float(1.0 - roughness); + // Compute the Fresnel-Schlick term for the given cosine of the angle + float powTheta = pow(1.0 - cosTheta, 5.0); + + // Adjust the reflectance based on roughness: The roughness scales the contrast of the Fresnel term. + vec3 fresnel = F0 + (vec3(1.0, 1.0, 1.0) - F0) * powTheta; + + // Modulate the fresnel term by roughness, reducing the effect at higher roughness + fresnel *= (1.0 - roughness); // Adjust based on roughness - ret.x = F0.x + (max(invRough, F0.x) - F0.x) * powTheta; - ret.y = F0.y + (max(invRough, F0.y) - F0.y) * powTheta; - ret.z = F0.z + (max(invRough, F0.z) - F0.z) * powTheta; - - return ret; + return fresnel; } -float V_SmithGGXCorrelated(float NdotL, float NdotV, float alphaRoughnessSq) -{ - float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); - float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); - - float GGX = GGXV + GGXL; - if (GGX > 0.0f) - { - return 0.5f / GGX; - } - return 0.f; +float GeometrySchlickGGX(float NdotV, float roughness) { + float r = (roughness + 1.0); + float k = (r * r) / 8.0; + return NdotV / (NdotV * (1.0 - k) + k); } -float D_GGX(float NdotH, float alphaRoughnessSq) +float V_SmithGGXCorrelated(float NdotL, float NdotV, float roughness) { + float r = roughness * roughness; + float ggx1 = NdotV / (NdotV * (1.0 - r) + r); + float ggx2 = NdotL / (NdotL * (1.0 - r) + r); + return ggx1 * ggx2; +} + +float D_GGX(float NdotH, float roughnessSq) { - float f = (NdotH * alphaRoughnessSq - NdotH) * NdotH + 1; - return alphaRoughnessSq / (M_PI_F * f * f); + float f = (NdotH * NdotH * (roughnessSq - 1.0) + 1.0); + return roughnessSq / (M_PI_F * f * f); +} + +// The Cook-Torrance BRDF for specular reflection +vec3 CookTorrance(vec3 normal, vec3 viewDir, float roughness, vec3 F) +{ + vec3 H = normalize(viewDir + reflect(-viewDir, normal)); + float NdotH = max(dot(normal, H), 0.0001); + float VdotH = max(dot(viewDir, H), 0.0001); + float NdotV = clamp( dot(normal, viewDir), 0.0009765625f,0.9990234375f); + float NdotL = NdotH; // Approximate light angle + + // Normal Distribution Function (GGX) + float D = D_GGX(NdotH, roughness); + + // Geometry Term (Smith GGX) + float G = V_SmithGGXCorrelated(NdotL, NdotV, roughness); + + // Final BRDF (Rebalanced Energy) + return (F * D * G) / max(4.0 * NdotV * NdotL, 0.0001); +} + + +vec3 OrenNayarDiffuse(vec3 albedo, vec3 N, vec3 V, vec3 L, float roughnessSq) { + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + + float alpha2 = roughnessSq * roughnessSq; + float A = 1.0 + (alpha2 / (alpha2 + 0.33)); + float B = 0.45 * (alpha2 / (alpha2 + 0.09)); + + float alpha = max(NdotL, NdotV); + float beta = min(NdotL, NdotV); + + return albedo * (A + B * max(0.0, dot(V - N * NdotV, L - N * NdotL)) * alpha * beta) * M_1OVER_PI_F; } vec3 Fr_DisneyDiffuse(vec3 F0, float NdotV, float NdotL, float LdotH, float linearRoughness) { - float energyBias = lerp(0 , 0.5 , linearRoughness ); - float energyFactor = lerp(1.0 , 1.0 / 1.51 , linearRoughness ); + float energyBias = lerp (0 , 0.5 , linearRoughness ); + float energyFactor = lerp (1.0 , 1.0 / 1.51 , linearRoughness ); float fd90 = energyBias + 2.0 * LdotH * LdotH * linearRoughness ; - vec3 lightScatter = F_Schlick( F0 , fd90 , NdotL ); - vec3 viewScatter = F_Schlick(F0 , fd90 , NdotV ); + vec3 lightScatter = F_Schlick( F0 , fd90 ); + vec3 viewScatter = F_Schlick(F0 , fd90); return lightScatter * viewScatter * energyFactor ; } - #endif diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl index 8de548cf1..2d24c8003 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl @@ -114,12 +114,12 @@ void updateSurface(inout Surface surface) surface.linearRoughness = surface.roughness * surface.roughness; surface.linearRoughnessSq = surface.linearRoughness * surface.linearRoughness; - surface.albedo = surface.baseColor.rgb * (1.0f - surface.metalness); + surface.albedo = max(toLinear(surface.baseColor.rgb),0.04f); surface.f0 = mix(vec3(0.04f,0.04f,0.04f), surface.baseColor.rgb, surface.metalness); surface.R = -reflect(surface.V, surface.N); surface.f90 = saturate(50.0 * dot(surface.f0, vec3(0.33,0.33,0.33))); - surface.F = F_Schlick(surface.f0, surface.f90, surface.NdotV); + surface.F = F_Schlick(surface.f0, surface.NdotV); } Surface createSurface(vec4 gbuffer0, sampler2D gbufferTex1, sampler2D gbufferTex2, in vec2 uv, in vec3 wsEyePos, in vec3 wsEyeRay, in mat4 invView) @@ -186,7 +186,7 @@ SurfaceToLight createSurfaceToLight(in Surface surface, in vec3 L) vec3 BRDF_GetDebugSpecular(in Surface surface, in SurfaceToLight surfaceToLight) { //GGX specular - vec3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); + vec3 F = F_Schlick(surface.f0, surface.NdotV); float Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq); float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq); vec3 Fr = D * F * Vis * M_1OVER_PI_F; @@ -225,19 +225,31 @@ float getDistanceAtt( vec3 unormalizedLightVector , float invSqrAttRadius ) vec3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) { - //diffuse term - vec3 Fd = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; + // Compute Fresnel term + vec3 F = F_Schlick(surface.f0, surfaceToLight.HdotV); - //GGX specular - vec3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); - float Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq); - float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq); - vec3 Fr = D * F * Vis; + // GGX Normal Distribution Function + float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughness); + + // Smith GGX Geometry Function + float G = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughness); + + // Specular BRDF + vec3 numerator = D * G * F; + float denominator = 4.0 * max(surface.NdotV, 0.0) * max(surfaceToLight.NdotL, 0.0) + 0.0001; + vec3 specularBRDF = numerator / denominator; + + vec3 diffuseBRDF = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; + + // Final output combining all terms + vec3 kS = F; // Specular reflectance + vec3 kD = (1.0 - kS) * (1.0 - surface.metalness); // Diffuse reflectance + vec3 returnBRDF = kD * (diffuseBRDF) + specularBRDF; if(isCapturing == 1) - return mix(Fd + Fr, surface.baseColor.rgb, surface.metalness); + return lerp(returnBRDF ,surface.albedo.rgb,surface.metalness); else - return Fd + Fr; + return returnBRDF; } vec3 getDirectionalLight(Surface surface, SurfaceToLight surfaceToLight, vec3 lightColor, float lightIntensity, float shadow) @@ -246,8 +258,13 @@ vec3 getDirectionalLight(Surface surface, SurfaceToLight surfaceToLight, vec3 li if(isCapturing != 1) lightfloor = 0.0; - vec3 factor = lightColor * max(surfaceToLight.NdotL * shadow * lightIntensity, lightfloor); - return evaluateStandardBRDF(surface,surfaceToLight) * factor; + // Calculate both specular and diffuse lighting in one BRDF evaluation + vec3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); + + // Direct Diffuse Light Contribution (using Lambertian diffuse in BRDF) + vec3 diffuseLight = directLighting * (lightColor * max(surfaceToLight.NdotL * shadow * lightIntensity, lightfloor)); + + return diffuseLight; } vec3 getPunctualLight(Surface surface, SurfaceToLight surfaceToLight, vec3 lightColor, float lightIntensity, float radius, float shadow) @@ -257,8 +274,14 @@ vec3 getPunctualLight(Surface surface, SurfaceToLight surfaceToLight, vec3 light lightfloor = 0.0; float attenuation = getDistanceAtt(surfaceToLight.Lu, radius); - vec3 factor = lightColor * max(surfaceToLight.NdotL * shadow * lightIntensity * attenuation, lightfloor); - return evaluateStandardBRDF(surface,surfaceToLight) * factor; + + // Calculate both specular and diffuse lighting in one BRDF evaluation + vec3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); + + // Direct Diffuse Light Contribution (using Lambertian diffuse in BRDF) + vec3 diffuseLight = directLighting * lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, lightfloor); + + return diffuseLight; } vec3 getSpotlight(Surface surface, SurfaceToLight surfaceToLight, vec3 lightColor, float lightIntensity, float radius, vec3 lightDir, vec2 lightSpotParams, float shadow) @@ -270,18 +293,32 @@ vec3 getSpotlight(Surface surface, SurfaceToLight surfaceToLight, vec3 lightColo float attenuation = 1.0f; attenuation *= getDistanceAtt(surfaceToLight.Lu, radius); attenuation *= getSpotAngleAtt(-surfaceToLight.L, lightDir, lightSpotParams.xy); - vec3 factor = lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, lightfloor); - return evaluateStandardBRDF(surface,surfaceToLight) * factor; + + // Calculate both specular and diffuse lighting in one BRDF evaluation + vec3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); + + // Direct Diffuse Light Contribution (using Lambertian diffuse in BRDF) + vec3 diffuseLight = directLighting * lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, lightfloor); + + return diffuseLight; } float computeSpecOcclusion( float NdotV , float AO , float roughness ) { - return saturate (pow( abs(NdotV + AO) , exp2 ( -16.0f * roughness - 1.0f )) - 1.0f + AO ); + // Compute the geometry term using Smith's GGX for occlusion + float r = roughness * roughness; // Roughness squared + float ggx = (NdotV) / (NdotV * (1.0 - r) + r); // Smith GGX Geometry Function for occlusion + + // Optionally modify by AO (ambient occlusion) and roughness + float specOcclusion = pow(ggx + AO, 2.0); + + // Return the final occlusion factor (clamped between 0 and 1) + return saturate(specOcclusion); } float roughnessToMipLevel(float roughness, float numMips) { - return pow(abs(roughness),0.25) * numMips; + return saturate((roughness * numMips) - (pow(roughness, 6.0) * (numMips * 0.125))); } vec4 compute4Lights( Surface surface, @@ -541,33 +578,24 @@ vec4 computeForwardProbes(Surface surface, if(skylightCubemapIdx != -1 && alpha >= 0.001) { - irradiance = mix(irradiance,textureLod(irradianceCubemapAR, vec4(surface.R, skylightCubemapIdx), 0).xyz, alpha); + irradiance = mix(irradiance,textureLod(irradianceCubemapAR, vec4(surface.N, skylightCubemapIdx), 0).xyz, alpha); specular = mix(specular,textureLod(specularCubemapAR, vec4(surface.R, skylightCubemapIdx), lod).xyz, alpha); } - - //energy conservation - vec3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - vec3 kD = 1.0f - F; - kD *= 1.0f - surface.metalness; - //float dfgNdotV = max( surface.NdotV , 0.0009765625f ); //0.5f/512.0f (512 is size of dfg/brdf lookup tex) vec2 envBRDF = textureLod(BRDFTexture, vec2(surface.NdotV, surface.roughness),0).rg; - specular *= F * envBRDF.x + surface.f90 * envBRDF.y; - irradiance *= kD * surface.baseColor.rgb; + vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness); - //AO - irradiance *= surface.ao; - specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); - - //http://marmosetco.tumblr.com/post/81245981087 - float horizonOcclusion = 1.3; - float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); - horizon *= horizon; - + vec3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y); + specularCol *= surface.metalness + (1.0 - surface.roughness); + // Final color output after environment lighting + vec3 finalColor = diffuse + specularCol; + finalColor *= surface.ao; if(isCapturing == 1) - return vec4(mix((irradiance + specular* horizon),surface.baseColor.rgb,surface.metalness),0); + return vec4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0); else - return vec4((irradiance + specular* horizon) , 0);//alpha writes disabled + { + return vec4(finalColor, 0); + } } vec4 debugVizForwardProbes(Surface surface, @@ -693,7 +721,7 @@ vec4 debugVizForwardProbes(Surface surface, if(skylightCubemapIdx != -1 && alpha >= 0.001) { - irradiance = mix(irradiance,textureLod(irradianceCubemapAR, vec4(surface.R, skylightCubemapIdx), 0).xyz,alpha); + irradiance = mix(irradiance,textureLod(irradianceCubemapAR, vec4(surface.N, skylightCubemapIdx), 0).xyz,alpha); specular = mix(specular,textureLod(specularCubemapAR, vec4(surface.R, skylightCubemapIdx), lod).xyz,alpha); } @@ -707,23 +735,18 @@ vec4 debugVizForwardProbes(Surface surface, return vec4(irradiance, 0); } - //energy conservation - vec3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - vec3 kD = 1.0f - F; - kD *= 1.0f - surface.metalness; - vec2 envBRDF = textureLod(BRDFTexture, vec2(surface.NdotV, surface.roughness),0).rg; - specular *= F * envBRDF.x + surface.f90 * envBRDF.y; - irradiance *= kD * surface.baseColor.rgb; + vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness); - //AO - irradiance *= surface.ao; - specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); - - //http://marmosetco.tumblr.com/post/81245981087 - float horizonOcclusion = 1.3; - float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); - horizon *= horizon; - - return vec4((irradiance + specular* horizon) , 0);//alpha writes disabled + vec3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y); + specularCol *= surface.metalness + (1.0 - surface.roughness); + // Final color output after environment lighting + vec3 finalColor = diffuse + specularCol; + finalColor *= surface.ao; + if(isCapturing == 1) + return vec4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0); + else + { + return vec4(finalColor, 0); + } } diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index d65b44d9d..4afc3cabd 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -113,12 +113,12 @@ struct Surface linearRoughness = roughness * roughness; linearRoughnessSq = linearRoughness * linearRoughness; - albedo = baseColor.rgb * (1.0f - metalness); - f0 = lerp(0.04f, baseColor.rgb, metalness); + albedo = max(toLinear(baseColor.rgb),0.04f); + f0 = lerp(0.04f, albedo.rgb, metalness); R = -reflect(V, N); f90 = saturate(50.0f * dot(f0, 0.33f)); - F = F_Schlick(f0, f90, NdotV); + F = F_Schlick(f0, NdotV); } }; @@ -187,7 +187,7 @@ inline SurfaceToLight createSurfaceToLight(in Surface surface, in float3 L) float3 BRDF_GetDebugSpecular(in Surface surface, in SurfaceToLight surfaceToLight) { //GGX specular - float3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); + float3 F = F_Schlick(surface.f0, surface.NdotV); float Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq); float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq); float3 Fr = D * F * Vis * M_1OVER_PI_F; @@ -226,19 +226,31 @@ float getDistanceAtt( float3 unormalizedLightVector , float invSqrAttRadius ) float3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) { - //diffuse term - float3 Fd = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; + // Compute Fresnel term + float3 F = F_Schlick(surface.f0, surfaceToLight.HdotV); - //GGX specular - float3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); - float Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq); - float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq); - float3 Fr = D * F * Vis; + // GGX Normal Distribution Function + float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughness); + + // Smith GGX Geometry Function + float G = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughness); + + // Specular BRDF + float3 numerator = D * G * F; + float denominator = 4.0 * max(surface.NdotV, 0.0) * max(surfaceToLight.NdotL, 0.0) + 0.0001; + float3 specularBRDF = numerator / denominator; + + float3 diffuseBRDF = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; + + // Final output combining all terms + float3 kS = F; // Specular reflectance + float3 kD = (1.0 - kS) * (1.0 - surface.metalness); // Diffuse reflectance + float3 returnBRDF = kD * (diffuseBRDF) + specularBRDF; if(isCapturing == 1) - return lerp(Fd + Fr,surface.baseColor.rgb,surface.metalness); + return lerp(returnBRDF ,surface.albedo.rgb,surface.metalness); else - return Fd + Fr; + return returnBRDF; } float3 getDirectionalLight(Surface surface, SurfaceToLight surfaceToLight, float3 lightColor, float lightIntensity, float shadow) @@ -247,8 +259,13 @@ float3 getDirectionalLight(Surface surface, SurfaceToLight surfaceToLight, float if(isCapturing != 1) lightfloor = 0.0; - float3 factor = lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity, lightfloor) ; - return evaluateStandardBRDF(surface,surfaceToLight) * factor; + // Calculate both specular and diffuse lighting in one BRDF evaluation + float3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); + + // Direct Diffuse Light Contribution (using Lambertian diffuse in BRDF) + float3 diffuseLight = directLighting * (lightColor * max(surfaceToLight.NdotL * shadow * lightIntensity, lightfloor)); + + return diffuseLight; } float3 getPunctualLight(Surface surface, SurfaceToLight surfaceToLight, float3 lightColor, float lightIntensity, float radius, float shadow) @@ -258,8 +275,14 @@ float3 getPunctualLight(Surface surface, SurfaceToLight surfaceToLight, float3 l lightfloor = 0.0; float attenuation = getDistanceAtt(surfaceToLight.Lu, radius); - float3 factor = lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, lightfloor) ; - return evaluateStandardBRDF(surface,surfaceToLight) * factor; + + // Calculate both specular and diffuse lighting in one BRDF evaluation + float3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); + + // Direct Diffuse Light Contribution (using Lambertian diffuse in BRDF) + float3 diffuseLight = directLighting * lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, lightfloor); + + return diffuseLight; } float3 getSpotlight(Surface surface, SurfaceToLight surfaceToLight, float3 lightColor, float lightIntensity, float radius, float3 lightDir, float2 lightSpotParams, float shadow) @@ -271,18 +294,32 @@ float3 getSpotlight(Surface surface, SurfaceToLight surfaceToLight, float3 light float attenuation = 1.0f; attenuation *= getDistanceAtt(surfaceToLight.Lu, radius); attenuation *= getSpotAngleAtt(-surfaceToLight.L, lightDir, lightSpotParams.xy); - float3 factor = lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, lightfloor) ; - return evaluateStandardBRDF(surface,surfaceToLight) * factor; + + // Calculate both specular and diffuse lighting in one BRDF evaluation + float3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); + + // Direct Diffuse Light Contribution (using Lambertian diffuse in BRDF) + float3 diffuseLight = directLighting * lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, lightfloor); + + return diffuseLight; } float computeSpecOcclusion( float NdotV , float AO , float roughness ) { - return saturate (pow( abs(NdotV + AO) , exp2 ( -16.0f * roughness - 1.0f )) - 1.0f + AO ); + // Compute the geometry term using Smith's GGX for occlusion + float r = roughness * roughness; // Roughness squared + float ggx = (NdotV) / (NdotV * (1.0 - r) + r); // Smith GGX Geometry Function for occlusion + + // Optionally modify by AO (ambient occlusion) and roughness + float specOcclusion = pow(ggx + AO, 2.0); + + // Return the final occlusion factor (clamped between 0 and 1) + return saturate(specOcclusion); } float roughnessToMipLevel(float roughness, float numMips) { - return pow(abs(roughness),0.25) * numMips; + return saturate((roughness * numMips) - (pow(roughness, 6.0) * (numMips * 0.125))); } float4 compute4Lights( Surface surface, @@ -547,33 +584,24 @@ float4 computeForwardProbes(Surface surface, if(skylightCubemapIdx != -1 && alpha >= 0.001) { - irradiance = lerp(irradiance,TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, surface.R, skylightCubemapIdx, 0).xyz,alpha); + irradiance = lerp(irradiance,TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, surface.N, skylightCubemapIdx, 0).xyz,alpha); specular = lerp(specular,TORQUE_TEXCUBEARRAYLOD(specularCubemapAR, surface.R, skylightCubemapIdx, lod).xyz,alpha); } - //energy conservation - float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - float3 kD = 1.0f - F; - kD *= 1.0f - surface.metalness; + float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(surface.NdotV, surface.roughness)).rg; + float3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness); - //float dfgNdotV = max( surface.NdotV , 0.0009765625f ); //0.5f/512.0f (512 is size of dfg/brdf lookup tex) - float2 envBRDF = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.NdotV, surface.roughness,0,0)).rg; - specular *= F * envBRDF.x + surface.f90 * envBRDF.y; - irradiance *= kD * surface.baseColor.rgb; - - //AO - irradiance *= surface.ao; - specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); - - //http://marmosetco.tumblr.com/post/81245981087 - float horizonOcclusion = 1.3; - float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); - horizon *= horizon; - + float3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y); + specularCol *= surface.metalness + (1.0 - surface.roughness); + // Final color output after environment lighting + float3 finalColor = diffuse + specularCol; + finalColor *= surface.ao; if(isCapturing == 1) - return float4(lerp((irradiance + specular* horizon),surface.baseColor.rgb,surface.metalness),0); + return float4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0); else - return float4((irradiance + specular* horizon) , 0);//alpha writes disabled + { + return float4(finalColor, 0); + } } float4 debugVizForwardProbes(Surface surface, @@ -713,23 +741,18 @@ float4 debugVizForwardProbes(Surface surface, return float4(irradiance, 0); } - //energy conservation - float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - float3 kD = 1.0f - F; - kD *= 1.0f - surface.metalness; + float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(surface.NdotV, surface.roughness)).rg; + float3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness); - float2 envBRDF = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.NdotV, surface.roughness,0,0)).rg; - specular *= F * envBRDF.x + surface.f90 * envBRDF.y; - irradiance *= kD * surface.baseColor.rgb; - - //AO - irradiance *= surface.ao; - specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); - - //http://marmosetco.tumblr.com/post/81245981087 - float horizonOcclusion = 1.3; - float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); - horizon *= horizon; - - return float4((irradiance + specular* horizon) , 0);//alpha writes disabled + float3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y); + specularCol *= surface.metalness + (1.0 - surface.roughness); + // Final color output after environment lighting + float3 finalColor = diffuse + specularCol; + finalColor *= surface.ao; + if(isCapturing == 1) + return float4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0); + else + { + return float4(finalColor, 0); + } } \ No newline at end of file diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl index ae93738df..725202b26 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl @@ -204,27 +204,18 @@ void main() return; #endif - - //energy conservation - vec3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - vec3 kD = 1.0f - F; - kD *= 1.0f - surface.metalness; - vec2 envBRDF = textureLod(BRDFTexture, vec2(surface.NdotV, surface.roughness),0).rg; - specular *= F * envBRDF.x + surface.f90 * envBRDF.y; - irradiance *= kD * surface.baseColor.rgb; + vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness); - //AO - irradiance *= surface.ao; - specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); - - //http://marmosetco.tumblr.com/post/81245981087 - float horizonOcclusion = 1.3; - float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); - horizon *= horizon; - + vec3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y); + specularCol *= surface.metalness + (1.0 - surface.roughness); + // Final color output after environment lighting + vec3 finalColor = diffuse + specularCol; + finalColor *= surface.ao; if(isCapturing == 1) - OUT_col = vec4(mix((irradiance + specular* horizon),surface.baseColor.rgb, surface.metalness),0); + OUT_col = vec4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0); else - OUT_col = vec4((irradiance + specular* horizon)*ambientColor, 0);//alpha writes disabled + { + OUT_col = vec4(finalColor*ambientColor, 0); + } } diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index 9202f9911..40ebbac4b 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -159,13 +159,14 @@ float4 main(PFXVertToPix IN) : SV_TARGET dampen(surface, TORQUE_SAMPLER2D_MAKEARG(WetnessTexture), accumTime, wetAmmout*dampness); // Radiance (Specular) -#if DEBUGVIZ_SPECCUBEMAP == 0 - float lod = roughnessToMipLevel(surface.roughness, cubeMips); -#elif DEBUGVIZ_SPECCUBEMAP == 1 float lod = 0; -#endif +#if DEBUGVIZ_SPECCUBEMAP == 0 + lod = roughnessToMipLevel(surface.roughness, cubeMips); +#elif DEBUGVIZ_SPECCUBEMAP == 1 + lod = 0; +#endif -#if SKYLIGHT_ONLY == 0 +#if SKYLIGHT_ONLY == 0 for (i = 0; i < numProbes; i++) { float contrib = contribution[i]; @@ -181,7 +182,7 @@ float4 main(PFXVertToPix IN) : SV_TARGET #endif if(skylightCubemapIdx != -1 && alpha >= 0.001) { - irradiance = lerp(irradiance,TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, surface.R, skylightCubemapIdx, 0).xyz,alpha); + irradiance = lerp(irradiance,TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, surface.N, skylightCubemapIdx, 0).xyz,alpha); specular = lerp(specular,TORQUE_TEXCUBEARRAYLOD(specularCubemapAR, surface.R, skylightCubemapIdx, lod).xyz,alpha); } @@ -189,27 +190,20 @@ float4 main(PFXVertToPix IN) : SV_TARGET return float4(specular, 1); #elif DEBUGVIZ_DIFFCUBEMAP == 1 return float4(irradiance, 1); -#endif - //energy conservation - float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - float3 kD = 1.0f - F; - kD *= 1.0f - surface.metalness; +#endif - float2 envBRDF = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.NdotV, surface.roughness,0,0)).rg; - specular *= F * envBRDF.x + surface.f90 * envBRDF.y; - irradiance *= kD * surface.baseColor.rgb; + float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(surface.NdotV, surface.roughness)).rg; + float3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness); - //AO - irradiance *= surface.ao; - specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); - - //http://marmosetco.tumblr.com/post/81245981087 - float horizonOcclusion = 1.3; - float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); - horizon *= horizon; - + float3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y); + specularCol *= surface.metalness + (1.0 - surface.roughness); + // Final color output after environment lighting + float3 finalColor = diffuse + specularCol; + finalColor *= surface.ao; if(isCapturing == 1) - return float4(lerp((irradiance + specular* horizon), surface.baseColor.rgb,surface.metalness),0); + return float4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0); else - return float4((irradiance + specular* horizon)*ambientColor, 0);//alpha writes disabled + { + return float4((finalColor*ambientColor), 0); + } }