summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2025-11-11 17:26:35 -0800
committeryum <yum.food.vr@gmail.com>2025-11-11 17:26:35 -0800
commitcb101b9bd1f0e9dbfcfe45d114d1a63af3e85d82 (patch)
tree117733c30b5888ad24143e09cd0afa3510048cc3
parentb11eecd7e4bf9cd5d1cdf95a5e995a39c59ca4bf (diff)
fix energy conservation issues; notably smooth=0 metallic=1 matches filamented now
-rw-r--r--brdf.cginc22
-rw-r--r--filamented.cginc4
-rw-r--r--lighting.cginc5
3 files changed, 18 insertions, 13 deletions
diff --git a/brdf.cginc b/brdf.cginc
index 3a0db4c..804cfe9 100644
--- a/brdf.cginc
+++ b/brdf.cginc
@@ -136,7 +136,7 @@ float4 brdf(Pbr pbr, LightData data) {
data.direct.LoH = 1;
#endif
- float2 dfg_uv = float2(pbr.roughness, data.common.NoV);
+ float2 dfg_uv = float2(data.common.NoV, pbr.roughness_perceptual);
float3 dfg;
[branch]
if (textureExists(_DFG_LUT)) {
@@ -147,7 +147,8 @@ float4 brdf(Pbr pbr, LightData data) {
#if defined(_CLEARCOAT)
const float cc_f0 = 0.04f;
- float2 cc_dfg_uv = float2(pbr.cc_roughness, data.common.NoV_cc);
+ float cc_perceptual_roughness = saturate(sqrt(pbr.cc_roughness));
+ float2 cc_dfg_uv = float2(data.common.NoV_cc, cc_perceptual_roughness);
float3 cc_dfg;
[branch]
if (textureExists(_DFG_LUT)) {
@@ -159,7 +160,9 @@ float4 brdf(Pbr pbr, LightData data) {
float cc_energy_comp = 1.0f + cc_f0 * (rcp(cc_Ess) - 1.0f);
#endif
- float3 F0_color = lerp(float3(f0, f0, f0), pbr.albedo.xyz, pbr.metallic);
+ float3 f0_color = lerp(f0, pbr.albedo.xyz, pbr.metallic);
+ float Ess = max(dfg.y, 1e-4f);
+ float3 energy_comp = 1.0f + f0_color * (rcp(Ess) - 1.0f);
// Direct
{
@@ -197,9 +200,6 @@ float4 brdf(Pbr pbr, LightData data) {
FDG = FDG * dfg.x + dfg.y;
float3 direct_specular = FDG * remainder * data.direct.color * data.direct.NoL * lerp(1.0f, pbr.albedo.xyz, pbr.metallic);
- float Ess = max(dfg.y, 1e-4f);
- float invEss = rcp(Ess);
- float3 energy_comp = 1.0f + F0_color * (invEss - 1.0f);
direct_specular *= energy_comp;
direct_specular = max(0, direct_specular);
specular += direct_specular;
@@ -237,13 +237,11 @@ float4 brdf(Pbr pbr, LightData data) {
// Standard PBR IBL using split-sum approximation
// Specular lobe
float3 f0_spec = lerp(f0, pbr.albedo.xyz, pbr.metallic);
- float3 ibl_specular_reflectance = f0_spec * dfg.x + dfg.y;
- float3 indirect_specular = data.indirect.specular * ibl_specular_reflectance;
- // surface reduction term lifted from here:
- // UnityStandardBRDF.cginc :: BRDF1_Unity_PBS
- float surface_reduction = 1.0f / (pbr.roughness * pbr.roughness + 1.0f);
+ float3 ibl_specular_reflectance = lerp(dfg.xxx, dfg.yyy, f0_spec);
+ float3 indirect_specular = data.indirect.specular * ibl_specular_reflectance * energy_comp;
const float F = F_Schlick(data.indirect.NoL, f0_spec, f90);
- const float3 is_conserved = surface_reduction * indirect_specular * F;
+ const float3 is_conserved = indirect_specular * F;
+
specular += is_conserved;
remainder = saturate(remainder - is_conserved);
diff --git a/filamented.cginc b/filamented.cginc
index f6bd67a..62ec4c3 100644
--- a/filamented.cginc
+++ b/filamented.cginc
@@ -254,6 +254,10 @@ UnityGIInput InitialiseUnityGIInput(float3 worldPos, float3 view_dir)
return d;
}
+float3 getSpecularDominantDirection(const float3 n, const float3 r, float roughness) {
+ return lerp(r, n, roughness * roughness);
+}
+
inline half3 UnityGI_prefilteredRadiance(const UnityGIInput data, const float perceptualRoughness, const float3 r)
{
half3 specular;
diff --git a/lighting.cginc b/lighting.cginc
index 4e5a61b..5f66b80 100644
--- a/lighting.cginc
+++ b/lighting.cginc
@@ -188,7 +188,10 @@ void GetLighting(v2f i, Pbr pbr, out LightData data) {
data.direct.color = lightColorIntensity.rgb * lightColorIntensity.w;
// Indirect lighting
- data.indirect.dir = -reflect(data.common.V, pbr.normal);
+ float3 reflect_dir = -reflect(data.common.V, pbr.normal);
+ float3 dominant_dir = getSpecularDominantDirection(pbr.normal, reflect_dir, pbr.roughness);
+
+ data.indirect.dir = normalize(dominant_dir);
data.indirect.H = normalize(data.common.V + data.indirect.dir);
data.indirect.NoL = saturate(dot(pbr.normal, data.indirect.dir));
data.indirect.NoH = saturate(dot(pbr.normal, data.indirect.H));