فهرست منبع

specular review
cleanup of various calcs

AzaezelX 6 ماه پیش
والد
کامیت
42710ec6f0

+ 59 - 24
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 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)
@@ -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 ;
 }

+ 61 - 27
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 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)
 {
-	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

+ 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.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;
-
-   //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)
-      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;
-
-   //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;
         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;
-
-   //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)
-      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);
+   }
 }

+ 10 - 19
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;
-
-   //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)
-      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);
+   }
 }

+ 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);
    
    // 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;
-
-   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)
-      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);
+   }
 }