yum-archive/Tooner

A toon shader for Unity's BIRP.

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

yumBegin adding gimmicks for downstairs map4880e41

master
14.2 KiB434 linesraw
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