yum/3ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum/3ner
f3918c9
master
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