yum-archive/Tooner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum-archive/Tooner

yumAdd ability to swap decal slots5f0746e

master
14.1 KiB483 linesraw
1#include "atrix256.cginc"
2#include "globals.cginc"
3#include "ltcgi.cginc"
4#include "filament_math.cginc"
5#include "globals.cginc"
6#include "interpolators.cginc"
7#include "math.cginc"
8#include "MochieStandardBRDF.cginc"
9#include "poi.cginc"
10#include "tone.cginc"
11
12#ifndef __PBR_INC
13#define __PBR_INC
14
15UNITY_DECLARE_TEXCUBE(_Cubemap);
16half4 _Cubemap_HDR;
17float _Cubemap_Limit_To_Metallic;
18
19float getShadowAttenuation(v2f i)
20{
21  float attenuation;
22  // This whole block is yoinked from AutoLight.cginc. I needed a way to
23  // control shadow strength so I had to duplicate the code.
24#if defined(DIRECTIONAL_COOKIE)
25  DECLARE_LIGHT_COORD(i, i.worldPos);
26  float shadow = UNITY_SHADOW_ATTENUATION(i, i.worldPos);
27  attenuation = tex2D(_LightTexture0, lightCoord).w;
28#elif defined(POINT_COOKIE)
29  DECLARE_LIGHT_COORD(i, i.worldPos);
30  float shadow = UNITY_SHADOW_ATTENUATION(i, i.worldPos);
31  attenuation = tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).r *
32    texCUBE(_LightTexture0, lightCoord).w;
33#elif defined(DIRECTIONAL)
34  float shadow = UNITY_SHADOW_ATTENUATION(i, i.worldPos);
35  attenuation = 1;
36#elif defined(SPOT)
37  DECLARE_LIGHT_COORD(i, i.worldPos);
38  float shadow = UNITY_SHADOW_ATTENUATION(i, i.worldPos);
39  attenuation = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz);
40#elif defined(POINT)
41  unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(i.worldPos, 1)).xyz;
42  float shadow = UNITY_SHADOW_ATTENUATION(i, i.worldPos);
43  attenuation = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r;
44#else
45  float shadow = 1;
46  attenuation = 1;
47#endif
48  attenuation *= lerp(1, shadow, _Shadow_Strength);
49  return attenuation;
50}
51
52float3 getDirectLightDirection(v2f i) {
53#if defined(POINT) || defined(POINT_COOKIE) || defined(SPOT)
54  return normalize((_WorldSpaceLightPos0 - i.worldPos).xyz);
55#else
56  return _WorldSpaceLightPos0;
57#endif
58}
59
60float3 getDirectLightColor()
61{
62  return _LightColor0.rgb;
63}
64
65float GetRoughness(float smoothness) {
66  float r = 1 - smoothness;
67  r *= 1.7 - 0.7 * r;
68  return r;
69}
70
71float3 BoxProjection (
72	float3 direction, float3 position,
73	float4 cubemapPosition, float3 boxMin, float3 boxMax
74) {
75	#if UNITY_SPECCUBE_BOX_PROJECTION
76		UNITY_BRANCH
77		if (cubemapPosition.w > 0) {
78			float3 factors =
79				((direction > 0 ? boxMax : boxMin) - position) / direction;
80			float scalar = min(min(factors.x, factors.y), factors.z);
81			direction = direction * scalar + (position - cubemapPosition);
82		}
83	#endif
84	return direction;
85}
86
87float4 getIndirectDiffuse(v2f i, float4 vertexLightColor) {
88  float4 diffuse = vertexLightColor;
89#if defined(FORWARD_BASE_PASS)
90#if defined(LIGHTMAP_ON)
91  diffuse.xyz = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv2));
92#else
93  diffuse.xyz += max(0, BetterSH9(float4(0, 0, 0, 1)));
94#endif
95#endif
96  return diffuse;
97}
98
99float4 getIndirectDiffuse(v2f i, float4 vertexLightColor, float3 normal) {
100  float4 diffuse = vertexLightColor;
101#if defined(FORWARD_BASE_PASS)
102#if defined(LIGHTMAP_ON)
103  diffuse.xyz = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv2));
104#else
105  if (_Mesh_Normals_Mode == 3) {  // Toon
106    diffuse.xyz += max(0, BetterSH9(float4(0, 0, 0, 1)));
107  } else {
108    diffuse.xyz += max(0, BetterSH9(float4(normal, 1)));
109  }
110#endif
111#endif
112  return diffuse;
113}
114
115float3 getIndirectSpecular(v2f i, float3 view_dir, float3 normal,
116    float smoothness, float metallic, float3 worldPos) {
117  float3 specular = 0;
118
119#if defined(FORWARD_BASE_PASS)
120  float3 reflect_dir = reflect(-view_dir, normal);
121  Unity_GlossyEnvironmentData env_data;
122  env_data.roughness = GetRoughness(smoothness);
123  env_data.reflUVW = BoxProjection(
124    reflect_dir, worldPos,
125    unity_SpecCube0_ProbePosition,
126    unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax
127  );
128  float3 probe0 = Unity_GlossyEnvironment(
129      UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, env_data
130      );
131  specular = probe0;
132  if (unity_SpecCube0_BoxMin.w < 0.99999) {
133    env_data.reflUVW = BoxProjection(
134        reflect_dir, worldPos,
135        unity_SpecCube1_ProbePosition,
136        unity_SpecCube1_BoxMin.xyz, unity_SpecCube1_BoxMax.xyz);
137    float3 probe1 = Unity_GlossyEnvironment(
138        UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0),
139        unity_SpecCube1_HDR, env_data
140        );
141    specular = lerp(probe1, probe0, unity_SpecCube0_BoxMin.w);
142	}
143
144#if defined(_CUBEMAP)
145#if 0
146  float3 specular_tmp =
147    UNITY_SAMPLE_TEXCUBE_LOD(
148        _Cubemap,
149        reflect_dir,
150        roughness * UNITY_SPECCUBE_LOD_STEPS);
151#else
152  env_data.reflUVW = BoxProjection(
153    reflect_dir, worldPos,
154    unity_SpecCube0_ProbePosition,
155    unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax
156  );
157  float3 specular_tmp = Unity_GlossyEnvironment(
158      UNITY_PASS_TEXCUBE(_Cubemap), _Cubemap_HDR, env_data);
159#endif
160  if (_Cubemap_Limit_To_Metallic) {
161    specular = lerp(specular, specular_tmp, metallic);
162  } else {
163    specular = specular_tmp;
164  }
165#endif  // _CUBEMAP
166
167  // Lifted from poi toon shader (MIT).
168  float horizon = min(1 + dot(reflect_dir, normal), 1);
169  specular *= horizon * horizon;
170
171#endif  // FORWARD_BASE_PASS
172
173  return specular;
174}
175
176float4 getLitColor(
177    float4 vertexLightColor,
178    float4 albedo,
179    float3 worldPos,
180    float3 normal,
181    float metallic,
182    float smoothness,
183    float2 uv,
184    float ao,
185    // hack while i figure out view-dependent flickering in outlines
186    bool enable_direct,
187    float3 diffuse_contrib,
188    v2f i,
189    ToonerData tdata)
190{
191  float3 specular_tint;
192  float one_minus_reflectivity;
193  albedo.rgb = DiffuseAndSpecularFromMetallic(
194    albedo, metallic, specular_tint, one_minus_reflectivity);
195
196  const float3 view_dir = normalize(_WorldSpaceCameraPos - worldPos);
197  uint normals_mode = round(_Mesh_Normals_Mode);
198  switch (normals_mode) {
199    case 0:
200      {
201        float3 flat_normal = normalize(
202            (1.0 / _Flatten_Mesh_Normals_Str) * normal +
203            _Flatten_Mesh_Normals_Str * view_dir);
204        normal = flat_normal;
205      }
206      break;
207    case 1:
208      {
209        float3 spherical_normal = normalize(UnityObjectToWorldNormal(normalize(i.objPos)));
210        normal = spherical_normal;
211      }
212      break;
213    default:
214      break;
215  }
216
217	UnityIndirect indirect_light;
218  vertexLightColor *= _Vertex_Lighting_Factor;
219
220
221#if defined(LIGHTMAP_ON)
222  UNITY_LIGHT_ATTENUATION(shadow_attenuation, i, i.worldPos);
223#else
224  float shadow_attenuation = getShadowAttenuation(i);
225#endif
226
227  UnityLight direct_light;
228  direct_light.color = 0;
229  {
230    direct_light.dir = getDirectLightDirection(i);
231    direct_light.ndotl = DotClamped(normal, direct_light.dir);
232//#define POI_LIGHTING
233#if defined(POI_LIGHTING)
234    direct_light.color = getPoiLightingDirect(normal);
235#else
236    direct_light.color = getDirectLightColor();
237#endif
238    indirect_light.diffuse = getIndirectDiffuse(i, vertexLightColor, normal) + diffuse_contrib;
239    indirect_light.specular = getIndirectSpecular(i, view_dir, normal,
240        smoothness, metallic, worldPos);
241  }
242
243  if (normals_mode == 0) {
244    float e = 0.8;
245    indirect_light.diffuse += direct_light.color * e;
246    direct_light.color *= (1 - e);
247  }
248
249#if defined(_LTCGI)
250#if !defined(LIGHTMAP_ON) && !defined(_FORCE_WORLD_LIGHTING)
251  ltcgi_acc acc = (ltcgi_acc) 0;
252  if (_LTCGI_Enabled_Dynamic) {
253    LTCGI_Contribution(
254        acc,
255        i.worldPos,
256        normal,
257        view_dir,
258        GetRoughness(smoothness),
259        i.uv2);
260    indirect_light.diffuse += acc.diffuse * _LTCGI_Strength;
261    indirect_light.specular += acc.specular * _LTCGI_Strength;
262  }
263#endif
264#endif
265
266#if defined(_BRIGHTNESS_CLAMP) || defined(_PROXIMITY_DIMMING)
267  direct_light.color = RGBtoHSV(direct_light.color);
268  indirect_light.specular = RGBtoHSV(indirect_light.specular);
269  indirect_light.diffuse = RGBtoHSV(indirect_light.diffuse);
270
271  if (_Reflection_Probe_Saturation < 1.0) {
272    direct_light.color[1] *= _Reflection_Probe_Saturation;
273    indirect_light.specular[1] *= _Reflection_Probe_Saturation;
274    indirect_light.diffuse[1] *= _Reflection_Probe_Saturation;
275  }
276
277  float2 brightnesses = float2(
278      direct_light.color[2],
279      indirect_light.diffuse[2]);
280  // Do this to avoid division by 0. If both light sources are black,
281  // sum_brightness could be 0;
282  const float min_brightness = max(_Min_Brightness, 1E-6);
283#if defined(_BRIGHTNESS_CLAMP)
284  //brightnesses = smooth_max(brightnesses, _Min_Brightness);
285  brightnesses = max(brightnesses, min_brightness);
286
287  float sum_brightness = max(brightnesses[0] + brightnesses[1], min_brightness);
288  float2 brightness_proportions = brightnesses / sum_brightness;
289
290  sum_brightness = clamp(sum_brightness, min_brightness, _Max_Brightness);
291
292  direct_light.color[2] = sum_brightness * brightness_proportions[0];
293  indirect_light.diffuse[2] = sum_brightness * brightness_proportions[1];
294#endif
295
296#if defined(_PROXIMITY_DIMMING)
297  {
298    float cam_dist = length(i.centerCamPos - worldPos);
299    // Map onto [min, max]
300    cam_dist = clamp(cam_dist, _Proximity_Dimming_Min_Dist,
301        _Proximity_Dimming_Max_Dist);
302    // Map onto [0, max - min]
303    cam_dist -= _Proximity_Dimming_Min_Dist;
304    // Map onto [0, 1]
305    cam_dist /= _Proximity_Dimming_Max_Dist - _Proximity_Dimming_Min_Dist;
306    float dim_factor = lerp(_Proximity_Dimming_Factor, 1, cam_dist);
307    direct_light.color[2] *= dim_factor;
308    indirect_light.diffuse[2] *= dim_factor;
309    indirect_light.specular[2] *= dim_factor;
310  }
311#endif
312
313  // Specular has to be clamped separately to avoid artifacting.
314#if defined(_BRIGHTNESS_CLAMP)
315  indirect_light.specular[2] = clamp(indirect_light.specular[2], min_brightness, _Max_Brightness);
316#endif
317
318  direct_light.color = HSVtoRGB(direct_light.color);
319  indirect_light.specular = HSVtoRGB(indirect_light.specular);
320  indirect_light.diffuse = HSVtoRGB(indirect_light.diffuse);
321#endif
322  // _BRIGHTNESS_CLAMP || _PROXIMITY_DIMMING
323  direct_light.color *= _Lighting_Factor * _Direct_Lighting_Factor * enable_direct;
324  indirect_light.specular *= _Lighting_Factor * _Indirect_Specular_Lighting_Factor * _Indirect_Specular_Lighting_Factor2;
325  indirect_light.diffuse *= _Lighting_Factor * _Indirect_Diffuse_Lighting_Factor;
326
327  // Apply AO
328  indirect_light.diffuse *= ao;
329  float3 direct_color = direct_light.color;
330  direct_light.color *= ao;
331
332  direct_light.color *= shadow_attenuation;
333
334  float2 screenUVs = 0;
335  float4 screenPos = 0;
336  #if SSR_ENABLED
337    float perspective_divide = rcp(i.pos.w+0.0000000001);
338    screenUVs = i.screenPos.xy * perspective_divide;
339    #if UNITY_SINGLE_PASS_STEREO || defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED        )
340      screenUVs.x *= 2;
341    #endif
342    screenPos = float4(i.screenPos, 0, i.pos.w);
343  #endif
344
345#if 1
346  float reflection_strength = _ReflectionStrength;
347#if defined(_REFLECTION_STRENGTH_TEX)
348  reflection_strength *= _ReflectionStrengthTex.SampleLevel(linear_repeat_s, i.uv0, 0);
349#endif
350#if defined(SSR_MASK)
351  float ssr_mask = _SSR_Mask.SampleLevel(linear_repeat_s, i.uv0, 0);
352#else
353  float ssr_mask = 1;
354#endif
355  float4 pbr = BRDF1_Mochie_PBS(
356      albedo,
357      specular_tint,
358      one_minus_reflectivity,
359      smoothness,
360      normal,
361      view_dir,
362      i.worldPos,
363      screenUVs,
364      screenPos,
365      metallic,
366      /*thickness=*/1,
367      /*ssColor=*/0,
368      shadow_attenuation,
369      i.uv2,
370      vertexLightColor,
371      direct_light,
372      indirect_light,
373      reflection_strength,
374      ssr_mask);
375#else
376    float3 pbr = UNITY_BRDF_PBS(
377        albedo,
378        specular_tint,
379        one_minus_reflectivity,
380        smoothness,
381        normal,
382        view_dir,
383        direct_light,
384        indirect_light).xyz;
385#endif
386
387
388#if defined(_LTCGI)
389#if defined(LIGHTMAP_ON) || defined(_FORCE_WORLD_LIGHTING)
390  ltcgi_acc acc = (ltcgi_acc) 0;
391  if (_LTCGI_Enabled_Dynamic) {
392    LTCGI_Contribution(
393        acc,
394        i.worldPos,
395        normal,
396        view_dir,
397        1.0 - smoothness,
398        i.uv2);
399    pbr.rgb += (acc.diffuse * pbr.rgb + acc.specular) * albedo.a;
400  }
401#endif
402#endif
403
404  // TODO formalize with parameters
405  // Break up color banding by adding some dithering to shaded color.
406  //float screen_dither = ign(tdata.screen_uv_round) * .00390625;
407  //pbr += screen_dither;
408
409#if defined(_CLEARCOAT)
410    // Direct lighting
411    // TODO add keywords to optimize away mask samples when not used
412    float cc_mask = 1;
413#if defined(_CLEARCOAT_MASK)
414    float cc_mask_tmp = _Clearcoat_Mask.SampleBias(linear_repeat_s, i.uv0, _Global_Sample_Bias);
415    if (_Clearcoat_Mask_Invert) {
416      cc_mask_tmp = 1 - cc_mask_tmp;
417    }
418    cc_mask *= cc_mask_tmp;
419#endif
420#if defined(_CLEARCOAT_MASK2)
421    float cc_mask2_tmp = _Clearcoat_Mask2.SampleBias(linear_repeat_s, i.uv0, _Global_Sample_Bias);
422    if (_Clearcoat_Mask_Invert) {
423      cc_mask2_tmp = 1 - cc_mask2_tmp;
424    }
425    cc_mask *= cc_mask2_tmp;
426#endif
427    const float3 cc_normal = _Clearcoat_Use_Texture_Normals ? normal : i.normal;
428    // Diffuse specular
429    const float cc_roughness = max(1E-4, _Clearcoat_Roughness);
430    const float3 cc_indirect_specular = getIndirectSpecular(
431        i, view_dir,
432        cc_normal,
433        /*smoothness=*/1 - cc_roughness,
434        /*metallic=*/0,
435        worldPos);
436    // Indirect specular
437    {
438      // TODO fold this into the full BRDF and apply the brightness corrections
439      // described in the filament whitepaper:
440      // https://google.github.io/filament/Filament.html
441      const float3 l = reflect(-view_dir, cc_normal);
442      const float3 h = normalize(l + view_dir);
443      const float NoH = dot(cc_normal, h);
444      const float LoH = dot(l, h);
445
446      float Fc;
447      float cc_term = FilamentClearcoat(
448          cc_roughness,
449          _Clearcoat_Strength,
450          NoH,
451          LoH,
452          h,
453          Fc);
454      pbr.rgb += cc_term * indirect_light.specular * cc_mask;
455    }
456    // Direct
457    {
458      const float3 l = direct_light.dir;
459      const float3 h = normalize(l + view_dir);
460      const float NoH = dot(cc_normal, h);
461      const float LoH = dot(direct_light.dir, h);
462
463      float Fc;
464      float cc_term = FilamentClearcoat(
465          cc_roughness,
466          _Clearcoat_Strength,
467          NoH,
468          LoH,
469          h,
470          Fc);
471      pbr.rgb += cc_term * direct_light.color * cc_mask;
472    }
473#endif
474
475#if defined(_UNITY_FOG)
476  UNITY_APPLY_FOG(i.fogCoord, pbr.rgb);
477#endif
478
479  return float4(pbr.rgb, albedo.a);
480}
481
482#endif  // __PBR_INC
483