yum/3ner

A toon shader for Unity's BIRP.

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

yumClanker vertex light supportf3918c9

master
15.5 KiB434 linesraw
1#ifndef __LIGHTING_INC
2#define __LIGHTING_INC
3
4#include "UnityCG.cginc"
5#include "AutoLight.cginc"
6#include "UnityPBSLighting.cginc"
7#include "UnityLightingCommon.cginc"
8#include "UnityStandardCoreMinimal.cginc"
9
10#include "burley.cginc"
11#include "data.cginc"
12#include "features.cginc"
13#include "filamented.cginc"
14#include "interpolators.cginc"
15#include "LightVolumes.cginc"
16#include "glitter.cginc"
17#include "poi.cginc"
18
19float3 getDirectLightDirection(v2f i) {
20#if defined(POINT) || defined(POINT_COOKIE) || defined(SPOT)
21	return normalize((_WorldSpaceLightPos0 - i.worldPos).xyz);
22#else
23	return _WorldSpaceLightPos0;
24#endif
25}
26
27float getShadowAttenuation(v2f i)
28{
29  UNITY_LIGHT_ATTENUATION(attenuation, i, i.worldPos);
30	return attenuation;
31}
32
33float4 getDirectLightColorIntensity() {
34	// Properly separate light color from intensity like filamented
35	if (_LightColor0.w <= 0) return float4(0, 0, 0, 0);
36	return float4(_LightColor0.xyz, _LightColor0.w);
37}
38
39#if defined(VERTEXLIGHT_ON) && (defined(FORWARD_BASE_PASS) || defined(OUTLINES_PASS))
40// Mirrors can disable pixel lights, which makes Unity expose up to four point
41// lights through the vertex-light uniforms instead of drawing ForwardAdd.
42// Evaluate them per fragment so they use the same normals and BRDF as pixel
43// lights. Demoted spot lights are supplied by Unity as point lights here.
44void GetVertexLighting(v2f i, Pbr pbr, uint light_index, inout LightData data) {
45  float3 light_position = float3(
46      unity_4LightPosX0[light_index],
47      unity_4LightPosY0[light_index],
48      unity_4LightPosZ0[light_index]);
49  float3 to_light = light_position - i.worldPos;
50  float distance_squared = max(dot(to_light, to_light), 1e-6f);
51  float3 light_direction = to_light * rsqrt(distance_squared);
52
53  // Match Unity/Poiyomi's vertex-light range fade rather than allowing the
54  // reciprocal attenuation tail to illuminate indefinitely.
55  float attenuation_squared = unity_4LightAtten0[light_index];
56  float attenuation = rcp(1.0f + distance_squared * attenuation_squared);
57  float range_fade = saturate(1.0f - distance_squared * attenuation_squared / 25.0f);
58  attenuation = min(attenuation, range_fade * range_fade);
59
60  data.direct.dir = light_direction;
61  data.direct.H = normalize(data.common.V + light_direction);
62#if defined(_WRAPPED_LIGHTING)
63  data.direct.NoL = max(1e-4, wrapNoL(saturate(dot(pbr.normal, light_direction)), _Wrapped_Lighting_Amount));
64#else
65  data.direct.NoL = max(1e-4, dot(pbr.normal, light_direction));
66#endif
67  data.direct.NoH = max(1e-4, dot(pbr.normal, data.direct.H));
68  data.direct.LoH = max(1e-4, dot(light_direction, data.direct.H));
69#if defined(_CLEARCOAT)
70  data.direct.NoH_cc = max(1e-4, dot(pbr.cc_normal, data.direct.H));
71  data.direct.NoL_cc = max(1e-4, dot(pbr.cc_normal, light_direction));
72#endif
73  float direct_LoV = dot(light_direction, data.common.V);
74  data.direct.LoV = max(1e-4, direct_LoV);
75  data.direct.double_LoV = max(1e-4, 2.0f * direct_LoV * direct_LoV - 1.0f);
76  data.direct.color = unity_LightColor[light_index].rgb * attenuation;
77
78#if defined(_BRIGHTNESS_CLAMP)
79  float3 direct_hsv = RGBtoHSV(data.direct.color);
80  direct_hsv[2] = clamp(direct_hsv[2], _Brightness_Clamp_Min, _Brightness_Clamp_Max);
81  data.direct.color = HSVtoRGB(direct_hsv);
82#endif
83#if defined(_BRIGHTNESS_MULTIPLIER)
84  data.direct.color *= _Brightness_Multiplier;
85#endif
86
87#if defined(_GLITTER)
88  float2 glitter_uv = UV_SCOFF(i, _Glitter_Mask_ST, _Glitter_UV_Channel);
89  float2x2 glitter_uv_J = uv_ellipsoid(transpose(float2x2(ddx(glitter_uv), ddy(glitter_uv))));
90#if defined(_GLITTER_NORMAL_OVERRIDE)
91  float2 glitter_normal_uv = UV_SCOFF(i, _Glitter_Normal_Override_ST,
92      _Glitter_UV_Channel);
93  float3 glitter_normal = _Glitter_Normal_Override.Sample(
94      bilinear_clamp_s, glitter_normal_uv).xyz * 2 - 1;
95  glitter_normal = glitter_normal.xzy * float3(-1, 1, -1);
96  glitter_normal = UnityObjectToWorldNormal(glitter_normal);
97  float3x3 glitter_tbn = tbn_from_normal_tangent(glitter_normal, i.tangent);
98#else
99  float3x3 glitter_tbn = pbr.tbn;
100#endif
101  float3 direct_H_tangent = mul(data.direct.H, transpose(glitter_tbn));
102  float3 direct_micro_normal;
103#if defined(_GLITTER_BASE_ROUGHNESS_OVERRIDE)
104  float glitter_roughness = _Glitter_Base_Roughness_Override;
105#else
106  float glitter_roughness = pbr.roughness;
107#endif
108  data.glitter.direct_D = D_Kemppinen(
109      direct_H_tangent, glitter_roughness, _Glitter_Roughness,
110      _Glitter_Angular_Cells, glitter_uv, glitter_uv_J,
111      GLITTER_REFERENCE_N * GLITTER_POPULATION_SCALE, _Glitter_Amount,
112      _Glitter_Filter_Size, direct_micro_normal);
113#endif
114}
115#endif
116
117float3 getIndirectSpecular(v2f i, float perceptual_roughness, float3 view_dir, float3 reflect_dir, float3 indirect_diffuse) {
118  UnityGIInput data = InitialiseUnityGIInput(i.worldPos, view_dir);
119  float3 env_refl = UnityGI_prefilteredRadiance(data, perceptual_roughness, reflect_dir);
120
121  return env_refl;
122}
123
124float3 getAverageSHDirection(float3 L1r, float3 L1g, float3 L1b, float3 fallback_dir) {
125  float3 raw_dir = L1r + L1g + L1b;
126  float raw_dir_len = length(raw_dir);
127  if (abs(raw_dir_len) < 1e-3) {
128    return fallback_dir;
129  }
130  return raw_dir / raw_dir_len;
131}
132
133// Geomerics SH evaluation
134// https://community.arm.com/cfs-file/__key/telligent-evolution-components-attachments/01-2066-00-00-00-01-27-70/Simplifying_2D00_Spherical_2D00_Harmonics_2D00_for_2D00_Lighting.pdf
135float shEvaluateDiffuseL1Geomerics(float L0, float3 L1, float3 n) {
136  // average energy
137  float R0 = max(L0, 0);
138
139  // avg direction of incoming light
140  float3 R1 = 0.5f * L1;
141
142  // directional brightness
143  float lenR1 = length(R1);
144
145  // linear angle between normal and direction 0-1
146  float q = dot(normalize(R1), n) * 0.5 + 0.5;
147  q = saturate(q);
148
149  // power for q
150  // lerps from 1 (linear) to 3 (cubic) based on directionality
151  float p = 1.0f + 2.0f * lenR1 / R0;
152
153  // dynamic range constant
154  // should vary between 4 (highly directional) and 0 (ambient)
155  float a = (1.0f - lenR1 / R0) / (1.0f + lenR1 / R0);
156
157  return R0 * (a + (1.0f - a) * (p + 1.0f) * pow(q, p));
158}
159
160float3 yumSH9(float4 n, float3 worldPos, inout LightIndirect light) {
161  [branch]
162  if (_UdonLightVolumeEnabled) {
163    LightVolumeSH(worldPos, light.L00, light.L01r, light.L01g, light.L01b);
164    return light.L00 + float3(
165        dot(light.L01r, n.xyz),
166        dot(light.L01g, n.xyz),
167        dot(light.L01b, n.xyz));
168  }
169
170  // Unity gives us the first three bands (L0-L2) of SH coefficients as follows:
171  //   unity_SHA*.w:   L0 coefficients
172  //   unity_SHA*.xyz: L1 coefficients
173  //   unity_SHB*:     first four of the L2 coefficients
174  //   unity_SHC:      last L2 coefficient
175
176  // Equation 13 from "An Efficient Representation for Irradiance Environment
177  // Maps" by Ramamoorthi and Hanrahan. Normalization constants have been
178  // premultiplied by Unity into the coefficient buffers.
179  //
180  // L0+L1: dot4 per channel (n.w=1 picks up the L0 term from SHA*.w)
181  // L2: four quadratic terms packed into vB via swizzle multiply, plus L22
182  float3 L0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);
183  float3 L1 = float3(dot(unity_SHAr, n.xyz), dot(unity_SHAg, n.xyz), dot(unity_SHAb, n.xyz));
184  float4 vB = n.xyzz * n.yzzx;
185  float3 L2 = float3(dot(unity_SHBr, vB), dot(unity_SHBg, vB), dot(unity_SHBb, vB))
186            + unity_SHC * (n.x * n.x - n.y * n.y);
187
188#if defined(_WRAPPED_LIGHTING)
189  // Original coefficients: 1, 2/3, 1/4.
190  // Wrapped coefficients: 1, (2-w)/3, ((1-w)^2)/4.
191
192  // Setting w=0, the l1 band is:
193  //   (2-w)/3 = 2/3
194  //   2-w = 2
195  //   1-w/2 = 1
196  float wrap_amount = _Wrapped_Lighting_Amount;
197  float l1_wrap = 1.0f - wrap_amount * 0.75f;
198  L1 *= l1_wrap;
199
200  // The l2 band is:
201  //   ((1-w)^2)/4 = 1/4
202  //   (1-w)^2 = 1
203  float l2_wrap = (1.0f-wrap_amount);
204  l2_wrap *= l2_wrap;
205  L2 *= l2_wrap;
206#else
207  float l1_wrap = 1.0f;
208#endif  // _WRAPPED_LIGHTING
209
210  light.L00 = L0;
211  light.L01r = unity_SHAr.xyz;
212  light.L01g = unity_SHAg.xyz;
213  light.L01b = unity_SHAb.xyz;
214
215  return L0 + L1 + L2;
216}
217
218float4 getIndirectDiffuse(v2f i, Pbr pbr, inout LightData light) {
219  float4 diffuse = 0;
220
221#if defined(FORWARD_BASE_PASS) || defined(OUTLINES_PASS)
222#if defined(_BENT_NORMALS)
223  diffuse.xyz += max(0, yumSH9(float4(pbr.bent_normal, 1.0), i.worldPos, light.indirect));
224#else
225  diffuse.xyz += max(0, yumSH9(float4(pbr.normal, 1.0), i.worldPos, light.indirect));
226#endif
227#endif
228
229#if defined(_SHADOWS) || defined(_SSFD)
230  float3 dom_dir = getAverageSHDirection(
231      light.indirect.L01r,
232      light.indirect.L01g,
233      light.indirect.L01b,
234      light.direct.dir);
235  light.indirect.diffuse_dominant_dir = dom_dir;
236#endif
237
238#if defined(_SHADOWS)
239  float light_amount = dot(dom_dir, pbr.normal);
240  float3 shadow_color = lerp(
241    _Shadow_0_Color.rgb,
242    1,
243    smoothstep(_Shadow_0_Threshold - _Shadow_0_Blur, _Shadow_0_Threshold + _Shadow_0_Blur, light_amount));
244
245#if defined(_SHADOW_1)
246  shadow_color = lerp(
247    _Shadow_1_Color.rgb,
248    shadow_color,
249    smoothstep(_Shadow_1_Threshold - _Shadow_1_Blur, _Shadow_1_Threshold + _Shadow_1_Blur, light_amount));
250#endif  // _SHADOW_1
251
252  diffuse.xyz *= shadow_color;
253#endif  // _SHADOWS
254
255  return diffuse;
256}
257
258float getAO(v2f i) {
259  float ao = 1;
260#if defined(_BURLEY_TILING) && defined(_BURLEY_TILING_AMBIENT_OCCLUSION)
261  ao = saturate(lerp(
262      1.0,
263      burley_sample_scalar(
264          _Burley_Tiling_Ambient_Occlusion_Map,
265          _Burley_Tiling_Ambient_Occlusion_Map_LUT),
266      _OcclusionStrength));
267#elif defined(_AMBIENT_OCCLUSION)
268  ao = saturate(lerp(1.0, _OcclusionMap.Sample(bilinear_repeat_s, i.uv01.xy).r, _OcclusionStrength));
269#endif
270  return ao;
271}
272
273float getSpecularAO(v2f i, Pbr pbr, LightData data, float3 reflect_dir) {
274  float ao_vis = 1.0;
275#if defined(_AMBIENT_OCCLUSION)
276  ao_vis = data.common.ao;
277#endif
278
279  // Exposure occlusion: derive specular AO from diffuse irradiance magnitude.
280  // When IBL diffuse goes dark, attenuate specular to avoid implausible
281  // reflections. Based on filamented's IrradianceToExposureOcclusion.
282  float exposure_ao = saturate(length(data.indirect.diffuse) / _Exposure_Occlusion);
283  ao_vis *= exposure_ao;
284
285#if defined(_BENT_NORMALS)
286  float3 spec_ao_normal = pbr.bent_normal;
287#else
288  float3 spec_ao_normal = pbr.normal;
289#endif
290  float spec_ao = computeSpecularAO(data.common.NoV, ao_vis, pbr.roughness, spec_ao_normal, -data.indirect.dir);
291#if defined(_BENT_NORMALS)
292  spec_ao = saturate(lerp(1.0, spec_ao, _Bent_Normals_Strength));
293#endif
294
295  return spec_ao;
296}
297
298void GetLighting(v2f i, Pbr pbr, out LightData data) {
299	data = (LightData) 0;
300  data.common.ao = 1.0f;
301  data.common.spec_ao = 1.0f;
302
303	float3 view_dir = normalize(i.eyeVec.xyz);
304
305	data.common.V = -view_dir;
306	data.common.N = pbr.normal;
307  data.common.NoV = max(1e-4, dot(pbr.normal, data.common.V));
308  data.common.ao = getAO(i);
309#if defined(_CLEARCOAT)
310  data.common.NoV_cc = max(1e-4, dot(pbr.cc_normal, data.common.V));
311#endif
312
313	// Direct lighting
314	data.direct.dir = getDirectLightDirection(i);
315	data.direct.H = normalize(data.common.V + data.direct.dir);
316#if defined(_WRAPPED_LIGHTING)
317  data.direct.NoL = max(1e-4, wrapNoL(saturate(dot(pbr.normal, data.direct.dir)), _Wrapped_Lighting_Amount));
318#else
319	data.direct.NoL = max(1e-4, dot(pbr.normal, data.direct.dir));
320#endif
321	data.direct.NoH = max(1e-4, dot(pbr.normal, data.direct.H));
322  data.direct.LoH = max(1e-4, dot(data.direct.dir, data.direct.H));
323#if defined(_CLEARCOAT)
324  data.direct.NoH_cc = max(1e-4, dot(pbr.cc_normal, data.direct.H));
325  data.direct.NoL_cc = max(1e-4, dot(pbr.cc_normal, data.direct.dir));
326#endif
327  float direct_LoV = dot(data.direct.dir, data.common.V);
328  data.direct.LoV = max(1e-4, direct_LoV);
329  data.direct.double_LoV = max(1e-4, 2.0f * direct_LoV * direct_LoV - 1.0f);
330
331	float4 lightColorIntensity = getDirectLightColorIntensity();
332	data.direct.color = lightColorIntensity.rgb * (lightColorIntensity.w * getShadowAttenuation(i));
333
334	// Indirect lighting
335  float3 reflect_dir = reflect(-data.common.V, pbr.normal);
336  float3 dominant_dir = getSpecularDominantDirection(pbr.normal, reflect_dir, pbr.roughness);
337
338  data.indirect.dir = normalize(dominant_dir);
339	data.indirect.H = normalize(data.common.V + data.indirect.dir);
340	data.indirect.NoL = max(1e-4, dot(pbr.normal, data.indirect.dir));
341	data.indirect.NoH = max(1e-4, dot(pbr.normal, data.indirect.H));
342#if defined(_CLEARCOAT)
343  float3 cc_reflect_dir = reflect(-data.common.V, pbr.cc_normal);
344  float3 cc_dominant_dir = getSpecularDominantDirection(pbr.cc_normal, cc_reflect_dir, pbr.cc_roughness);
345  float3 dir_cc = normalize(cc_dominant_dir);
346  float3 H_cc = normalize(data.common.V + dir_cc);
347#endif
348  data.indirect.LoH = max(1e-4, dot(data.indirect.dir, data.indirect.H));
349  float indirect_LoV = dot(data.indirect.dir, data.common.V);
350  data.indirect.LoV = max(1e-4, indirect_LoV);
351  data.indirect.double_LoV = max(1e-4, 2.0f * indirect_LoV * indirect_LoV - 1.0f);
352
353	data.indirect.diffuse = getIndirectDiffuse(i, pbr, data);
354	data.indirect.specular = getIndirectSpecular(i, pbr.roughness_perceptual, view_dir, data.indirect.dir, data.indirect.diffuse);
355#if defined(_GLITTER)
356  float3 glitter_indirect_dir = getAverageSHDirection(
357      data.indirect.L01r,
358      data.indirect.L01g,
359      data.indirect.L01b,
360      data.indirect.dir);
361#if defined(_GLITTER_BASE_ROUGHNESS_OVERRIDE)
362  float glitter_roughness = _Glitter_Base_Roughness_Override;
363#else
364  float glitter_roughness = pbr.roughness;
365#endif
366#if defined(_GLITTER_NORMAL_OVERRIDE)
367  float2 glitter_normal_uv = UV_SCOFF(i, _Glitter_Normal_Override_ST,
368      _Glitter_UV_Channel);
369  float3 glitter_normal = _Glitter_Normal_Override.Sample(
370      bilinear_clamp_s, glitter_normal_uv).xyz * 2 - 1;
371  glitter_normal = glitter_normal.xzy * float3(-1, 1, -1);
372  glitter_normal = UnityObjectToWorldNormal(glitter_normal);
373  float3x3 tbn = tbn_from_normal_tangent(glitter_normal, i.tangent);
374#else
375  float3 glitter_normal = pbr.normal;
376  float3x3 tbn = pbr.tbn;
377#endif
378  data.glitter = GetGlitterLighting(
379      _Glitter_Amount, _Glitter_Roughness, _Glitter_Angular_Cells,
380      _Glitter_Filter_Size, UV_SCOFF(i, _Glitter_Mask_ST, _Glitter_UV_Channel), tbn, glitter_roughness, glitter_normal,
381      data.common.V, data.direct.H, glitter_indirect_dir);
382#endif
383
384  data.common.spec_ao = getSpecularAO(i, pbr, data, reflect_dir);
385
386#if defined(_CLEARCOAT)
387  data.indirect.specular_cc = getIndirectSpecular(i, pbr.cc_roughness_perceptual, view_dir, dir_cc, data.indirect.diffuse);
388#if defined(_CLEARCOAT_MASK)
389  float cc_mask = _Clearcoat_Mask.Sample(bilinear_clamp_s, i.uv01.xy).r;
390  data.indirect.specular_cc *= cc_mask;
391#endif
392#endif
393
394#if defined(_BRIGHTNESS_CLAMP)
395  float3 tmpHSV = RGBtoHSV(data.direct.color);
396  tmpHSV[2] = clamp(tmpHSV[2], _Brightness_Clamp_Min, _Brightness_Clamp_Max);
397  data.direct.color = HSVtoRGB(tmpHSV);
398
399  tmpHSV = RGBtoHSV(data.indirect.diffuse);
400  tmpHSV[2] = clamp(tmpHSV[2], _Brightness_Clamp_Min, _Brightness_Clamp_Max);
401  data.indirect.diffuse = HSVtoRGB(tmpHSV);
402
403  // No minimum for specular lighting. It would look awful.
404  tmpHSV = RGBtoHSV(data.indirect.specular);
405  tmpHSV[2] = clamp(tmpHSV[2], 0, _Brightness_Clamp_Max);
406  data.indirect.specular = HSVtoRGB(tmpHSV);
407
408#if defined(_GLITTER)
409  tmpHSV = RGBtoHSV(data.indirect.L00);
410  tmpHSV[2] = clamp(tmpHSV[2], 0, _Brightness_Clamp_Max);
411  data.indirect.L00 = HSVtoRGB(tmpHSV);
412#endif
413
414#if defined(_CLEARCOAT)
415  tmpHSV = RGBtoHSV(data.indirect.specular_cc);
416  tmpHSV[2] = clamp(tmpHSV[2], 0, _Brightness_Clamp_Max);
417  data.indirect.specular_cc = HSVtoRGB(tmpHSV);
418#endif
419#endif
420
421#if defined(_BRIGHTNESS_MULTIPLIER)
422  data.direct.color *= _Brightness_Multiplier;
423  data.indirect.diffuse *= _Brightness_Multiplier;
424  data.indirect.specular *= _Brightness_Multiplier;
425#if defined(_GLITTER)
426  data.indirect.L00 *= _Brightness_Multiplier;
427#endif
428#if defined(_CLEARCOAT)
429  data.indirect.specular_cc *= _Brightness_Multiplier;
430#endif
431#endif
432}
433
434#endif  // __LIGHTING_INC