summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--brdf.cginc91
-rw-r--r--filamented.cginc33
-rw-r--r--pbr.cginc3
4 files changed, 78 insertions, 50 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c6bb293
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.meta
diff --git a/brdf.cginc b/brdf.cginc
index ea21b49..1504ef0 100644
--- a/brdf.cginc
+++ b/brdf.cginc
@@ -17,36 +17,91 @@ float F_Schlick(float LoH, float f0, float f90) {
return f0 + (f90 - f0) * term5;
}
+// Walter "Microfacet Models for Refraction through Rough Surfaces"
+// Equation 33.
+// In the paper:
+// - m = microsurface normal
+// - n = macrosurface normal
+// - theta_m = angle between micro- & macrosurface normals
+// - alpha = roughness
+// - cos(theta_m) = NoH
+// Per sohcahtoa:
+// tan(theta) = sin(theta) / cos(theta)
+// tan^2(theta) = sin^2(theta) / cos^2(theta)
+// = (1 - cos^2(theta)) / cos^2(theta)
+// = -1 + 1 / cos^2(theta)
+float D_GGX(float roughness, float NoH) {
+ float r2 = roughness * roughness;
+ float NoH2 = NoH * NoH;
+ float NoH4 = NoH2 * NoH2;
+
+ float k = rcp(NoH2) - 1;
+ float r2_plus_k = r2 + k;
+ // Not sure why, but not using the factor of PI here makes the specular match
+ // the Unity standard much more closely. Maybe the author was just folding
+ // the 4.0 (historically used to be a PI) into the GGX calculation?
+ float denom = NoH4 * r2_plus_k * r2_plus_k;
+
+ return r2 / denom;
+}
+
+// Hammon "PBR Diffuse Lighting for GGX+Smith Microsurfaces"
+// Slide 84. Note that we remove the (4 * NoL * NoV) from the
+// denominator of the specular lobe because of some cancellations.
+// The original, un-optimized equation is:
+// 2 * NoL * NoV / lerp(2 * NoL * NoV, NoL + NoV, roughness)
+float V_GGXSmith(float roughness, float NoL, float NoV) {
+ float denom = 2.0f * lerp(2.0f * NoL * NoV, NoL + NoV, roughness);
+ return rcp(denom);
+}
+
float4 brdf(Pbr pbr, LightData data) {
float3 specular = 0;
float3 diffuse = 0;
+ float f0 = 0.04f;
+ const float f90 = 1.0f;
+
+//#define FURNACE_TEST_DIRECT
+#if defined(FURNACE_TEST_DIRECT)
+ // Create the conditions for the standard BRDF furnace test.
+ // Only applies to the direct lighting stage. The only variable left over is
+ // NoV.
+ f0 = 1;
+ data.direct.color = 1;
+ data.direct.NoL = 1;
+ data.direct.NoH = 1;
+ data.direct.LoH = 1;
+ data.direct.LoV = 1;
+#endif
+
// Direct
if (true) {
- float F = F_Schlick(data.direct.LoH, 0.04f, 1.0f);
- float D = D_GGX(pbr.roughness, data.direct.NoH, data.direct.H);
- float V = V_SmithGGXCorrelated_Fast(pbr.roughness, data.common.NoV, data.direct.NoL);
-
- float denom = 4.0f * data.common.NoV * data.direct.NoL;
- float FDV = denom > _BRDF_Specular_Min_Denom ? F * D * V / denom : 0.0f;
- specular += FDV * data.direct.color * data.direct.NoL;
-
- float Fd = Fd_OrenNayar(pbr.roughness, data.common.NoV, data.direct.NoL, data.direct.LoV);
- float3 remainder = (1.0f - F);
- diffuse += (Fd / PI) * remainder * pbr.albedo.xyz * data.direct.color;
- remainder *= (1.0f - (Fd / PI) * pbr.albedo);
+ float F = F_Schlick(data.direct.LoH, f0, f90);
+ float D = D_GGX(pbr.roughness, data.direct.NoH);
+ float G = V_GGXSmith(pbr.roughness, data.direct.NoL, data.common.NoV);
+
+ float FDG = F * D * G;
+ float3 direct_specular = FDG * data.direct.color * data.direct.NoL * lerp(1.0f, pbr.albedo.xyz, pbr.metallic);
+ specular += direct_specular;
+
+ float3 remainder = (1.0f - direct_specular);
+ float Fd = Fd_OrenNayar(pbr.roughness, data.common.NoV, data.direct.NoL, data.direct.LoV) / PI;
+ float3 direct_diffuse = Fd * remainder * (1.0f - pbr.metallic) * pbr.albedo.xyz * data.direct.color;
+ diffuse += direct_diffuse;
}
// Indirect
- if (true) {
- float F = F_Schlick(data.indirect.LoH, 0.04f, 1.0f);
+ if (false) {
+ float F = F_Schlick(data.indirect.LoH, f0, f90);
- specular += F * data.indirect.specular;
+ float3 indirect_specular = F * data.indirect.specular;
+ specular += indirect_specular;
float Fd = 1.0f; // Lambertian divide is baked into SH
- float3 remainder = (1.0f - F);
- diffuse += Fd * remainder * pbr.albedo.xyz * data.indirect.diffuse;
- remainder *= (1.0f - Fd * pbr.albedo);
+ float3 remainder = (1.0f - indirect_specular);
+ float3 indirect_diffuse = Fd * remainder * pbr.albedo.xyz * data.indirect.diffuse;
+ diffuse += indirect_diffuse;
}
return float4(diffuse + specular, 1);
diff --git a/filamented.cginc b/filamented.cginc
index 0e27bbc..fb019cb 100644
--- a/filamented.cginc
+++ b/filamented.cginc
@@ -213,37 +213,6 @@
#include "UnityCG.cginc"
#include "UnityImageBasedLightingMinimal.cginc"
-#define MIN_PERCEPTUAL_ROUGHNESS 0.045f
-#define MIN_ROUGHNESS 0.002025f
-
-float D_GGX(float roughness, float NoH, const float3 h) {
- // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
-
- // In mediump, there are two problems computing 1.0 - NoH^2
- // 1) 1.0 - NoH^2 suffers floating point cancellation when NoH^2 is close to 1 (highlights)
- // 2) NoH doesn't have enough precision around 1.0
- // Both problem can be fixed by computing 1-NoH^2 in highp and providing NoH in highp as well
-
- // However, we can do better using Lagrange's identity:
- // ||a x b||^2 = ||a||^2 ||b||^2 - (a . b)^2
- // since N and H are unit vectors: ||N x H||^2 = 1.0 - NoH^2
- // This computes 1.0 - NoH^2 directly (which is close to zero in the highlights and has
- // enough precision).
- // Overall this yields better performance, keeping all computations in mediump
- // Not available without reworking to pass NxH to the function
- float oneMinusNoHSquared = 1.0 - NoH * NoH;
- float a = NoH * roughness;
- float k = roughness / (oneMinusNoHSquared + a * a);
- float d = k * k * (1.0 / PI);
- return d;
-}
-
-float V_SmithGGXCorrelated_Fast(float roughness, float NoV, float NoL) {
- // Hammon 2017, "PBR Diffuse Lighting for GGX+Smith Microsurfaces"
- float v = 0.5 / lerp(2.0 * NoL * NoV, NoL + NoV, roughness);
- return v;
-}
-
float normalFiltering(float perceptualRoughness, const float3 worldNormal) {
// Kaplanyan 2016, "Stable specular highlights"
// Tokuyoshi 2017, "Error Reduction and Simplification for Shading Anti-Aliasing"
@@ -275,7 +244,7 @@ half3 Unity_GlossyEnvironment_local (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_G
// Workaround for issue where objects are blurrier than they should be
// due to specular AA.
float roughnessAdjustment = 1-perceptualRoughness;
- roughnessAdjustment = MIN_PERCEPTUAL_ROUGHNESS * roughnessAdjustment * roughnessAdjustment;
+ roughnessAdjustment = 1e-6f * roughnessAdjustment * roughnessAdjustment;
perceptualRoughness = perceptualRoughness - roughnessAdjustment;
// Unity derivation
diff --git a/pbr.cginc b/pbr.cginc
index b297a0f..7167929 100644
--- a/pbr.cginc
+++ b/pbr.cginc
@@ -14,6 +14,9 @@ struct Pbr {
float metallic;
};
+#define MIN_PERCEPTUAL_ROUGHNESS 5e-2f
+#define MIN_ROUGHNESS 2e-3f
+
// TODO consider normal filtering like filamented
void propagateSmoothness(inout Pbr pbr) {
pbr.roughness_perceptual = max(MIN_PERCEPTUAL_ROUGHNESS, 1.0f - pbr.smoothness);