From 6968753783c8ad691982d6f3c0893a35dc4d95c4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 19 Dec 2017 17:40:34 +0100 Subject: [PATCH 01/10] Added G factor in PBR shading and removed evalPBRShadingGloss --- libraries/render-utils/src/LightingModel.slh | 92 +++++++++----------- 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 521c4894dc..64e819cf38 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -121,86 +121,72 @@ float fresnelSchlickScalar(float fresnelScalar, vec3 lightDir, vec3 halfDir) { return (exponential) + fresnelScalar * (1.0 - exponential); } -float specularDistribution(float roughness, vec3 normal, vec3 halfDir) { - float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0); -// float gloss2 = pow(0.001 + roughness, 4); - float gloss2 = (0.001 + roughness); - gloss2 *= gloss2; // pow 2 - gloss2 *= gloss2; // pow 4 - float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0); - float power = gloss2 / (3.14159 * denom * denom); +float specularDistribution(float roughness, vec3 normal, vec3 halfDir, vec3 eyeDir, float ndotl) { + // See https://www.khronos.org/assets/uploads/developers/library/2017-web3d/glTF-2.0-Launch_Jun17.pdf + // for details of equations, especially page 20 + float ndoth = dot(halfDir, normal); + float ndotv = dot(eyeDir, normal); + vec3 nd = clamp(vec3(ndoth, ndotv, ndotl), vec3(0.0), vec3(1.0)); + vec3 nd2 = nd * nd; + float roughness2 = (0.001 + roughness*0.999); + roughness2 *= roughness2; // pow 2 + float roughness4 = roughness2; + roughness4 *= roughness4; // pow 4 + float denom = (nd2.x * (roughness2 - 1.0) + 1.0); + denom *= denom; + // Add geometric factors G1(n,l) and G1(n,v) + float oneMinusRoughness4 = 1.0-roughness4; + vec2 invG = nd.yz + sqrt(vec2(roughness4)+nd2.yz*oneMinusRoughness4); + denom *= invG.x * invG.y; + // Don't divide by PI as diffuse isn't either. The real PBR formula normalizes + // by PI on both but if we decide to not do it, it is just as if we + // multiplied all our lights by PI + //denom *= 3.1415926; + float power = roughness4 / denom; return power; } -float specularDistributionGloss(float gloss2, vec3 normal, vec3 halfDir) { - float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0); -// float gloss2 = pow(0.001 + roughness, 4); - float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0); - float power = gloss2 / (3.14159 * denom * denom); - return power; -} - + // Frag Shading returns the diffuse amount as W and the specular rgb as xyz vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + // Incident angle attenuation + float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); // Specular Lighting vec3 halfDir = normalize(fragEyeDir + fragLightDir); vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; + float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 specular = fresnelColor * power * angleAttenuation; - return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); + return vec4(specular, (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x)); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, float fresnel) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + // Incident angle attenuation + float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); // Specular Lighting vec3 halfDir = normalize(fragEyeDir + fragLightDir); float fresnelScalar = fresnelSchlickScalar(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = vec3(fresnelScalar) * power * diffuse; + float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; - return vec4(specular, diffuse * (1.0 - fresnelScalar)); + return vec4(specular, angleAttenuation * (1.0 - fresnelScalar)); } vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + // Incident angle attenuation + float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); // Specular Lighting vec3 halfDir = normalize(fragEyeDir + fragLightDir); vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; + float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 specular = fresnelColor * power * angleAttenuation; return vec4(specular, 0.f); } -// Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShadingGloss(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float gloss2) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); - - // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistributionGloss(gloss2, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; - - return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); -} - <@endfunc@> @@ -244,7 +230,7 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); } else { - vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); diffuse = vec3(shading.w); specular = shading.xyz; } @@ -274,7 +260,7 @@ void evalFragShadingGloss(out vec3 diffuse, out vec3 specular, vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float gloss, vec3 albedo ) { - vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss); + vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss); diffuse = vec3(shading.w); diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled()); specular = shading.xyz; From 0e666ce671c024b2bfc6b2332c7e9d710b72535d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 20 Dec 2017 09:34:53 +0100 Subject: [PATCH 02/10] Metallic value is now continuous and materials can be linearly blended between pure dielectric and pure metal --- .../render-utils/src/DeferredBufferRead.slh | 23 +++++++------------ libraries/render-utils/src/LightingModel.slh | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index fbca241bb9..5667f63d13 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -46,6 +46,12 @@ struct DeferredFragment { float depthVal; }; +vec3 getFresnelF0(float metallic, vec3 metalF0) { + // Enable continuous metallness value by lerping between dielectric + // and metal fresnel F0 value based on the "metallic" parameter + return mix(vec3(0.03), metalF0, metallic); +} + DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { vec4 normalVal; vec4 diffuseVal; @@ -73,13 +79,7 @@ DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { frag.scattering = specularVal.x; } - if (frag.metallic <= 0.5) { - frag.metallic = 0.0; - frag.fresnel = vec3(0.03); // Default Di-electric fresnel value - } else { - frag.fresnel = vec3(diffuseVal.xyz); - frag.metallic = 1.0; - } + frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz); return frag; } @@ -106,14 +106,7 @@ DeferredFragment unpackDeferredFragmentNoPositionNoAmbient(vec2 texcoord) { //frag.emissive = specularVal.xyz; frag.obscurance = 1.0; - - if (frag.metallic <= 0.5) { - frag.metallic = 0.0; - frag.fresnel = vec3(0.03); // Default Di-electric fresnel value - } else { - frag.fresnel = vec3(diffuseVal.xyz); - frag.metallic = 1.0; - } + frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz); return frag; } diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 64e819cf38..d77e19f13a 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -128,7 +128,7 @@ float specularDistribution(float roughness, vec3 normal, vec3 halfDir, vec3 eyeD float ndotv = dot(eyeDir, normal); vec3 nd = clamp(vec3(ndoth, ndotv, ndotl), vec3(0.0), vec3(1.0)); vec3 nd2 = nd * nd; - float roughness2 = (0.001 + roughness*0.999); + float roughness2 = mix(0.001, 1.0, roughness); roughness2 *= roughness2; // pow 2 float roughness4 = roughness2; roughness4 *= roughness4; // pow 4 From e8b88cd3f845dcd9ce5624f406ad6058b69c9564 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 20 Dec 2017 11:14:07 +0100 Subject: [PATCH 03/10] Specular lighting comming from ambient sphere map is now multiplied by PI to be at the same level as normal light specular --- libraries/render-utils/src/LightAmbient.slh | 29 ++++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 5f74b46d3e..f3f1411394 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,8 +22,10 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { - return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0); +vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { + float f = pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0); + return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; +// return mix(fresnelColor, vec3(1.0), f); } <@if supportAmbientMap@> @@ -31,7 +33,7 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float <@endif@> vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) { - vec3 direction = -reflect(fragEyeDir, fragNormal); + vec3 lightDir = -reflect(fragEyeDir, fragNormal); vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -40,7 +42,13 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f { float levels = getLightAmbientMapNumMips(ambient); float lod = min(((roughness)* levels), levels); - specularLight = evalSkyboxLight(direction, lod).xyz; + specularLight = evalSkyboxLight(lightDir, lod).xyz; + // We multiply specular by Pi to match specular / diffuse normalization in + // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously + // speaking, based on physical equations). The spherical harmonics evaluation, on + // the other hand, seems to be Pi times stronger than usual. So all in all, everything + // should be at the right level now. + specularLight *= 3.1415926; } <@endif@> <@if supportIfAmbientMapElseAmbientSphere@> @@ -48,11 +56,11 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@endif@> <@if supportAmbientSphere@> { - specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), direction).xyz; + specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; } <@endif@> - return specularLight; + return specularLight; } <@endfunc@> @@ -67,14 +75,14 @@ float curvatureAO(in float k) { <@endif@> void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, vec3 eyeDir, vec3 normal, - float roughness, float metallic, vec3 fresnel, vec3 albedo, float obscurance + float roughness, float metallic, vec3 fresnelF0, vec3 albedo, float obscurance <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature <@endif@> ) { // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, eyeDir, normal, 1.0 - roughness); // Diffuse from ambient diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; @@ -107,8 +115,9 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie diffuse *= albedo; } - diffuse *= lightEnergy * isDiffuseEnabled() * isAmbientEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isAmbientEnabled(); + lightEnergy *= isAmbientEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); } <@endfunc@> From 053bd2ba9826a13311219e30cb3ede9214783310 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 21 Dec 2017 17:41:36 +0100 Subject: [PATCH 04/10] Created SurfaceData structure to centralize computation of frequently used shader temporary values --- .../render-utils/src/DeferredBufferRead.slh | 3 + .../render-utils/src/DeferredGlobalLight.slh | 24 ++- .../render-utils/src/ForwardGlobalLight.slh | 18 +- libraries/render-utils/src/LightAmbient.slh | 25 +-- .../render-utils/src/LightDirectional.slh | 11 +- libraries/render-utils/src/LightPoint.slh | 11 +- libraries/render-utils/src/LightSpot.slh | 10 +- libraries/render-utils/src/LightingModel.slh | 166 +++++++++++------- .../render-utils/src/SubsurfaceScattering.slh | 32 ---- .../render-utils/src/local_lights_shading.slf | 17 +- .../render-utils/src/model_translucent.slf | 8 +- .../src/model_translucent_fade.slf | 8 +- libraries/render-utils/src/overlay3D.slf | 4 +- .../src/overlay3D_translucent.slf | 4 +- 14 files changed, 180 insertions(+), 161 deletions(-) diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index 5667f63d13..3cbed3fcef 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -46,11 +46,14 @@ struct DeferredFragment { float depthVal; }; +<@if not GETFRESNEL0@> +<@def GETFRESNEL0@> vec3 getFresnelF0(float metallic, vec3 metalF0) { // Enable continuous metallness value by lerping between dielectric // and metal fresnel F0 value based on the "metallic" parameter return mix(vec3(0.03), metalF0, metallic); } +<@endif@> DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { vec4 normalVal; diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index de2d41be6b..ad4a9a3006 100644 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -110,10 +112,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu ) { <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -123,7 +127,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -174,19 +178,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; @@ -199,19 +205,21 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze( { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; diff --git a/libraries/render-utils/src/ForwardGlobalLight.slh b/libraries/render-utils/src/ForwardGlobalLight.slh index aba0498ef5..3f77f85b51 100644 --- a/libraries/render-utils/src/ForwardGlobalLight.slh +++ b/libraries/render-utils/src/ForwardGlobalLight.slh @@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -109,10 +111,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu ) { <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -124,7 +128,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -173,19 +177,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index f3f1411394..e466702a34 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,8 +22,9 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { - float f = pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0); +vec3 fresnelSchlickAmbient(vec3 fresnelColor, SurfaceData surface) { + float gloss = 1.0 - surface.roughness; + float f = pow(1.0 - surface.ldoth, 5.0); return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; // return mix(fresnelColor, vec3(1.0), f); } @@ -32,8 +33,8 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, floa <$declareSkyboxMap()$> <@endif@> -vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) { - vec3 lightDir = -reflect(fragEyeDir, fragNormal); +vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { + vec3 lightDir = -reflect(surface.eyeDir, surface.normal); vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -41,8 +42,8 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@if supportAmbientMap@> { float levels = getLightAmbientMapNumMips(ambient); - float lod = min(((roughness)* levels), levels); - specularLight = evalSkyboxLight(lightDir, lod).xyz; + float lod = min(((surface.roughness)* levels), levels); + specularLight = evalSkyboxLight(surface.lightDir, lod).xyz; // We multiply specular by Pi to match specular / diffuse normalization in // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously // speaking, based on physical equations). The spherical harmonics evaluation, on @@ -56,7 +57,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@endif@> <@if supportAmbientSphere@> { - specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; + specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.lightDir).xyz; } <@endif@> @@ -74,21 +75,21 @@ float curvatureAO(in float k) { } <@endif@> -void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, vec3 eyeDir, vec3 normal, - float roughness, float metallic, vec3 fresnelF0, vec3 albedo, float obscurance +void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, SurfaceData surface, + float metallic, vec3 fresnelF0, vec3 albedo, float obscurance <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature <@endif@> ) { // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, eyeDir, normal, 1.0 - roughness); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface); // Diffuse from ambient - diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; + diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.normal).xyz; // Specular highlight from ambient - specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness) * ambientFresnel; + specular = evalAmbientSpecularIrradiance(ambient, surface) * ambientFresnel; <@if supportScattering@> if (scattering * isScatteringEnabled() > 0.0) { diff --git a/libraries/render-utils/src/LightDirectional.slh b/libraries/render-utils/src/LightDirectional.slh index 749709c600..b6e1720a2c 100644 --- a/libraries/render-utils/src/LightDirectional.slh +++ b/libraries/render-utils/src/LightDirectional.slh @@ -12,7 +12,7 @@ <@func declareLightingDirectional(supportScattering)@> void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, vec3 lightIrradiance, - vec3 eyeDir, vec3 normal, float roughness, + SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -22,14 +22,17 @@ void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, // Attenuation vec3 lightEnergy = shadow * lightIrradiance; - evalFragShading(diffuse, specular, normal, -lightDir, eyeDir, metallic, fresnel, roughness, albedo + updateSurfaceDataWithLight(surface, -lightDir); + + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isDirectionalEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isDirectionalEnabled(); + lightEnergy *= isDirectionalEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); } <@endfunc@> diff --git a/libraries/render-utils/src/LightPoint.slh b/libraries/render-utils/src/LightPoint.slh index 7e389e11f6..91a1260fcc 100644 --- a/libraries/render-utils/src/LightPoint.slh +++ b/libraries/render-utils/src/LightPoint.slh @@ -12,7 +12,7 @@ <@func declareLightingPoint(supportScattering)@> void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light, - vec4 fragLightDirLen, vec3 fragEyeDir, vec3 normal, float roughness, + vec4 fragLightDirLen, SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -23,19 +23,22 @@ void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light, float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); vec3 lightEnergy = radialAttenuation * shadow * getLightIrradiance(light); // Eval shading - evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isPointEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isPointEnabled(); + lightEnergy *= isPointEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); if (isShowLightContour() > 0.0) { // Show edge diff --git a/libraries/render-utils/src/LightSpot.slh b/libraries/render-utils/src/LightSpot.slh index 8627dae0eb..73c5bd9559 100644 --- a/libraries/render-utils/src/LightSpot.slh +++ b/libraries/render-utils/src/LightSpot.slh @@ -12,7 +12,7 @@ <@func declareLightingSpot(supportScattering)@> void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, - vec4 fragLightDirLen, float cosSpotAngle, vec3 fragEyeDir, vec3 normal, float roughness, + vec4 fragLightDirLen, float cosSpotAngle, SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -23,6 +23,7 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); @@ -30,14 +31,15 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, vec3 lightEnergy = angularAttenuation * radialAttenuation * shadow *getLightIrradiance(light); // Eval shading - evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isSpotEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isSpotEnabled(); + lightEnergy *= isSpotEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); if (isShowLightContour() > 0.0) { // Show edges diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index d77e19f13a..27b5327b09 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -77,6 +77,30 @@ float isWireframeEnabled() { <@endfunc@> <$declareLightingModel()$> +struct SurfaceData { + vec3 normal; + vec3 eyeDir; + vec3 lightDir; + vec3 halfDir; + float roughness; + float roughness2; + float roughness4; + float ndotv; + float ndotl; + float ndoth; + float ldoth; + float smithInvG1NdotV; +}; + +<@if not GETFRESNEL0@> +<@def GETFRESNEL0@> +vec3 getFresnelF0(float metallic, vec3 metalF0) { + // Enable continuous metallness value by lerping between dielectric + // and metal fresnel F0 value based on the "metallic" parameter + return mix(vec3(0.03), metalF0, metallic); +} +<@endif@> + <@func declareBeckmannSpecular()@> uniform sampler2D scatteringSpecularBeckmann; @@ -85,17 +109,13 @@ float fetchSpecularBeckmann(float ndoth, float roughness) { return pow(2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0); } -vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { +vec2 skinSpecular(SurfaceData surface, float intensity) { vec2 result = vec2(0.0, 1.0); - float ndotl = dot(N, L); - if (ndotl > 0.0) { - vec3 h = L + V; - vec3 H = normalize(h); - float ndoth = dot(N, H); - float PH = fetchSpecularBeckmann(ndoth, roughness); - float F = fresnelSchlickScalar(0.028, H, V); - float frSpec = max(PH * F / dot(h, h), 0.0); - result.x = ndotl * intensity * frSpec; + if (surface.ndotl > 0.0) { + float PH = fetchSpecularBeckmann(surface.ndoth, surface.roughness); + float F = fresnelSchlickScalar(0.028, surface); + float frSpec = max(PH * F / dot(surface.halfDir, surface.halfDir), 0.0); + result.x = surface.ndotl * intensity * frSpec; result.y -= F; } @@ -105,83 +125,105 @@ vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { <@func declareEvalPBRShading()@> -vec3 fresnelSchlickColor(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) { - float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0); +float evalSmithInvG1(float roughness4, float ndotd) { + return ndotd + sqrt(roughness4+ndotd*ndotd*(1.0-roughness4)); +} + +SurfaceData initSurfaceData(float roughness, vec3 normal, vec3 eyeDir) { + SurfaceData surface; + surface.eyeDir = eyeDir; + surface.normal = normal; + surface.lightDir = vec3(0,0,0); + surface.halfDir = vec3(0,0,0); + surface.roughness = mix(0.001, 1.0, roughness); + surface.roughness2 = surface.roughness * surface.roughness; + surface.roughness4 = surface.roughness2 * surface.roughness2; + surface.ndotv = clamp(dot(normal, eyeDir), 0.0, 1.0); + surface.ndoth = 0.0; + surface.ndotl = 0.0; + surface.ldoth = 0.0; + surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv); + return surface; +} + +void updateSurfaceDataWithLight(inout SurfaceData surface, vec3 lightDir) { + surface.lightDir = lightDir; + surface.halfDir = normalize(surface.eyeDir + lightDir); + vec3 dots; + dots.x = dot(surface.normal, surface.halfDir); + dots.y = dot(surface.normal, surface.lightDir); + dots.z = dot(surface.halfDir, surface.lightDir); + dots = clamp(dots, vec3(0), vec3(1)); + surface.ndoth = dots.x; + surface.ndotl = dots.y; + surface.ldoth = dots.z; +} + +vec3 fresnelSchlickColor(vec3 fresnelColor, SurfaceData surface) { + float base = 1.0 - surface.ldoth; //float exponential = pow(base, 5.0); float base2 = base * base; float exponential = base * base2 * base2; return vec3(exponential) + fresnelColor * (1.0 - exponential); } -float fresnelSchlickScalar(float fresnelScalar, vec3 lightDir, vec3 halfDir) { - float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0); +float fresnelSchlickScalar(float fresnelScalar, SurfaceData surface) { + float base = 1.0 - surface.ldoth; //float exponential = pow(base, 5.0); float base2 = base * base; float exponential = base * base2 * base2; return (exponential) + fresnelScalar * (1.0 - exponential); } -float specularDistribution(float roughness, vec3 normal, vec3 halfDir, vec3 eyeDir, float ndotl) { +float specularDistribution(SurfaceData surface) { // See https://www.khronos.org/assets/uploads/developers/library/2017-web3d/glTF-2.0-Launch_Jun17.pdf // for details of equations, especially page 20 - float ndoth = dot(halfDir, normal); - float ndotv = dot(eyeDir, normal); - vec3 nd = clamp(vec3(ndoth, ndotv, ndotl), vec3(0.0), vec3(1.0)); - vec3 nd2 = nd * nd; - float roughness2 = mix(0.001, 1.0, roughness); - roughness2 *= roughness2; // pow 2 - float roughness4 = roughness2; - roughness4 *= roughness4; // pow 4 - float denom = (nd2.x * (roughness2 - 1.0) + 1.0); + float denom = (surface.ndoth*surface.ndoth * (surface.roughness2 - 1.0) + 1.0); denom *= denom; // Add geometric factors G1(n,l) and G1(n,v) - float oneMinusRoughness4 = 1.0-roughness4; - vec2 invG = nd.yz + sqrt(vec2(roughness4)+nd2.yz*oneMinusRoughness4); - denom *= invG.x * invG.y; + float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl); + denom *= surface.smithInvG1NdotV * smithInvG1NdotL; // Don't divide by PI as diffuse isn't either. The real PBR formula normalizes // by PI on both but if we decide to not do it, it is just as if we // multiplied all our lights by PI //denom *= 3.1415926; - float power = roughness4 / denom; + float power = surface.roughness4 / denom; return power; } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { +vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { // Incident angle attenuation - float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); + float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; return vec4(specular, (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x)); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, float fresnel) { +vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { // Incident angle attenuation - float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - float fresnelScalar = fresnelSchlickScalar(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + float fresnelScalar = fresnelSchlickScalar(fresnel, surface); + float power = specularDistribution(surface); vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; return vec4(specular, angleAttenuation * (1.0 - fresnelScalar)); } -vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) { +vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { // Incident angle attenuation - float angleAttenuation = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir, fragEyeDir, angleAttenuation); + vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); + float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; return vec4(specular, 0.f); @@ -193,15 +235,9 @@ vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, <$declareEvalPBRShading()$> -// Return xyz the specular/reflection component and w the diffuse component -//vec4 evalFragShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { -// return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); -//} - void evalFragShading(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo) { - vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo) { + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); if (isAlbedoEnabled() > 0.0) { diffuse *= albedo; @@ -215,22 +251,19 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, void evalFragShading(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo, + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature) { if (scattering * isScatteringEnabled() > 0.0) { - vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); - float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); - diffuse = mix(vec3(NdotL), brdf, scattering); + vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); + diffuse = mix(vec3(surface.ndotl), brdf, scattering); // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0); + vec2 specularBrdf = skinSpecular(surface, 1.0); diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); } else { - vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); specular = shading.xyz; } @@ -239,17 +272,15 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo ,float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature ) { - vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); - float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); + float NdotL = surface.ndotl; diffuse = mix(vec3(NdotL), brdf, scattering); // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0); + vec2 specularBrdf = skinSpecular(surface, 1.0); diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); @@ -257,10 +288,9 @@ void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, } void evalFragShadingGloss(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float gloss, vec3 albedo + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo ) { - vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss); + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled()); specular = shading.xyz; diff --git a/libraries/render-utils/src/SubsurfaceScattering.slh b/libraries/render-utils/src/SubsurfaceScattering.slh index 201ec2291a..233dfd7a0c 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.slh +++ b/libraries/render-utils/src/SubsurfaceScattering.slh @@ -63,38 +63,6 @@ vec3 scatter(float r) { <@endfunc@> -<@func declareSkinSpecularLighting()@> - -uniform sampler2D scatteringSpecularBeckmann; - -float fetchSpecularBeckmann(float ndoth, float roughness) { - return pow( 2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0); -} - -float fresnelReflectance(vec3 H, vec3 V, float Fo) { - float base = 1.0 - dot(V, H); - float exponential = pow(base, 5.0); - return exponential + Fo * (1.0 - exponential); -} - -float skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { - float result = 0.0; - float ndotl = dot(N, L); - if (ndotl > 0.0) { - vec3 h = L + V; - vec3 H = normalize(h); - float ndoth = dot(N, H); - float PH = fetchSpecularBeckmann(ndoth, roughness); - float F = fresnelReflectance(H, V, 0.028); - float frSpec = max(PH * F / dot(h, h), 0.0); - result = ndotl * intensity * frSpec; - } - - return result; -} - -<@endfunc@> - <@func declareSubsurfaceScatteringIntegrate(NumIntegrationSteps)@> diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index c6310cb079..a935a8cb89 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -84,9 +84,8 @@ void main(void) { // Frag to eye vec vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - - // Compute the rougness into gloss2 once: - float fragGloss2 = pow(frag.roughness + 0.001, 4.0); + + SurfaceData surface = initSurfaceData(frag.roughness, frag.normal, fragEyeDir); bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); int numLightTouching = 0; @@ -119,16 +118,18 @@ void main(void) { float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); vec3 lightEnergy = radialAttenuation * getLightIrradiance(light); // Eval shading if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo + evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo ,frag.scattering, midNormalCurvature, lowNormalCurvature ); } else { - evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo); + evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); } diffuse *= lightEnergy * isDiffuseEnabled(); @@ -173,6 +174,8 @@ void main(void) { float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle); @@ -180,10 +183,10 @@ void main(void) { // Eval shading if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo + evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo ,frag.scattering, midNormalCurvature, lowNormalCurvature ); } else { - evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo); + evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); } diffuse *= lightEnergy * isDiffuseEnabled(); diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 38f162fdc3..7e64c5ab8b 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -50,13 +50,7 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index 9d5477304c..0d5a452fed 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -60,13 +60,7 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf index 0cb3340845..b58dd5afac 100644 --- a/libraries/render-utils/src/overlay3D.slf +++ b/libraries/render-utils/src/overlay3D.slf @@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a vec3 fragEyeDir; <$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$> + SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir); + vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient); // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse * isDiffuseEnabled() * isDirectionalEnabled(); color += directionalSpecular * isSpecularEnabled() * isDirectionalEnabled(); diff --git a/libraries/render-utils/src/overlay3D_translucent.slf b/libraries/render-utils/src/overlay3D_translucent.slf index 9bdac2d21f..72bd0d740e 100644 --- a/libraries/render-utils/src/overlay3D_translucent.slf +++ b/libraries/render-utils/src/overlay3D_translucent.slf @@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a vec3 fragEyeDir; <$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$> + SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir); + vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient); // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; From cb4d78ce5ce7d21638480973ac0d000accdfaedf Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 21 Dec 2017 18:47:18 +0100 Subject: [PATCH 05/10] Fixed bug in specular ambient --- libraries/render-utils/src/LightAmbient.slh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index e466702a34..1c13de3221 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,11 +22,9 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, SurfaceData surface) { - float gloss = 1.0 - surface.roughness; - float f = pow(1.0 - surface.ldoth, 5.0); +vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { + float f = pow(1.0 - ndotd, 5.0); return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; -// return mix(fresnelColor, vec3(1.0), f); } <@if supportAmbientMap@> @@ -43,7 +41,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { { float levels = getLightAmbientMapNumMips(ambient); float lod = min(((surface.roughness)* levels), levels); - specularLight = evalSkyboxLight(surface.lightDir, lod).xyz; + specularLight = evalSkyboxLight(lightDir, lod).xyz; // We multiply specular by Pi to match specular / diffuse normalization in // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously // speaking, based on physical equations). The spherical harmonics evaluation, on @@ -57,7 +55,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@endif@> <@if supportAmbientSphere@> { - specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.lightDir).xyz; + specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; } <@endif@> @@ -83,7 +81,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie ) { // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface.ndotv, 1.0-surface.roughness); // Diffuse from ambient diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.normal).xyz; From a6d5e33ecaae18250e05c158d8b3d118a5c71adf Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 22 Dec 2017 10:39:45 +0100 Subject: [PATCH 06/10] Fixed levels between specular, diffuse, ambient specular, ambiend diffuse and background sky --- libraries/render-utils/src/LightAmbient.slh | 16 +++++++--------- libraries/render-utils/src/LightingModel.slh | 16 ++++++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 1c13de3221..8c6dbb85a2 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -22,9 +22,10 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { +vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { float f = pow(1.0 - ndotd, 5.0); return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; +// return fresnelColor + (vec3(1.0) - fresnelColor) * f; } <@if supportAmbientMap@> @@ -40,14 +41,10 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientMap@> { float levels = getLightAmbientMapNumMips(ambient); - float lod = min(((surface.roughness)* levels), levels); + float m = 12.0 / (1.0+5.0*surface.roughness); + float lod = levels - m; + lod = max(lod, 0); specularLight = evalSkyboxLight(lightDir, lod).xyz; - // We multiply specular by Pi to match specular / diffuse normalization in - // LightingModel.slh where diffuse and specular arent divided by Pi (when they should, rigourously - // speaking, based on physical equations). The spherical harmonics evaluation, on - // the other hand, seems to be Pi times stronger than usual. So all in all, everything - // should be at the right level now. - specularLight *= 3.1415926; } <@endif@> <@if supportIfAmbientMapElseAmbientSphere@> @@ -56,6 +53,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientSphere@> { specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; + specularLight /= 3.1415926; } <@endif@> @@ -99,7 +97,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie // Diffuse from ambient diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz; - + diffuse /= 3.1415926; specular = vec3(0.0); } <@endif@> diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 27b5327b09..8a6c03d423 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -183,10 +183,7 @@ float specularDistribution(SurfaceData surface) { // Add geometric factors G1(n,l) and G1(n,v) float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl); denom *= surface.smithInvG1NdotV * smithInvG1NdotL; - // Don't divide by PI as diffuse isn't either. The real PBR formula normalizes - // by PI on both but if we decide to not do it, it is just as if we - // multiplied all our lights by PI - //denom *= 3.1415926; + // Don't divide by PI as it will be done later float power = surface.roughness4 / denom; return power; } @@ -200,8 +197,11 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; + float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x); - return vec4(specular, (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x)); + specular /= 3.1415926; + diffuse /= 3.1415926; + return vec4(specular, diffuse); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz @@ -213,8 +213,11 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { float fresnelScalar = fresnelSchlickScalar(fresnel, surface); float power = specularDistribution(surface); vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; + float diffuse = angleAttenuation * (1.0 - fresnelScalar); - return vec4(specular, angleAttenuation * (1.0 - fresnelScalar)); + specular /= 3.1415926; + diffuse /= 3.1415926; + return vec4(specular, diffuse); } vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { @@ -226,6 +229,7 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; + specular /= 3.1415926; return vec4(specular, 0.f); } From 93ba9ad3b19c0e6fa90ff17cd736e4a939152930 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 26 Dec 2017 15:43:23 +0100 Subject: [PATCH 07/10] Adjusted roughness aspect on specular reflection of sky box to more closely match Unity's PBR look especially on high roughness values --- libraries/render-utils/src/LightAmbient.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 8c6dbb85a2..409b8b0587 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -41,7 +41,7 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientMap@> { float levels = getLightAmbientMapNumMips(ambient); - float m = 12.0 / (1.0+5.0*surface.roughness); + float m = 12.0 / (1.0+11.0*surface.roughness); float lod = levels - m; lod = max(lod, 0); specularLight = evalSkyboxLight(lightDir, lod).xyz; From dcfeed9b2cb99713c4d6adb982674d27409a9e43 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 27 Dec 2017 15:52:17 +0100 Subject: [PATCH 08/10] Switched to using getFresnelF0 function in overlay3D model shaders --- libraries/render-utils/src/overlay3D_model.slf | 8 +------- .../render-utils/src/overlay3D_model_translucent.slf | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/overlay3D_model.slf b/libraries/render-utils/src/overlay3D_model.slf index bb0d84a513..ea61c2bb75 100644 --- a/libraries/render-utils/src/overlay3D_model.slf +++ b/libraries/render-utils/src/overlay3D_model.slf @@ -46,13 +46,7 @@ void main(void) { albedo *= _color; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; diff --git a/libraries/render-utils/src/overlay3D_model_translucent.slf b/libraries/render-utils/src/overlay3D_model_translucent.slf index b26e70f465..b6ae8eb75e 100644 --- a/libraries/render-utils/src/overlay3D_model_translucent.slf +++ b/libraries/render-utils/src/overlay3D_model_translucent.slf @@ -44,13 +44,7 @@ void main(void) { albedo *= _color; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; From c904302057ac5d827849963413854c532757ad23 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 8 Jan 2018 15:23:39 +0100 Subject: [PATCH 09/10] Added back multiplication of specular for point / directional / spot lighting by PI as Naty Hoffman recommends. Limited texture LOD of ambient map by LOD computed for filtering to prevent aliasing --- libraries/render-utils/src/LightAmbient.slh | 4 +++- libraries/render-utils/src/LightingModel.slh | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 409b8b0587..3decdbcf7c 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -16,6 +16,9 @@ uniform samplerCube skyboxMap; vec4 evalSkyboxLight(vec3 direction, float lod) { // textureQueryLevels is not available until #430, so we require explicit lod // float mipmapLevel = lod * textureQueryLevels(skyboxMap); + float filterLod = textureQueryLod(skyboxMap, direction); + // Keep texture filtering LOD as limit to prevent aliasing on specular reflection + lod = max(lod, filterLod); return textureLod(skyboxMap, direction, lod); } <@endfunc@> @@ -53,7 +56,6 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { <@if supportAmbientSphere@> { specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; - specularLight /= 3.1415926; } <@endif@> diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 8a6c03d423..279a010cf4 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -199,8 +199,12 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { vec3 specular = fresnelColor * power * angleAttenuation; float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x); - specular /= 3.1415926; diffuse /= 3.1415926; + // Diffuse is divided by PI but specular isn't because an infinitesimal volume light source + // has a multiplier of PI, says Naty Hoffman. + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") + return vec4(specular, diffuse); } @@ -215,8 +219,11 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; float diffuse = angleAttenuation * (1.0 - fresnelScalar); - specular /= 3.1415926; diffuse /= 3.1415926; + // Diffuse is divided by PI but specular isn't because an infinitesimal volume light source + // has a multiplier of PI, says Naty Hoffman. + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") return vec4(specular, diffuse); } @@ -229,7 +236,10 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; - specular /= 3.1415926; + // Specular isn't divided by PI because an infinitesimal volume light source + // has a multiplier of PI, says Naty Hoffman. + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") return vec4(specular, 0.f); } From 2960ad845c0ddf0b0904d3e3c4b366c0b8dc607e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 10 Jan 2018 10:55:45 +0100 Subject: [PATCH 10/10] Fixed weird rendering bug. --- libraries/render-utils/src/LightAmbient.slh | 2 +- libraries/render-utils/src/LightingModel.slh | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 3decdbcf7c..eb565d60e4 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -16,7 +16,7 @@ uniform samplerCube skyboxMap; vec4 evalSkyboxLight(vec3 direction, float lod) { // textureQueryLevels is not available until #430, so we require explicit lod // float mipmapLevel = lod * textureQueryLevels(skyboxMap); - float filterLod = textureQueryLod(skyboxMap, direction); + float filterLod = textureQueryLod(skyboxMap, direction).x; // Keep texture filtering LOD as limit to prevent aliasing on specular reflection lod = max(lod, filterLod); return textureLod(skyboxMap, direction, lod); diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 279a010cf4..d96c565b81 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -133,16 +133,19 @@ SurfaceData initSurfaceData(float roughness, vec3 normal, vec3 eyeDir) { SurfaceData surface; surface.eyeDir = eyeDir; surface.normal = normal; - surface.lightDir = vec3(0,0,0); - surface.halfDir = vec3(0,0,0); surface.roughness = mix(0.001, 1.0, roughness); surface.roughness2 = surface.roughness * surface.roughness; surface.roughness4 = surface.roughness2 * surface.roughness2; surface.ndotv = clamp(dot(normal, eyeDir), 0.0, 1.0); + surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv); + + // These values will be set when we know the light direction, in updateSurfaceDataWithLight surface.ndoth = 0.0; surface.ndotl = 0.0; surface.ldoth = 0.0; - surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv); + surface.lightDir = vec3(0,0,1); + surface.halfDir = vec3(0,0,1); + return surface; }