yum/3ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum/3ner
f3918c9
master
1#ifndef __BRDF_INC 2#define __BRDF_INC 3 4#include "LightVolumes.cginc" 5#include "lysenko.cginc" 6#include "math.cginc" 7#include "pema99.cginc" 8#include "pbr.cginc" 9#include "glitter.cginc" 10#include "poi.cginc" 11 12float pow5(float x) { 13 float x2 = x * x; 14 return x2 * x2 * x; 15} 16 17float Fd_Lambertian(float NoL) { 18 return NoL; 19} 20 21// Schlick "An Inexpensive BRDF Model for Physically-based Rendering". 22// Equation 24. 23// f0: Reflectance at normal incidence. Typically around 0.04. 24// f90: Reflectance at grazing incidence. Typically around 1.0. 25float3 F_Schlick(float LoH, float3 f0, float f90) { 26 float term5 = pow5(1.0f - LoH); 27 float3 f90v = float3(f90, f90, f90); 28 return f0 + (f90v - f0) * term5; 29} 30 31// Walter "Microfacet Models for Refraction through Rough Surfaces" 32// Equation 33. 33// In the paper: 34// - m = microsurface normal 35// - n = macrosurface normal 36// - theta_m = angle between micro- & macrosurface normals 37// - alpha = roughness 38// - cos(theta_m) = NoH 39// Per sohcahtoa: 40// tan(theta) = sin(theta) / cos(theta) 41// tan^2(theta) = sin^2(theta) / cos^2(theta) 42// = (1 - cos^2(theta)) / cos^2(theta) 43// = -1 + 1 / cos^2(theta) 44float D_GGX(float roughness, float NoH) { 45 float r2 = roughness * roughness; 46 float NoH2 = NoH * NoH; 47 float NoH4 = NoH2 * NoH2; 48 49 float k = rcp(NoH2) - 1; 50 float r2_plus_k = r2 + k; 51 float denom = NoH4 * r2_plus_k * r2_plus_k; 52 53 //return min(4096, r2 / denom); 54 return r2 / denom; 55} 56 57float D_Estevez(float roughness, float NoH) { 58 float r_rcp = rcp(roughness); 59 float sin_theta = sqrt(1 - NoH * NoH); 60 float D = (2 + r_rcp) * pow(sin_theta, r_rcp) / TAU; 61 62 return D; 63} 64 65// Hammon "PBR Diffuse Lighting for GGX+Smith Microsurfaces" 66// Slide 84. Note that we remove the (4 * NoL * NoV) from the 67// denominator of the specular lobe because of some cancellations. 68// The original, un-optimized equation is: 69// 2 * NoL * NoV / lerp(2 * NoL * NoV, NoL + NoV, roughness) 70float G_GGXSmith(float roughness, float NoL, float NoV) { 71 float denom = 2.0f * lerp(2.0f * NoL * NoV, NoL + NoV, roughness); 72 return rcp(denom); 73} 74 75float L_Estevez(float r, float x) { 76 // Recover constants according to Table 1. 77 float one_minus_r = 1 - r; 78 float interpolator = one_minus_r * one_minus_r; 79 float one_minus_i = 1 - interpolator; 80 float a = interpolator * 25.3245 + one_minus_i * 21.5473; 81 float b = interpolator * 3.32435 + one_minus_i * 3.82987; 82 float c = interpolator * 0.16801 + one_minus_i * 0.19823; 83 float d = interpolator * -1.27393 + one_minus_i * -1.97760; 84 float e = interpolator * -4.85967 + one_minus_i * -4.32054; 85 86 return a / (1 + b*pow(x, c)) + d*x + e; 87} 88 89float Lambda_Estevez_Raw(float cos_theta, float roughness) { 90 // Equation 3 91 return cos_theta < 0.5 92 ? exp(L_Estevez(roughness, cos_theta)) 93 : exp(2 * L_Estevez(roughness, 0.5) - L_Estevez(roughness, 1 - cos_theta)); 94} 95 96float Lambda_Estevez_Softened(float cos_theta, float roughness) { 97 // Equation 4 applies only to the light-side term. 98 float lambda = Lambda_Estevez_Raw(cos_theta, roughness); 99 return pow(lambda, 1 + 2 * pow(1 - cos_theta, 8)); 100} 101 102// Estevez & Kulla "Production Friendly Microfacet Sheen BRDF" 103// Height-correlated Smith: G2 / (4 * NoL * NoV) 104float G_Estevez(float roughness, float NoL, float NoV) { 105 float lambda_l = Lambda_Estevez_Softened(NoL, roughness); 106 float lambda_v = Lambda_Estevez_Raw(NoV, roughness); 107 return 1.0 / ((1.0 + lambda_l + lambda_v) * 4.0 * NoL * NoV); 108} 109 110float4 brdf(v2f i, Pbr pbr, LightData data, bool direct_only, out BrdfData bd) { 111 bd = (BrdfData)0; 112 float3 specular = 0; 113 float3 diffuse = 0; 114 115//#define FURNACE_TEST_DIRECT 116#if defined(FURNACE_TEST_DIRECT) 117 // Create the conditions for the standard BRDF furnace test. 118 // Only applies to the direct lighting stage. The only variable left over is 119 // NoV. 120 f0 = 1; 121 data.direct.color = 1; 122 data.direct.NoL = 1; 123 data.direct.NoH = 1; 124 data.direct.LoH = 1; 125#endif 126 127 // TODO parameterize 128 float f0 = 0.04f; 129 const float f90 = 1.0f; 130 float2 dfg_uv = float2(data.common.NoV, pbr.roughness_perceptual); 131 [branch] 132 if (textureExists(_DFG_LUT)) { 133 bd.ibl_dfg = _DFG_LUT.SampleLevel(bilinear_clamp_s, dfg_uv, 0); 134 } else { 135 bd.ibl_dfg = float4(1, 1, 1, 1); 136 } 137 float3 f0_color = lerp(f0, pbr.albedo.xyz, pbr.metallic); 138 float3 energy_comp = 1.0f + f0_color * (1.0f / (bd.ibl_dfg.xxx + bd.ibl_dfg.yyy) - 1.0f); 139 140#if defined(_CLEARCOAT) 141 const float cc_f0 = 0.04f; 142 float2 cc_dfg_uv = float2(data.common.NoV_cc, pbr.cc_roughness_perceptual); 143 [branch] 144 if (textureExists(_DFG_LUT)) { 145 bd.ibl_dfg_cc = _DFG_LUT.SampleLevel(bilinear_clamp_s, cc_dfg_uv, 0); 146 } else { 147 bd.ibl_dfg_cc = float4(1, 1, 1, 1); 148 } 149 float3 cc_f0_color = lerp(cc_f0, pbr.albedo.xyz, pbr.metallic); 150 float3 cc_energy_comp = 1.0f + cc_f0_color * (1.0f / (bd.ibl_dfg_cc.xxx + bd.ibl_dfg_cc.yyy) - 1.0f); 151#endif 152 153 // Direct 154 { 155 float3 remainder = 1.0f; 156 157#if defined(_CLEARCOAT) 158 bd.direct_f_cc = F_Schlick(data.direct.LoH, cc_f0, f90); 159 bd.direct_d_cc = D_GGX(pbr.cc_roughness, data.direct.NoH_cc); 160 bd.direct_g_cc = G_GGXSmith(pbr.cc_roughness, data.direct.NoL_cc, data.common.NoV_cc); 161 float DFGcc = bd.direct_f_cc * bd.direct_d_cc * bd.direct_g_cc; 162 float3 direct_specular_cc = DFGcc * data.direct.color * data.direct.NoL_cc * pbr.cc_strength; 163 direct_specular_cc *= cc_energy_comp; 164 direct_specular_cc *= remainder; 165 direct_specular_cc = max(0, direct_specular_cc); 166 specular += direct_specular_cc; 167 remainder *= saturate(1.0f - bd.direct_f_cc * pbr.cc_strength); 168#endif 169 170#if defined(_CLOTH) 171 float3 cloth_f0 = _Cloth_Sheen.rgb; 172 bd.direct_f = F_Schlick(data.direct.LoH, cloth_f0, f90); 173 bd.direct_d = D_Estevez(pbr.roughness, data.direct.NoH); 174 bd.direct_g = G_Estevez(pbr.roughness, data.direct.NoL, data.common.NoV); 175 176 float4 cloth_dfg_i = bd.ibl_dfg; 177 [branch] 178 if (textureExists(_DFG_LUT)) { 179 float2 cloth_direct_uv = float2(data.direct.NoL, pbr.roughness_perceptual); 180 cloth_dfg_i = _DFG_LUT.SampleLevel(bilinear_clamp_s, cloth_direct_uv, 0); 181 } 182 float3 cloth_alpha_o = cloth_f0 * bd.ibl_dfg.zzz; 183 float3 cloth_alpha_i = cloth_f0 * cloth_dfg_i.www; 184 float cloth_base_scale = luminance(min(1.0f - cloth_alpha_i, 1.0f - cloth_alpha_o)); 185 186 float3 direct_specular_cloth = (bd.direct_d * bd.direct_g) * bd.direct_f; 187 direct_specular_cloth *= data.direct.color * data.direct.NoL; 188 direct_specular_cloth *= remainder; 189 specular += direct_specular_cloth; 190 remainder *= saturate(cloth_base_scale); 191 /* 192 float Fd = Fd_Lambertian(data.direct.NoL) / PI; 193 float3 direct_diffuse = Fd * pbr.albedo.xyz * data.direct.color; 194 direct_diffuse *= remainder; 195 direct_diffuse = max(0, direct_diffuse); 196 diffuse += direct_diffuse; 197 */ 198#endif // _CLOTH 199 200#if defined(_GLITTER) 201 float3 direct_f_glitter = F_Schlick(data.direct.LoH, 0.15f, 1.0f); 202 float direct_g_glitter = G_GGXSmith(pbr.roughness, data.direct.NoL, data.common.NoV); 203 float3 direct_specular_glitter = (data.glitter.direct_D * direct_g_glitter) 204 * direct_f_glitter * data.direct.color * data.direct.NoL 205 * _Glitter_Tint; 206 // No spec ao for glitter, please. 207 direct_specular_glitter *= remainder; 208#if defined(_GLITTER_MASK) 209 float glitter_mask = _Glitter_Mask.Sample(bilinear_clamp_s, i.uv01.xy).r; 210 direct_specular_glitter *= glitter_mask; 211#endif 212 specular += direct_specular_glitter; 213#endif 214 215 bd.direct_g = G_GGXSmith(pbr.roughness, data.direct.NoL, data.common.NoV); 216 bd.direct_f = F_Schlick(data.direct.LoH, f0_color, f90); 217 bd.direct_d = D_GGX(pbr.roughness, data.direct.NoH); 218 219 float3 direct_specular = (bd.direct_d * bd.direct_g) * bd.direct_f; 220 direct_specular *= data.direct.color * data.direct.NoL; 221 direct_specular *= energy_comp; 222 direct_specular *= remainder; 223 specular += direct_specular * data.common.spec_ao; 224 225#if defined(F_OREN_NAYAR) 226 float Fd = Fd_OrenNayar(pbr.roughness, data.common.NoV, data.direct.NoL, data.direct.LoV); 227#else 228 float Fd = Fd_Lambertian(data.direct.NoL); 229#endif 230 float3 direct_diffuse = Fd * (1.0f - pbr.metallic) * pbr.albedo.xyz * data.direct.color; 231 direct_diffuse *= remainder; 232 direct_diffuse = max(0, direct_diffuse); 233 diffuse += direct_diffuse; 234 } 235 236 // Indirect 237#if !defined(FURNACE_TEST_DIRECT) && (defined(FORWARD_BASE_PASS) || defined(OUTLINES_PASS)) 238 [branch] 239 if (!direct_only) 240 { 241 float3 remainder = 1.0f; 242#if defined(_CLEARCOAT) 243 float3 cc_specular_dfg = bd.ibl_dfg_cc.xxx * cc_f0_color + bd.ibl_dfg_cc.yyy; // filament 5.3.4.6 244 float3 cc_indirect_specular = data.indirect.specular_cc * cc_specular_dfg; 245 cc_indirect_specular *= cc_energy_comp; 246 specular += cc_indirect_specular * data.common.spec_ao; 247 remainder -= cc_specular_dfg; 248#endif 249 250#if defined(_CLOTH) 251 float3 specular_dfg = _Cloth_Sheen.rgb * bd.ibl_dfg.zzz; 252 float3 indirect_specular = data.indirect.specular * specular_dfg; 253 specular += indirect_specular * remainder * data.common.spec_ao; 254 remainder *= saturate(1.0f - specular_dfg); 255 256 float3 indirect_diffuse = pbr.albedo.xyz * data.indirect.diffuse; 257 diffuse += indirect_diffuse * remainder; 258#else 259#if defined(_GLITTER) 260 float3 indirect_f_glitter = F_Schlick(data.glitter.indirect_LoH, 0.15f, 1.0f); 261 float indirect_g_glitter = G_GGXSmith(pbr.roughness, data.glitter.indirect_NoL, data.common.NoV); 262 float3 indirect_specular_glitter = (data.glitter.indirect_D * indirect_g_glitter) 263 * indirect_f_glitter * max(0, data.indirect.L00) * data.glitter.indirect_NoL 264 * _Glitter_Tint; 265 // No spec ao for glitter, please. 266#if defined(_GLITTER_MASK) 267 float glitter_mask = _Glitter_Mask.Sample(bilinear_clamp_s, i.uv01.xy).r; 268 indirect_specular_glitter *= glitter_mask; 269#endif 270 specular += indirect_specular_glitter * remainder; 271 remainder *= saturate(1 - indirect_specular_glitter * remainder); 272#endif 273 274 float3 specular_dfg = bd.ibl_dfg.xxx * f0_color + bd.ibl_dfg.yyy; // filament 5.3.4.6 275 float3 indirect_specular = data.indirect.specular * specular_dfg; 276 277 indirect_specular *= energy_comp; 278 specular += indirect_specular * remainder * data.common.spec_ao; 279 280 float3 indirect_diffuse = pbr.albedo.xyz * data.indirect.diffuse * (1.0 - pbr.metallic); 281 diffuse += indirect_diffuse * remainder; 282#endif 283 } 284#endif 285 286#if defined(FORWARD_BASE_PASS) 287 [branch] 288 if (!direct_only) 289 { 290 [branch] 291 if (_UdonLightVolumeEnabled) { 292 float3 light_volume_specular = LightVolumeSpecular(pbr.albedo.xyz, pbr.smoothness, 293 pbr.metallic, pbr.normal, data.common.V, data.indirect.L00, 294 data.indirect.L01r, data.indirect.L01g, data.indirect.L01b); 295 296#if defined(_BRIGHTNESS_CLAMP) 297 float3 light_volume_specular_hsv = RGBtoHSV(light_volume_specular); 298 light_volume_specular_hsv[2] = clamp(light_volume_specular_hsv[2], 0, _Brightness_Clamp_Max); 299 light_volume_specular = HSVtoRGB(light_volume_specular_hsv); 300#endif 301#if defined(_BRIGHTNESS_MULTIPLIER) 302 light_volume_specular *= _Brightness_Multiplier; 303#endif 304 305 specular += light_volume_specular; 306 } 307 } 308#endif 309 310 diffuse *= data.common.ao; 311 312#if (defined(_EMISSIONS) || defined(_LETTER_GRID)) && defined(FORWARD_BASE_PASS) 313 float3 emission = direct_only ? 0 : pbr.emission; 314#else 315 float3 emission = 0; 316#endif 317 float4 lit = float4(diffuse + specular + emission, pbr.albedo.a); 318 // Scale albedo by alpha. 319 return float4(lit.rgb * lit.a, lit.a); 320} 321 322float4 brdf(v2f i, Pbr pbr, LightData data, out BrdfData bd) { 323 return brdf(i, pbr, data, false, bd); 324} 325 326#endif // __BRDF_INC