diff options
| author | yum <yum.food.vr@gmail.com> | 2025-02-11 16:54:10 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2025-02-11 16:54:10 -0800 |
| commit | 20c32dcb055058b770062b7e9dd07f79d33a8caa (patch) | |
| tree | a712206014334ab41a012c2cc27086c5f0591c45 /yum_brdf.cginc | |
| parent | 2dcf39113fab3aac8fe280f3b0ead9cf87fa958d (diff) | |
Add code
Implement basic diffuse and specular BRDF based heavily on silent's
filamented shader.
Diffstat (limited to 'yum_brdf.cginc')
| -rw-r--r-- | yum_brdf.cginc | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/yum_brdf.cginc b/yum_brdf.cginc new file mode 100644 index 0000000..204a276 --- /dev/null +++ b/yum_brdf.cginc @@ -0,0 +1,89 @@ +#ifndef __YUM_BRDF_INC
+#define __YUM_BRDF_INC
+
+#include "UnityCG.cginc"
+#include "UnityStandardConfig.cginc"
+#include "UnityLightingCommon.cginc"
+
+#include "filamented.cginc"
+#include "math.cginc"
+#include "yum_pbr.cginc"
+#include "yum_lighting.cginc"
+
+// See "Physically Based Shading at Disney" by Burley et. al.
+// This is from page 14.
+// From that paper, we have:
+// * theta_v (aka arccos(NoV)): angle between view and normal
+// * theta_h (aka arccos(NoH)): angle between normal and half vector
+// * theta_l (aka arccos(NoL)): angle between light and normal
+// * theta_d (aka arccos(LoH)): angle between light and half vector
+float DisneyDiffuse(float roughness,
+ float LoH, float NoL, float NoV, float f90) {
+ const float f_d =
+ (1 + (f90 - 1) * pow5(1 - NoL)) *
+ (1 + (f90 - 1) * pow5(1 - NoV));
+ return f_d;
+}
+
+float3 diffuseLobe(float3 albedo, float roughness, float LoH, float NoL, float NoV, float f90)
+{
+ return albedo * DisneyDiffuse(roughness, LoH, NoL, NoV, f90);
+}
+
+float3 specularLobe(YumPbr pbr, float f0,
+ float3 h, float LoH, float NoH, float NoV, float NoL)
+{
+ const float3 F = F_Schlick(f0, LoH);
+
+ // Normal distribution function
+ float D = D_GGX(pbr.roughness, NoH, h);
+ // Geometric shadowing
+ float V = V_SmithGGXCorrelated_Fast(pbr.roughness, NoV, NoL);
+
+ return (D * V) * F * PI;
+}
+
+float4 YumBRDF(v2f i, const YumLighting light, YumPbr pbr) {
+ const float3 h = normalize(light.view_dir + light.dir);
+ const float LoH = saturate(dot(light.dir, h));
+ const float NoL = dot(pbr.normal, light.dir);
+ const float NoV = max(1E-4, dot(pbr.normal, light.view_dir));
+ const float NoH = saturate(dot(pbr.normal, h));
+ const float VoL = saturate(dot(light.view_dir, light.dir));
+ const float f90 = 0.5 + 2 * pbr.roughness * LoH * LoH;
+
+ // Typical values for different materials listed here:
+ // https://google.github.io/filament/Filament.html#toc4.8.3.2
+ // TODO expose an enum for different types of materials
+ const float reflectance = _reflectance;
+ // f0 = amount of light reflected back when viewing surface at a right angle
+ // I think the way that f0 is calculated in the comment below is correct, but
+ // the other way is how filamented works, so I'm going with that for now.
+ //const float3 f0 = lerp(0.16 * reflectance * reflectance, pbr.albedo, pbr.metallic);
+ const float3 f0 = reflectance * (1.0 - pbr.metallic) + pbr.albedo * pbr.metallic;
+ const float3 dfg = PrefilteredDFG_LUT(pbr.roughness_perceptual, NoV);
+ const float3 E = specularDFG(dfg, f0);
+ const float3 energy_compensation = energyCompensation(dfg, f0);
+
+ float3 direct;
+ {
+
+ float3 Fd = diffuseLobe(pbr.albedo, pbr.roughness_perceptual, LoH, NoL, NoV, f90);
+ Fd *= (1.0 - pbr.metallic);
+ float3 Fr = specularLobe(pbr, f0, h, LoH, NoH, NoV, NoL);
+ float3 color = Fd + Fr * energy_compensation;
+
+ direct = color * light.direct * saturate(NoL);
+ }
+
+ float3 indirect;
+ {
+ float3 Fr = E * light.specular * energy_compensation;
+ float3 Fd = pbr.albedo * light.diffuse * (1.0 - E) * (1.0 - pbr.metallic) * pbr.ao;
+ indirect = Fr + Fd;
+ }
+
+ return float4(direct + indirect, 1);
+}
+
+#endif // __YUM_BRDF_INC
|
