yum-archive/Tooner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/Tooner
5f0746e
master
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