specular review

cleanup of various calcs
This commit is contained in:
AzaezelX 2025-03-22 10:00:45 -05:00
parent 903c987d53
commit 42710ec6f0
6 changed files with 317 additions and 217 deletions

View file

@ -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); // Compute the Fresnel-Schlick term for the given cosine of the angle
float powTheta = pow(1.0 - cosTheta, 5.0); float powTheta = pow(1.0 - cosTheta, 5.0);
float invRough = float(1.0 - roughness);
// 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; return fresnel;
ret.y = F0.y + (max(invRough, F0.y) - F0.y) * powTheta;
ret.z = F0.z + (max(invRough, F0.z) - F0.z) * powTheta;
return ret;
} }
float V_SmithGGXCorrelated(float NdotL, float NdotV, float alphaRoughnessSq) float GeometrySchlickGGX(float NdotV, float roughness) {
{ float r = (roughness + 1.0);
float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); float k = (r * r) / 8.0;
float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); return NdotV / (NdotV * (1.0 - k) + k);
float GGX = GGXV + GGXL;
if (GGX > 0.0f)
{
return 0.5f / GGX;
}
return 0.f;
} }
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; float f = (NdotH * NdotH * (roughnessSq - 1.0) + 1.0);
return alphaRoughnessSq / (M_PI_F * f * f); 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) 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 lightScatter = F_Schlick( F0 , fd90 );
float3 viewScatter = F_Schlick(F0 , fd90 , NdotV ); float3 viewScatter = F_Schlick(F0 , fd90);
return lightScatter * viewScatter * energyFactor ; return lightScatter * viewScatter * energyFactor ;
} }

View file

@ -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); // Compute the Fresnel-Schlick term for the given cosine of the angle
float powTheta = pow(1.0 - cosTheta, 5.0); float powTheta = pow(1.0 - cosTheta, 5.0);
float invRough = float(1.0 - roughness);
// 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; return fresnel;
ret.y = F0.y + (max(invRough, F0.y) - F0.y) * powTheta;
ret.z = F0.z + (max(invRough, F0.z) - F0.z) * powTheta;
return ret;
} }
float V_SmithGGXCorrelated(float NdotL, float NdotV, float alphaRoughnessSq) float GeometrySchlickGGX(float NdotV, float roughness) {
{ float r = (roughness + 1.0);
float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); float k = (r * r) / 8.0;
float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - alphaRoughnessSq) + alphaRoughnessSq); return NdotV / (NdotV * (1.0 - k) + k);
float GGX = GGXV + GGXL;
if (GGX > 0.0f)
{
return 0.5f / GGX;
}
return 0.f;
} }
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; float f = (NdotH * NdotH * (roughnessSq - 1.0) + 1.0);
return alphaRoughnessSq / (M_PI_F * f * f); 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) vec3 Fr_DisneyDiffuse(vec3 F0, float NdotV, float NdotL, float LdotH, float linearRoughness)
{ {
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 ;
vec3 lightScatter = F_Schlick( F0 , fd90 , NdotL ); vec3 lightScatter = F_Schlick( F0 , fd90 );
vec3 viewScatter = F_Schlick(F0 , fd90 , NdotV ); vec3 viewScatter = F_Schlick(F0 , fd90);
return lightScatter * viewScatter * energyFactor ; return lightScatter * viewScatter * energyFactor ;
} }
#endif #endif

View file

@ -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 // Compute Fresnel term
vec3 Fd = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; vec3 F = F_Schlick(surface.f0, surfaceToLight.HdotV);
//GGX specular // GGX Normal Distribution Function
vec3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughness);
float Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq);
float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq); // Smith GGX Geometry Function
vec3 Fr = D * F * Vis; 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); // Calculate both specular and diffuse lighting in one BRDF evaluation
return evaluateStandardBRDF(surface,surfaceToLight) * factor; 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; vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
irradiance *= kD * surface.baseColor.rgb;
//AO vec3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y);
irradiance *= surface.ao; specularCol *= surface.metalness + (1.0 - surface.roughness);
specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); // Final color output after environment lighting
vec3 finalColor = diffuse + specularCol;
//http://marmosetco.tumblr.com/post/81245981087 finalColor *= surface.ao;
float horizonOcclusion = 1.3;
float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N));
horizon *= horizon;
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; vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
irradiance *= kD * surface.baseColor.rgb;
//AO vec3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y);
irradiance *= surface.ao; specularCol *= surface.metalness + (1.0 - surface.roughness);
specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); // Final color output after environment lighting
vec3 finalColor = diffuse + specularCol;
//http://marmosetco.tumblr.com/post/81245981087 finalColor *= surface.ao;
float horizonOcclusion = 1.3; if(isCapturing == 1)
float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); return vec4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0);
horizon *= horizon; else
{
return vec4((irradiance + specular* horizon) , 0);//alpha writes disabled return vec4(finalColor, 0);
}
} }

View file

@ -113,12 +113,12 @@ struct Surface
linearRoughness = roughness * roughness; linearRoughness = roughness * roughness;
linearRoughnessSq = linearRoughness * linearRoughness; linearRoughnessSq = linearRoughness * linearRoughness;
albedo = baseColor.rgb * (1.0f - metalness); albedo = max(toLinear(baseColor.rgb),0.04f);
f0 = lerp(0.04f, baseColor.rgb, metalness); 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 // Compute Fresnel term
float3 Fd = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; float3 F = F_Schlick(surface.f0, surfaceToLight.HdotV);
//GGX specular // GGX Normal Distribution Function
float3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughness);
float Vis = V_SmithGGXCorrelated(surface.NdotV, surfaceToLight.NdotL, surface.linearRoughnessSq);
float D = D_GGX(surfaceToLight.NdotH, surface.linearRoughnessSq); // Smith GGX Geometry Function
float3 Fr = D * F * Vis; 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) ; // Calculate both specular and diffuse lighting in one BRDF evaluation
return evaluateStandardBRDF(surface,surfaceToLight) * factor; 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 float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(surface.NdotV, surface.roughness)).rg;
float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); float3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
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) float3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y);
float2 envBRDF = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.NdotV, surface.roughness,0,0)).rg; specularCol *= surface.metalness + (1.0 - surface.roughness);
specular *= F * envBRDF.x + surface.f90 * envBRDF.y; // Final color output after environment lighting
irradiance *= kD * surface.baseColor.rgb; float3 finalColor = diffuse + specularCol;
finalColor *= surface.ao;
//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;
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 float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(surface.NdotV, surface.roughness)).rg;
float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); float3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
float3 kD = 1.0f - F;
kD *= 1.0f - surface.metalness;
float2 envBRDF = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.NdotV, surface.roughness,0,0)).rg; float3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y);
specular *= F * envBRDF.x + surface.f90 * envBRDF.y; specularCol *= surface.metalness + (1.0 - surface.roughness);
irradiance *= kD * surface.baseColor.rgb; // Final color output after environment lighting
float3 finalColor = diffuse + specularCol;
//AO finalColor *= surface.ao;
irradiance *= surface.ao; if(isCapturing == 1)
specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); return float4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0);
else
//http://marmosetco.tumblr.com/post/81245981087 {
float horizonOcclusion = 1.3; return float4(finalColor, 0);
float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); }
horizon *= horizon;
return float4((irradiance + specular* horizon) , 0);//alpha writes disabled
} }

View file

@ -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; vec3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
irradiance *= kD * surface.baseColor.rgb;
//AO vec3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y);
irradiance *= surface.ao; specularCol *= surface.metalness + (1.0 - surface.roughness);
specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); // Final color output after environment lighting
vec3 finalColor = diffuse + specularCol;
//http://marmosetco.tumblr.com/post/81245981087 finalColor *= surface.ao;
float horizonOcclusion = 1.3;
float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N));
horizon *= horizon;
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);
}
} }

View file

@ -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 #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; float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(surface.NdotV, surface.roughness)).rg;
specular *= F * envBRDF.x + surface.f90 * envBRDF.y; float3 diffuse = irradiance * surface.baseColor.rgb * (1.0 - surface.metalness);
irradiance *= kD * surface.baseColor.rgb;
//AO float3 specularCol = specular * (F_Schlick(surface.f0, surface.NdotV) * envBRDF.x + envBRDF.y);
irradiance *= surface.ao; specularCol *= surface.metalness + (1.0 - surface.roughness);
specular *= computeSpecOcclusion(surface.NdotV, surface.ao, surface.roughness); // Final color output after environment lighting
float3 finalColor = diffuse + specularCol;
//http://marmosetco.tumblr.com/post/81245981087 finalColor *= surface.ao;
float horizonOcclusion = 1.3;
float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N));
horizon *= horizon;
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);
}
} }