yum/3ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum/3ner

yumClanker vertex light supportf3918c9

master
11.4 KiB326 linesraw
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