Browse Source

specular review
cleanup of various calcs

AzaezelX 6 months ago
parent
commit
42710ec6f0

+ 59 - 24
Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl

@@ -36,41 +36,76 @@
 // Charles de Rousiers - Electronic Arts Frostbite
 // Charles de Rousiers - Electronic Arts Frostbite
 // SIGGRAPH 2014
 // 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 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 fresnel;
+}
+
+float GeometrySchlickGGX(float NdotV, float roughness) { 
+    float r = (roughness + 1.0); 
+    float k = (r * r) / 8.0;
+    return NdotV / (NdotV * (1.0 - k) + k);
+}
 
 
-	return ret;
+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 V_SmithGGXCorrelated(float NdotL, float NdotV, float alphaRoughnessSq)
+float D_GGX(float NdotH, float roughnessSq)
 {
 {
-	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 f = (NdotH * NdotH * (roughnessSq - 1.0) + 1.0);
+	return roughnessSq / (M_PI_F * f * f);
 }
 }
 
 
-float D_GGX(float NdotH, float alphaRoughnessSq)
+// The Cook-Torrance BRDF for specular reflection
+float3 CookTorrance(float3 normal, float3 viewDir, float roughness, float3 F)
 {
 {
-	float f = (NdotH * alphaRoughnessSq - NdotH) * NdotH + 1;
-	return alphaRoughnessSq / (M_PI_F * f * 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)
 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 energyBias = lerp (0 , 0.5 , linearRoughness );
 	float energyFactor = lerp (1.0 , 1.0 / 1.51 , linearRoughness );
 	float energyFactor = lerp (1.0 , 1.0 / 1.51 , linearRoughness );
 	float fd90 = energyBias + 2.0 * LdotH * LdotH * 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 ;
 	return lightScatter * viewScatter * energyFactor ;
 }
 }

+ 61 - 27
Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl

@@ -30,52 +30,86 @@
 // Charles de Rousiers - Electronic Arts Frostbite
 // Charles de Rousiers - Electronic Arts Frostbite
 // SIGGRAPH 2014
 // 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 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 fresnel;
+}
+
+float GeometrySchlickGGX(float NdotV, float roughness) { 
+    float r = (roughness + 1.0); 
+    float k = (r * r) / 8.0;
+    return NdotV / (NdotV * (1.0 - k) + k);
+}
 
 
-	return ret;
+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 V_SmithGGXCorrelated(float NdotL, float NdotV, float alphaRoughnessSq)
+float D_GGX(float NdotH, float roughnessSq)
 {
 {
-	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 f = (NdotH * NdotH * (roughnessSq - 1.0) + 1.0);
+	return roughnessSq / (M_PI_F * f * f);
 }
 }
 
 
-float D_GGX(float NdotH, float alphaRoughnessSq)
+// The Cook-Torrance BRDF for specular reflection
+vec3 CookTorrance(vec3 normal, vec3 viewDir, float roughness, vec3 F)
 {
 {
-	float f = (NdotH * alphaRoughnessSq - NdotH) * NdotH + 1;
-	return alphaRoughnessSq / (M_PI_F * f * 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)
 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 ;
 	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 ;
 	return lightScatter * viewScatter * energyFactor ;
 }
 }
-
 #endif
 #endif

+ 81 - 58
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.linearRoughness = surface.roughness * surface.roughness;
     surface.linearRoughnessSq = surface.linearRoughness * surface.linearRoughness;
     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.f0 = mix(vec3(0.04f,0.04f,0.04f), surface.baseColor.rgb, surface.metalness);
 
 
 	surface.R = -reflect(surface.V, surface.N);
 	surface.R = -reflect(surface.V, surface.N);
 	surface.f90 = saturate(50.0 * dot(surface.f0, vec3(0.33,0.33,0.33)));
 	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)
 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)
 vec3 BRDF_GetDebugSpecular(in Surface surface, in SurfaceToLight surfaceToLight)
 {
 {
    //GGX specular
    //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 Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq);
    float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq);
    float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq);
    vec3 Fr = D * F * Vis * M_1OVER_PI_F;
    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)
 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)
    if(isCapturing == 1)
-      return mix(Fd + Fr, surface.baseColor.rgb, surface.metalness);
+      return lerp(returnBRDF ,surface.albedo.rgb,surface.metalness);
    else
    else
-      return Fd + Fr;
+      return returnBRDF;
 }
 }
 
 
 vec3 getDirectionalLight(Surface surface, SurfaceToLight surfaceToLight, vec3 lightColor, float lightIntensity, float shadow)
 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)
    if(isCapturing != 1)
       lightfloor = 0.0;
       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)
 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;
       lightfloor = 0.0;
       
       
    float attenuation = getDistanceAtt(surfaceToLight.Lu, radius);
    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)
 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;
    float attenuation = 1.0f;
    attenuation *= getDistanceAtt(surfaceToLight.Lu, radius);
    attenuation *= getDistanceAtt(surfaceToLight.Lu, radius);
    attenuation *= getSpotAngleAtt(-surfaceToLight.L, lightDir, lightSpotParams.xy);
    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 )
 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)
 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,
 vec4 compute4Lights( Surface surface,
@@ -541,33 +578,24 @@ vec4 computeForwardProbes(Surface surface,
       
       
    if(skylightCubemapIdx != -1 && alpha >= 0.001)
    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);
       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;
    vec2 envBRDF = textureLod(BRDFTexture, vec2(surface.NdotV, surface.roughness),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);
+   vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
 
 
-   //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)
    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
    else
-      return vec4((irradiance + specular* horizon) , 0);//alpha writes disabled
+   {
+      return vec4(finalColor, 0);
+   }
 }
 }
 
 
 vec4 debugVizForwardProbes(Surface surface,
 vec4 debugVizForwardProbes(Surface surface,
@@ -693,7 +721,7 @@ vec4 debugVizForwardProbes(Surface surface,
 
 
    if(skylightCubemapIdx != -1 && alpha >= 0.001)
    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);
       specular = mix(specular,textureLod(specularCubemapAR, vec4(surface.R, skylightCubemapIdx), lod).xyz,alpha);
    }
    }
 
 
@@ -707,23 +735,18 @@ vec4 debugVizForwardProbes(Surface surface,
       return vec4(irradiance, 0);
       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;
    vec2 envBRDF = textureLod(BRDFTexture, vec2(surface.NdotV, surface.roughness),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);
+   vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
 
 
-   //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);
+   }
 }
 }

+ 83 - 60
Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl

@@ -113,12 +113,12 @@ struct Surface
         linearRoughness = roughness * roughness;
         linearRoughness = roughness * roughness;
         linearRoughnessSq = linearRoughness * linearRoughness;
         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);
 		R = -reflect(V, N);
 		f90 = saturate(50.0f * dot(f0, 0.33f));
 		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)
 float3 BRDF_GetDebugSpecular(in Surface surface, in SurfaceToLight surfaceToLight)
 {
 {
    //GGX specular
    //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 Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq);
    float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq);
    float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq);
    float3 Fr = D * F * Vis * M_1OVER_PI_F;
    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)
 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)
    if(isCapturing == 1)
-      return lerp(Fd + Fr,surface.baseColor.rgb,surface.metalness);
+      return lerp(returnBRDF ,surface.albedo.rgb,surface.metalness);
    else
    else
-      return Fd + Fr;
+      return returnBRDF;
 }
 }
 
 
 float3 getDirectionalLight(Surface surface, SurfaceToLight surfaceToLight, float3 lightColor, float lightIntensity, float shadow)
 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)
    if(isCapturing != 1)
       lightfloor = 0.0;
       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)
 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;
       lightfloor = 0.0;
       
       
    float attenuation = getDistanceAtt(surfaceToLight.Lu, radius);
    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)
 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;
    float attenuation = 1.0f;
    attenuation *= getDistanceAtt(surfaceToLight.Lu, radius);
    attenuation *= getDistanceAtt(surfaceToLight.Lu, radius);
    attenuation *= getSpotAngleAtt(-surfaceToLight.L, lightDir, lightSpotParams.xy);
    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 )
 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)
 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,
 float4 compute4Lights( Surface surface,
@@ -547,33 +584,24 @@ float4 computeForwardProbes(Surface surface,
 
 
    if(skylightCubemapIdx != -1 && alpha >= 0.001)
    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);
       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;
-
-   //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;
+   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)
    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
    else
-      return float4((irradiance + specular* horizon) , 0);//alpha writes disabled
+   {
+      return float4(finalColor, 0);
+   }
 }
 }
 
 
 float4 debugVizForwardProbes(Surface surface,
 float4 debugVizForwardProbes(Surface surface,
@@ -713,23 +741,18 @@ float4 debugVizForwardProbes(Surface surface,
       return float4(irradiance, 0);
       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);
+   }
 }
 }

+ 10 - 19
Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl

@@ -204,27 +204,18 @@ void main()
    return;
    return;
 #endif
 #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;
    vec2 envBRDF = textureLod(BRDFTexture, vec2(surface.NdotV, surface.roughness),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);
+   vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
 
 
-   //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)
    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
    else
-      OUT_col = vec4((irradiance + specular* horizon)*ambientColor, 0);//alpha writes disabled
+   {
+      OUT_col = vec4(finalColor*ambientColor, 0);
+   }
 }
 }

+ 21 - 27
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);
    dampen(surface, TORQUE_SAMPLER2D_MAKEARG(WetnessTexture), accumTime, wetAmmout*dampness);
    
    
    // Radiance (Specular)
    // Radiance (Specular)
-#if DEBUGVIZ_SPECCUBEMAP == 0
-   float lod = roughnessToMipLevel(surface.roughness, cubeMips);
-#elif DEBUGVIZ_SPECCUBEMAP == 1
    float lod = 0;
    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++)
    for (i = 0; i < numProbes; i++)
    {
    {
       float contrib = contribution[i];
       float contrib = contribution[i];
@@ -181,7 +182,7 @@ float4 main(PFXVertToPix IN) : SV_TARGET
 #endif
 #endif
    if(skylightCubemapIdx != -1 && alpha >= 0.001)
    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);
       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);
    return float4(specular, 1);
 #elif DEBUGVIZ_DIFFCUBEMAP == 1
 #elif DEBUGVIZ_DIFFCUBEMAP == 1
    return float4(irradiance, 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;
-
-   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;
-   
+#endif   
+
+   float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(surface.NdotV, surface.roughness)).rg;
+   float3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
+
+   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)
    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
    else
-      return float4((irradiance + specular* horizon)*ambientColor, 0);//alpha writes disabled
+   {
+      return float4((finalColor*ambientColor), 0);
+   }
 }
 }