yum-archive/2ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/2ner
0a8d744
master
1#ifndef __YUM_LIGHTING_INC 2#define __YUM_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 "features.cginc" 11#include "LightVolumes.cginc" 12#include "poi.cginc" 13#include "yum_pbr.cginc" 14#include "math.cginc" 15 16// fucking kill me 17#ifndef __LTCGI_INC 18#define __LTCGI_INC 19 20#include "features.cginc" 21 22#if defined(_LTCGI) 23struct ltcgi_acc { 24 float3 diffuse; 25 float3 specular; 26}; 27 28#include "Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_structs.cginc" 29 30void ltcgi_cb_diffuse(inout ltcgi_acc acc, in ltcgi_output output); 31void ltcgi_cb_specular(inout ltcgi_acc acc, in ltcgi_output output); 32 33#define LTCGI_V2_CUSTOM_INPUT ltcgi_acc 34#define LTCGI_V2_DIFFUSE_CALLBACK ltcgi_cb_diffuse 35#define LTCGI_V2_SPECULAR_CALLBACK ltcgi_cb_specular 36 37#include "Third_Party/at.pimaker.ltcgi/Shaders/LTCGI.cginc" 38void ltcgi_cb_diffuse(inout ltcgi_acc acc, in ltcgi_output output) { 39 acc.diffuse += output.intensity * output.color * _LTCGI_DiffuseColor; 40} 41void ltcgi_cb_specular(inout ltcgi_acc acc, in ltcgi_output output) { 42 acc.specular += output.intensity * output.color * _LTCGI_SpecularColor; 43} 44#endif // _LTCGI 45 46#endif // __LTCGI_INC 47 48float3 Shade4PointLightsWrapped( 49 float4 lightPosX, float4 lightPosY, float4 lightPosZ, 50 float3 lightColor0, float3 lightColor1, float3 lightColor2, float3 lightColor3, 51 float4 lightAttenSq, 52 float3 pos, float3 normal, float wrapStrength) 53{ 54 // to light vectors 55 float4 toLightX = lightPosX - pos.x; 56 float4 toLightY = lightPosY - pos.y; 57 float4 toLightZ = lightPosZ - pos.z; 58 59 // squared lengths 60 float4 lengthSq = 0; 61 lengthSq += toLightX * toLightX; 62 lengthSq += toLightY * toLightY; 63 lengthSq += toLightZ * toLightZ; 64 65 // NdotL 66 float4 ndotl = 0; 67 ndotl += toLightX * normal.x; 68 ndotl += toLightY * normal.y; 69 ndotl += toLightZ * normal.z; 70 71 // correct NdotL 72 float4 corr = rsqrt(lengthSq); 73 ndotl = ndotl * corr; 74 75 // Apply wrapped lighting 76 float4 wrappedNdotl; 77 wrappedNdotl.x = saturate(wrapNoL(ndotl.x, wrapStrength)); 78 wrappedNdotl.y = saturate(wrapNoL(ndotl.y, wrapStrength)); 79 wrappedNdotl.z = saturate(wrapNoL(ndotl.z, wrapStrength)); 80 wrappedNdotl.w = saturate(wrapNoL(ndotl.w, wrapStrength)); 81 82 // attenuation 83 float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq); 84 float4 diff = wrappedNdotl * atten; 85 86 // final color 87 float3 col = 0; 88 col += lightColor0 * diff.x; 89 col += lightColor1 * diff.y; 90 col += lightColor2 * diff.z; 91 col += lightColor3 * diff.w; 92 93 return col; 94} 95 96struct YumLighting { 97 float3 view_dir; 98 float3 dir; 99 float3 direct; 100 float3 diffuse; 101 float diffuse_luminance; 102 float3 specular; 103 float NoL; 104#if defined(_WRAPPED_LIGHTING) 105 float NoL_wrapped_s; // specular 106 float NoL_wrapped_d; // diffuse 107#endif 108 float attenuation; 109 float3 L00; 110 float3 L01r; 111 float3 L01g; 112 float3 L01b; 113 float occlusion; 114 Light derivedLight; 115}; 116 117float getShadowAttenuation(v2f i, f2f f) 118{ 119 float attenuation; 120 float shadow; 121 // This whole block is yoinked from AutoLight.cginc. I needed a way to 122 // control shadow strength so I had to duplicate the code. 123#if defined(DIRECTIONAL_COOKIE) 124 DECLARE_LIGHT_COORD(i, f.worldPos); 125 shadow = UNITY_SHADOW_ATTENUATION(i, f.worldPos); 126 attenuation = tex2D(_LightTexture0, lightCoord).w; 127#elif defined(POINT_COOKIE) 128 DECLARE_LIGHT_COORD(i, f.worldPos); 129 shadow = UNITY_SHADOW_ATTENUATION(i, f.worldPos); 130 attenuation = tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).r * 131 texCUBE(_LightTexture0, lightCoord).w; 132#elif defined(DIRECTIONAL) 133 shadow = UNITY_SHADOW_ATTENUATION(i, f.worldPos); 134 attenuation = 1; 135#elif defined(SPOT) 136 DECLARE_LIGHT_COORD(i, f.worldPos); 137 shadow = UNITY_SHADOW_ATTENUATION(i, f.worldPos); 138 attenuation = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * 139 UnitySpotAttenuate(lightCoord.xyz); 140#elif defined(POINT) 141 unityShadowCoord3 lightCoord = 142 mul(unity_WorldToLight, unityShadowCoord4(f.worldPos, 1)).xyz; 143 shadow = UNITY_SHADOW_ATTENUATION(i, f.worldPos); 144 attenuation = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r; 145#else 146 shadow = 1; 147 attenuation = 1; 148#endif 149 float realtimeAttenuation = attenuation * lerp(1, shadow, _Shadow_Strength); 150 151 GetBakedAttenuation(realtimeAttenuation, i.uv01.zw, f.worldPos); 152 153 return realtimeAttenuation; 154} 155 156float3 getDirectLightDirection(v2f i, f2f f) { 157#if defined(POINT) || defined(POINT_COOKIE) || defined(SPOT) 158 return normalize((_WorldSpaceLightPos0 - f.worldPos).xyz); 159#else 160 return _WorldSpaceLightPos0; 161#endif 162} 163 164float GetLodRoughness(float roughness) { 165 return roughness * (1.7 - 0.7 * roughness); 166} 167 168float3 getIndirectSpecular(v2f i, f2f f, YumPbr pbr, float3 view_dir, float diffuse_luminance) { 169#if defined(_ANISOTROPY) 170 float3 aniso_tangent = cross(view_dir, pbr.binormal); 171 float3 aniso_normal = -normalize(cross(aniso_tangent, pbr.binormal)); 172 float3 refl_normal = normalize(lerp(pbr.normal, aniso_normal, _Anisotropy_Strength)); 173 float3 reflect_dir = reflect(-view_dir, refl_normal); 174#else 175 float3 reflect_dir = reflect(-view_dir, pbr.normal); 176#endif 177 178 UnityGIInput data; 179 data.worldPos = f.worldPos; 180 data.worldViewDir = view_dir; 181 data.probeHDR[0] = unity_SpecCube0_HDR; 182 data.probeHDR[1] = unity_SpecCube1_HDR; 183#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION) 184 data.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending 185#endif 186#ifdef UNITY_SPECCUBE_BOX_PROJECTION 187 data.boxMax[0] = unity_SpecCube0_BoxMax; 188 data.probePosition[0] = unity_SpecCube0_ProbePosition; 189 data.boxMax[1] = unity_SpecCube1_BoxMax; 190 data.boxMin[1] = unity_SpecCube1_BoxMin; 191 data.probePosition[1] = unity_SpecCube1_ProbePosition; 192#endif 193 194 // Apply roughness adjustment to match filamented's behavior 195 float3 env_refl = UnityGI_prefilteredRadiance(data, pbr.roughness_perceptual, reflect_dir); 196 197#if defined(_FALLBACK_CUBEMAP) 198 // Check if there's no valid scene cubemap 199 float3 canned_refl = env_refl; 200 if (!SceneHasReflections() || _Fallback_Cubemap_Force) { 201 // Set up data for fallback sampling similar to Unity's system 202 half3 reflectVector = reflect(-view_dir, pbr.normal); 203 204 #ifdef UNITY_SPECCUBE_BOX_PROJECTION 205 reflectVector = BoxProjectedCubemapDirection(reflectVector, data.worldPos, /*probe_position=*/0, /*box_min=*/-1, /*box_max=*/1); 206 #endif 207 208 half mip = pbr.roughness_perceptual * UNITY_SPECCUBE_LOD_STEPS; 209 float4 envSample = UNITY_SAMPLE_TEXCUBE_LOD(_Fallback_Cubemap, reflectVector, mip); 210 canned_refl = DecodeHDR(envSample, _Fallback_Cubemap_HDR) * _Fallback_Cubemap_Brightness * diffuse_luminance; 211 } 212#endif 213 214#if defined(_FALLBACK_CUBEMAP_LIMIT_METALLIC) 215 return lerp(env_refl, canned_refl, pbr.metallic); 216#elif defined(_FALLBACK_CUBEMAP) 217 return canned_refl; 218#else 219 return env_refl; 220#endif 221} 222 223float3 yumSH9(float4 n, float3 worldPos, inout YumLighting light) { 224//#define YUM_SH9_STANDARD 225#if defined(YUM_SH9_STANDARD) 226 // Unity gives us the first three bands (L0-L2) of SH coefficients as follows: 227 // unity_SHA*.w: L0 coefficients 228 // unity_SHA*.xyz: L1 coefficients 229 // unity_SHB*: first four of the L2 coefficients 230 // unity_SHC: last L2 coefficient 231 232 // Parse out coefficients into a simpler but less efficient format. 233 float3 L00 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); 234 float3 L1_1 = float3(unity_SHAr.x, unity_SHAg.x, unity_SHAb.x); 235 float3 L10 = float3(unity_SHAr.y, unity_SHAg.y, unity_SHAb.y); 236 float3 L11 = float3(unity_SHAr.z, unity_SHAg.z, unity_SHAb.z); 237 float3 L2_2 = float3(unity_SHBr.x, unity_SHBg.x, unity_SHBb.x); 238 float3 L2_1 = float3(unity_SHBr.y, unity_SHBg.y, unity_SHBb.y); 239 float3 L20 = float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z); 240 float3 L21 = float3(unity_SHBr.w, unity_SHBg.w, unity_SHBb.w); 241 float3 L22 = unity_SHC; 242 243 // Equation 13 from "An Efficient Representation for Irradiance Environment 244 // Maps" by Ramamoorthi and Hanrahan. Note that the order of some 245 // coefficients is different, and normalization constants have been 246 // premultiplied by Unity. 247 float3 L0 = L00; 248 float3 L1 = L1_1 * n.x + L10 * n.y + L11 * n.z; 249 float3 L2 = 250 L2_2 * n.x * n.y + 251 L2_1 * n.y * n.z + 252 L20 * n.z * n.z + 253 L21 * n.x * n.z + 254 L22 * (n.x * n.x - n.y * n.y); 255 256#if defined(_WRAPPED_LIGHTING) 257 float wrap_term = _Wrap_NoL_Diffuse_Strength; 258 // Original coefficients: 1, 2/3, 1/4. 259 // Wrapped coefficients: 1, (2-w)/3, ((1-w)^2)/4. 260 261 // Setting w=0, the l1 band is: 262 // (2-w)/3 = 2/3 263 // 2-w = 2 264 // 1-w/2 = 1 265 float l1_wrap = 1.0f - wrap_term * 0.75f; 266 L1 *= l1_wrap; 267 268 // The l2 band is: 269 // ((1-w)^2)/4 = 1/4 270 // (1-w)^2 = 1 271 float l2_wrap = (1.0f-wrap_term); 272 l2_wrap *= l2_wrap; 273 L2 *= l2_wrap; 274#else 275 float l1_wrap = 1.0f; 276#endif // _WRAPPED_LIGHTING 277 278 light.L00 = L00; 279 light.L01r = unity_SHAr.xyz * l1_wrap; 280 light.L01g = unity_SHAg.xyz * l1_wrap; 281 light.L01b = unity_SHAb.xyz * l1_wrap; 282 283 return L0 + L1 + L2; 284#else // !YUM_SH9_STANDARD 285 LightVolumeSH(worldPos, light.L00, light.L01r, light.L01g, light.L01b); 286 287#if defined(_LIGHT_VOLUMES_BRIGHTNESS) 288 [branch] 289 if (_Light_Volumes_Brightness_Enabled_Dynamic) { 290 float3 probe_L00 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); 291 float t = _Light_Volumes_Brightness; 292 light.L00 = lerp(probe_L00, light.L00, t); 293 light.L01r = lerp(unity_SHAr.xyz, light.L01r, t); 294 light.L01g = lerp(unity_SHAg.xyz, light.L01g, t); 295 light.L01b = lerp(unity_SHAb.xyz, light.L01b, t); 296 } 297#endif 298 299 // Hack to get directional information from SH. 300 float3 light_dir = normalize(float3(luminance(light.L01r), luminance(light.L01g), luminance(light.L01b))); 301 light.derivedLight.l = light_dir; 302 light.derivedLight.colorIntensity = float4(light.L00, 1); 303 light.derivedLight.attenuation = 1; 304 light.derivedLight.NoL = saturate(dot(n.xyz, light_dir)); 305 306#if defined(_WRAPPED_LIGHTING) 307 float wrap_term = _Wrap_NoL_Diffuse_Strength; 308 // Hack. Not energy preserving but sorta close. I think this looks better at fully flat mode. 309 float l1_wrap = 1.0f - wrap_term * 0.75f; 310 light.L01r *= l1_wrap; 311 light.L01g *= l1_wrap; 312 light.L01b *= l1_wrap; 313#endif // _WRAPPED_LIGHTING 314 315 return LightVolumeEvaluate(n.xyz, light.L00, light.L01r, light.L01g, light.L01b); 316#endif 317} 318 319float4 getIndirectDiffuse(v2f i, 320 f2f f, 321 float3 normal, 322 float4 vertexLightColor, 323 inout YumLighting light) { 324 float4 diffuse = vertexLightColor; 325#if defined(FORWARD_BASE_PASS) 326 diffuse.xyz += max(0, yumSH9(float4(normal, 0), f.worldPos, light)); 327#endif 328 329 return diffuse; 330} 331 332float3 applyQuasiShadows(float3 color, YumLighting light) { 333 float3 result = color; 334#if defined(_QUASI_SHADOWS) 335 float NoL = light.derivedLight.NoL; 336 float threshold = _Quasi_Shadows_0_Threshold; 337 float width = _Quasi_Shadows_0_Width; 338 float3 shadow_color = _Quasi_Shadows_0_Color.rgb; 339 float interp = smoothstep(threshold - width, threshold + width, NoL); 340 result = lerp(color * shadow_color, color, interp); 341#endif 342 return result; 343} 344 345YumLighting GetYumLighting(v2f i, f2f f, YumPbr pbr) { 346 YumLighting light = (YumLighting) 0; 347 348 // normalize has no visibile impact in test scene 349 light.view_dir = -f.viewDir; 350 351 light.dir = getDirectLightDirection(i, f); 352 353 // Use proper light color/intensity separation 354 light.direct = _LightColor0.rgb; 355 356 // Calculate attenuation first, before diffuse lighting 357 light.attenuation = getShadowAttenuation(i, f); 358 359 float3 tangentNormal = mul(f.tbn, pbr.normal); 360 float3x3 tangentToWorld = float3x3(i.tangent.xyz, f.binormal, i.normal); 361 362 // Use Bakery-aware irradiance function 363#if defined(LIGHTMAP_ON) 364 light.diffuse = BakeryGI_Irradiance( 365 pbr.normal, // worldNormal 366 f.worldPos, // worldPos 367 float4(i.uv01.zw, 0, 0), // lightmapUV (xy = uv0, zw = uv1) 368 float3(0,0,0), // ambient (will be calculated internally) 369 light.attenuation, // attenuation 370 tangentNormal, // tangentNormal 371 tangentToWorld, // tangentToWorld 372 light.occlusion, // out occlusion 373 light.derivedLight // out Light 374 ); 375#if defined(_GRAYSCALE_LIGHTMAPS) 376 light.diffuse.gb = light.diffuse.r; 377#endif 378#else 379 light.diffuse = getIndirectDiffuse(i, f, pbr.normal, float4(i.vertexLight.xyz, 0), light); 380 light.occlusion = 1; 381#endif 382 383#if defined(_MIN_BRIGHTNESS) 384 light.diffuse = max(_Min_Brightness, light.diffuse); 385#endif 386 387 light.diffuse_luminance = luminance(light.diffuse); 388 light.specular = getIndirectSpecular(i, f, pbr, light.view_dir, light.diffuse_luminance); 389 390#if defined(_LTCGI) 391 ltcgi_acc acc = (ltcgi_acc) 0; 392 LTCGI_Contribution( 393 acc, 394 f.worldPos, 395 pbr.normal, 396 light.view_dir, 397 pbr.roughness_perceptual, 398 0); 399 light.diffuse += acc.diffuse * _LTCGI_Strength; 400 light.specular += acc.specular * _LTCGI_Strength; 401#endif 402 403#if defined(_QUANTIZE_SPECULAR) 404 float specular_luminance = luminance(light.specular); 405 light.specular = light.specular * floor(specular_luminance * _Quantize_Specular_Steps) / _Quantize_Specular_Steps; 406#endif 407#if defined(_QUANTIZE_DIFFUSE) 408 light.diffuse = light.diffuse * floor(light.diffuse_luminance * _Quantize_Diffuse_Steps) / _Quantize_Diffuse_Steps; 409 light.diffuse_luminance = luminance(light.diffuse); 410#endif 411 412#if defined(_BRIGHTNESS_CONTROL) 413 light.direct *= _Brightness_Multiplier; 414 light.diffuse *= _Brightness_Multiplier; 415 light.specular *= _Brightness_Multiplier; 416#endif 417 418 light.NoL = saturate(dot(pbr.normal, light.dir)); 419#if defined(_QUANTIZE_NOL) 420 light.NoL = floor(light.NoL * _Quantize_NoL_Steps) / _Quantize_NoL_Steps; 421#endif 422#if defined(_WRAPPED_LIGHTING) 423 light.NoL_wrapped_s = saturate(wrapNoL(light.NoL, _Wrap_NoL_Specular_Strength)); 424 light.NoL_wrapped_d = saturate(wrapNoL(light.NoL, _Wrap_NoL_Diffuse_Strength)); 425#endif 426 427 return light; 428} 429 430#endif // __YUM_LIGHTING_INC