yum-archive/Tooner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/Tooner
4880e41
master
1#ifndef LTCGI_INCLUDED 2#define LTCGI_INCLUDED 3 4#include "LTCGI_config.cginc" 5 6#ifdef LTCGI_AVATAR_MODE 7 #undef LTCGI_STATIC_UNIFORMS 8 #undef LTCGI_BICUBIC_LIGHTMAP 9 #define LTCGI_ALWAYS_LTC_DIFFUSE 10 // for perf and locality don't allow cylinders on avatars for now (it probably would be misdetected anyway) 11 #undef LTCGI_CYLINDER 12#endif 13 14#ifdef LTCGI_TOGGLEABLE_SPEC_DIFF_OFF 15 #undef LTCGI_DIFFUSE_OFF 16 #undef LTCGI_SPECULAR_OFF 17#endif 18 19#if defined(LTCGI_V2_CUSTOM_INPUT) || defined(LTCGI_V2_DIFFUSE_CALLBACK) || defined(LTCGI_V2_SPECULAR_CALLBACK) 20 #define LTCGI_API_V2 21#endif 22 23#include "LTCGI_structs.cginc" 24#include "LTCGI_uniform.cginc" 25#include "LTCGI_functions.cginc" 26#include "LTCGI_shadowmap.cginc" 27 28#ifdef SHADER_TARGET_SURFACE_ANALYSIS 29#define const 30#endif 31 32// Main function - this calculates the approximated model for one pixel and one light 33void LTCGI_Evaluate(ltcgi_input input, float3 worldNorm, float3 viewDir, float3x3 Minv, float roughness, const bool diffuse, out ltcgi_output output) { 34 output.input = input; 35 output.color = input.rawColor; // copy for colormode static 36 output.intensity = 0; 37 38 // diffuse distance fade 39 #ifdef LTCGI_DISTANCE_FADE_APPROX 40 if (diffuse) // static branch, specular does not directly fade with distance 41 { 42 if (!input.flags.lmdOnly) { 43 // very approximate lol 44 float3 ctr = (input.Lw[0] + input.Lw[1]) * 0.5f; 45 if (dot(ctr, ctr) > LTCGI_DISTANCE_FADE_APPROX_MULT * LTCGI_DISTANCE_FADE_APPROX_MULT) 46 { 47 return; 48 } 49 } 50 } 51 #endif 52 53 #define RET1_IF_LMDIFF [branch] if (/*const*/ diffuse && input.flags.diffFromLm) { output.intensity = 1.0f; return; } 54 55 [branch] 56 if (input.flags.colormode == LTCGI_COLORMODE_SINGLEUV) { 57 float2 uv = input.uvStart; 58 if (uv.x < 0) uv.xy = uv.yx; 59 // TODO: make more configurable? 60 #ifdef LTCGI_VISUALIZE_SAMPLE_UV 61 output.color = float3(uv.xy, 0); 62 #elif !defined(SHADER_TARGET_SURFACE_ANALYSIS) 63 // sample video texture directly for accuracy 64 float3 sampled = _Udon_LTCGI_Texture_LOD0.SampleLevel(LTCGI_SAMPLER, uv.xy, 0).rgb; 65 output.color *= sampled; 66 #endif 67 68 RET1_IF_LMDIFF 69 } 70 71 #ifdef LTCGI_AUDIOLINK 72 [branch] 73 if (input.flags.colormode == LTCGI_COLORMODE_AUDIOLINK) { 74 float al = AudioLinkData(ALPASS_AUDIOLINK + uint2(0, input.flags.alBand)).r; 75 output.color *= al; 76 77 RET1_IF_LMDIFF 78 } 79 #endif 80 81 // create LTC polygon array 82 // note the order of source verts (keyword: winding order) 83 float3 L[5]; 84 L[0] = mul(Minv, input.Lw[0]); 85 L[1] = mul(Minv, input.Lw[1]); 86 L[2] = input.isTri ? L[1] : mul(Minv, input.Lw[3]); 87 L[3] = mul(Minv, input.Lw[2]); 88 L[4] = 0; 89 90 // get texture coords (before clipping!) 91 [branch] 92 if (input.flags.colormode == LTCGI_COLORMODE_TEXTURE) { 93 float3 RN; 94 float2 uv = LTCGI_calculateUV(input.i, input.flags, L, input.isTri, input.uvStart, input.uvEnd, RN); 95 float planeAreaSquared = dot(RN, RN); 96 float planeDistxPlaneArea = dot(RN, L[0]); 97 98 float3 sampled; 99 [branch] 100 if (diffuse) { // static branch 101 #ifdef LTCGI_BLENDED_DIFFUSE_SAMPLING 102 float3 sampled1; 103 LTCGI_sample(uv, 3, input.flags.texindex, 10, sampled1); 104 float3 sampled2; 105 LTCGI_sample(uv, 3, input.flags.texindex, 100, sampled2); 106 sampled = 107 sampled1 * 0.75 + 108 sampled2 * 0.25; 109 #else 110 LTCGI_sample(uv, 3, input.flags.texindex, 10, sampled); 111 #endif 112 } else { 113 float d = abs(planeDistxPlaneArea) / planeAreaSquared; 114 d *= LTCGI_UV_BLUR_DISTANCE; 115 d = log(d) / log(3.0); 116 117 // a rough material must never show a perfect reflection, 118 // since our LOD0 texture is not prefiltered (and thus cannot 119 // depict any blur correctly) - without this there is artifacting 120 // on the border of LOD0 and LOD1 121 d = clamp(d, saturate(roughness * 5.75), 1000); 122 123 LTCGI_trilinear(uv, d, input.flags.texindex, sampled); 124 } 125 126 // colorize output 127 output.color *= sampled; 128 } 129 130 RET1_IF_LMDIFF 131 #undef RET1_IF_LMDIFF 132 133 int n; 134 LTCGI_ClipQuadToHorizon(L, n); 135 136 // early out if everything was clipped below horizon 137 [branch] 138 if (n == 0) 139 return; 140 141 L[0] = normalize(L[0]); 142 L[1] = normalize(L[1]); 143 L[2] = normalize(L[2]); 144 L[3] = normalize(L[3]); 145 146 // integrate 147 float sum = 0; 148 sum += LTCGI_IntegrateEdge(L[0], L[1]).z; 149 sum += LTCGI_IntegrateEdge(L[1], L[2]).z; 150 sum += LTCGI_IntegrateEdge(L[2], L[3]).z; 151 [branch] 152 if (n >= 4) 153 { 154 L[4] = normalize(L[4]); 155 sum += LTCGI_IntegrateEdge(L[3], L[4]).z; 156 [branch] 157 if (n == 5) 158 sum += LTCGI_IntegrateEdge(L[4], L[0]).z; 159 } 160 161 // doublesided is accounted for with optimization at the start, so return abs 162 output.intensity = abs(sum); 163 return; 164} 165 166// Calculate light contribution for all lights, 167// call this from your shader and use the "diffuse" and "specular" outputs 168// lmuv is the raw lightmap UV coordinate (e.g. UV1) 169void LTCGI_Contribution( 170#ifdef LTCGI_API_V2 171 inout LTCGI_V2_CUSTOM_INPUT data, 172#endif 173 float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv 174#ifndef LTCGI_API_V2 175 , inout half3 diffuse, inout half3 specular, out float totalSpecularIntensity, out float totalDiffuseIntensity 176#endif 177) { 178 #ifndef LTCGI_API_V2 179 totalSpecularIntensity = 0; 180 totalDiffuseIntensity = 0; 181 #endif 182 183 #ifdef LTCGI_SPECULAR_OFF 184 specular = 0; 185 #endif 186 #ifdef LTCGI_DIFFUSE_OFF 187 diffuse = 0; 188 #endif 189 190 [branch] 191 if (_Udon_LTCGI_GlobalEnable == 0.0f) { 192 return; 193 } 194 195 // sample lookup tables 196 float theta = LTCGI_acos_fast(dot(worldNorm, viewDir)); 197 float2 uv = float2(roughness, theta/(0.5*UNITY_PI)); 198 uv = uv*LUT_SCALE + LUT_BIAS; 199 200 // calculate LTCGI custom lightmap UV and sample 201 float3 lms = LTCGI_SampleShadowmap(lmuv); 202 203 #ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER 204 // sample BDRF approximation from lookup texture 205 float4 t = _Udon_LTCGI_lut1.SampleLevel(LTCGI_SAMPLER, uv, 0); 206 #endif 207 float3x3 Minv = float3x3( 208 float3( 1, 0, t.w), 209 float3( 0, t.z, 0), 210 float3(t.y, 0, t.x) 211 ); 212 213 // construct orthonormal basis around N 214 float3 T1, T2; 215 T1 = normalize(viewDir - worldNorm*dot(viewDir, worldNorm)); 216 T2 = cross(worldNorm, T1); 217 218 // for diffuse lighting we assume the identity matrix as BDRF, so the 219 // LTC approximation is directly equivalent to the orthonormal rotation matrix 220 float3x3 identityBrdf = float3x3(float3(T1), float3(T2), float3(worldNorm)); 221 // rotate area light in (T1, T2, N) basis for actual BRDF matrix as well 222 Minv = mul(Minv, identityBrdf); 223 224 // specular brightness 225 float spec_amp = 1.0f; 226 #ifndef LTCGI_SPECULAR_OFF 227 #ifndef LTCGI_DISABLE_LUT2 228 #ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER 229 spec_amp = _Udon_LTCGI_lut2.SampleLevel(LTCGI_SAMPLER, uv, 0).x; 230 #endif 231 #endif 232 #endif 233 234 bool noLm = false; 235 #ifdef LTCGI_LTC_DIFFUSE_FALLBACK 236 #ifndef LTCGI_ALWAYS_LTC_DIFFUSE 237 #ifndef SHADER_TARGET_SURFACE_ANALYSIS 238 float2 lmSize; 239 _Udon_LTCGI_Lightmap.GetDimensions(lmSize.x, lmSize.y); 240 noLm = lmSize.x == 1; 241 #endif 242 #endif 243 #endif 244 #ifdef LTCGI_ALWAYS_LTC_DIFFUSE 245 noLm = true; 246 #endif 247 248 // loop through all lights and add them to the output 249#if MAX_SOURCES != 1 250 uint count = min(_Udon_LTCGI_ScreenCount, MAX_SOURCES); 251 [loop] 252#else 253 // mobile config 254 const uint count = 1; 255 [unroll(1)] 256#endif 257 for (uint i = 0; i < count; i++) { 258 // skip masked and black lights 259 if (_Udon_LTCGI_Mask[i]) continue; 260 float4 extra = _Udon_LTCGI_ExtraData[i]; 261 float3 color = extra.rgb; 262 if (!any(color)) continue; 263 264 ltcgi_flags flags = ltcgi_parse_flags(asuint(extra.w), noLm); 265 266 #ifdef LTCGI_ALWAYS_LTC_DIFFUSE 267 // can't honor a lightmap-only light in this mode 268 if (flags.lmdOnly) continue; 269 #endif 270 271 #ifdef LTCGI_TOGGLEABLE_SPEC_DIFF_OFF 272 // compile branches below away statically 273 flags.diffuse = flags.specular = true; 274 #endif 275 276 // calculate (shifted) world space positions 277 float3 Lw[4]; 278 float4 uvStart = (float4)0, uvEnd = (float4)0; 279 bool isTri = false; 280 if (flags.lmdOnly) { 281 Lw[0] = Lw[1] = Lw[2] = Lw[3] = (float3)0; 282 } else { 283 LTCGI_GetLw(i, flags, worldPos, Lw, uvStart, uvEnd, isTri); 284 } 285 286 // skip single-sided lights that face the other way 287 float3 screenNorm = cross(Lw[1] - Lw[0], Lw[2] - Lw[0]); 288 if (!flags.doublesided) { 289 if (dot(screenNorm, Lw[0]) < 0) 290 continue; 291 } 292 293 float lm = 1; 294 if (flags.lmch) { 295 lm = lms[flags.lmch - 1]; 296 if (lm < 0.001) continue; 297 } 298 299 ltcgi_input input; 300 input.i = i; 301 input.Lw = Lw; 302 input.isTri = isTri; 303 input.uvStart = uvStart; 304 input.uvEnd = uvEnd; 305 input.rawColor = color; 306 input.flags = flags; 307 input.screenNormal = screenNorm; 308 309 // diffuse lighting 310 #ifndef LTCGI_DIFFUSE_OFF 311 [branch] 312 if (flags.diffuse) 313 { 314 float lmd = lm; 315 if (flags.lmch) { 316 if (flags.diffFromLm) 317 lmd *= _Udon_LTCGI_LightmapMult[flags.lmch - 1]; 318 else 319 lmd = smoothstep(0.0, LTCGI_SPECULAR_LIGHTMAP_STEP, saturate(lm - LTCGI_LIGHTMAP_CUTOFF)); 320 } 321 ltcgi_output diff; 322 diff.color = 0; 323 LTCGI_Evaluate(input, worldNorm, viewDir, identityBrdf, roughness, true, diff); 324 diff.intensity *= lmd; 325 326 #ifdef LTCGI_API_V2 327 LTCGI_V2_DIFFUSE_CALLBACK(data, diff); 328 #else 329 // simply accumulate all lights 330 diffuse += (diff.intensity * diff.color); 331 totalDiffuseIntensity += diff.intensity; 332 #endif 333 } 334 #endif 335 336 // specular lighting 337 #ifndef LTCGI_SPECULAR_OFF 338 [branch] 339 if (flags.specular) 340 { 341 ltcgi_output spec; 342 spec.color = 0; 343 LTCGI_Evaluate(input, worldNorm, viewDir, Minv, roughness, false, spec); 344 spec.intensity *= spec_amp * smoothstep(0.0, LTCGI_SPECULAR_LIGHTMAP_STEP, saturate(lm - LTCGI_LIGHTMAP_CUTOFF)); 345 346 #ifdef LTCGI_API_V2 347 LTCGI_V2_SPECULAR_CALLBACK(data, spec); 348 #else 349 // simply accumulate all lights 350 specular += spec.intensity * spec.color; 351 totalSpecularIntensity += spec.intensity; 352 #endif 353 } 354 #endif 355 } 356} 357 358// COMPATIBILITY FALLBACKS 359 360#ifndef LTCGI_API_V2 361 362// missing totalSpecularIntensity, totalDiffuseIntensity, specular 363void LTCGI_Contribution( 364 float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv, inout half3 diffuse 365) { 366 half3 _u1 = (half3)0; 367 float _u2, _u3; 368 LTCGI_Contribution(worldPos, worldNorm, viewDir, roughness, lmuv, diffuse, _u1, _u2, _u3); 369} 370 371// missing totalSpecularIntensity, totalDiffuseIntensity 372void LTCGI_Contribution( 373 float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv, inout half3 diffuse, inout half3 specular 374) { 375 float _u1, _u2; 376 LTCGI_Contribution(worldPos, worldNorm, viewDir, roughness, lmuv, diffuse, specular, _u1, _u2); 377} 378 379// missing totalDiffuseIntensity 380void LTCGI_Contribution( 381 float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv, inout half3 diffuse, inout half3 specular, out float totalSpecularIntensity 382) { 383 float _u1; 384 LTCGI_Contribution(worldPos, worldNorm, viewDir, roughness, lmuv, diffuse, specular, totalSpecularIntensity, _u1); 385} 386 387#endif 388 389/* 390 391Parts of the code in this file are adapted from the example code found here: 392 393 https://github.com/selfshadow/ltc_code 394 395Modifications by _pi_ (@pimaker on GitHub), licensed under the terms of the 396MIT license as far as applicable. 397 398Original copyright notice: 399 400Copyright (c) 2017, Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. 401All rights reserved. 402 403Redistribution and use in source and binary forms, with or without 404modification, are permitted provided that the following conditions are met: 405 406* If you use (or adapt) the source code in your own work, please include a 407 reference to the paper: 408 409 Real-Time Polygonal-Light Shading with Linearly Transformed Cosines. 410 Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. 411 ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016. 412 Project page: https://eheitzresearch.wordpress.com/415-2/ 413 414* Redistributions of source code must retain the above copyright notice, this 415 list of conditions and the following disclaimer. 416 417* Redistributions in binary form must reproduce the above copyright notice, 418 this list of conditions and the following disclaimer in the documentation 419 and/or other materials provided with the distribution. 420 421THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 422AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 423IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 424DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 425FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 426DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 427SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 428CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 429OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 430OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 431 432*/ 433 434#endif