diff options
| author | yum <yum.food.vr@gmail.com> | 2025-02-23 02:24:39 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2025-02-23 02:24:58 -0800 |
| commit | f478606867d42e971c5fa83803f3255e922e6a8a (patch) | |
| tree | c50ec81b781aaaff9fd74a416c22be8ff1552e5e /Third_Party | |
| parent | 3ec0f0c2d35f21d2f28d0a116e9e211789b13903 (diff) | |
add ltcgi, alpha multiplier
Diffstat (limited to 'Third_Party')
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/LICENSE | 69 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/README.md | 72 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/Shaders/LTCGI.cginc | 434 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_AudioLinkNoOp.cginc | 1 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_config.cginc | 87 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_functions.cginc | 514 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_shadowmap.cginc | 93 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_structs.cginc | 43 | ||||
| -rw-r--r-- | Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_uniform.cginc | 146 |
9 files changed, 1459 insertions, 0 deletions
diff --git a/Third_Party/at.pimaker.ltcgi/LICENSE b/Third_Party/at.pimaker.ltcgi/LICENSE new file mode 100644 index 0000000..6249e15 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/LICENSE @@ -0,0 +1,69 @@ +MIT License + +Copyright (c) 2022 _pi_/pimaker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Regarding the files 'Shaders/LTCGI.cginc' and 'Shaders/LTCGI_functions.cginc': +Parts of the code in these file are adapted from the example code found here: + + https://github.com/selfshadow/ltc_code + +Modifications by _pi_ (@pimaker on GitHub), licensed under the terms of the +MIT license as far as applicable. + +Original copyright notice: + +Copyright (c) 2017, Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* If you use (or adapt) the source code in your own work, please include a + reference to the paper: + Real-Time Polygonal-Light Shading with Linearly Transformed Cosines. + Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. + ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016. + Project page: https://eheitzresearch.wordpress.com/415-2/ +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +The following files are licensed explicitly, and may not be modified or used +in commercial projects, but can be redistributed and displayed otherwise, +provided this license is kept: + + Propaganda/pi_graffiti.png + Propaganda/ltcgi_graffiti.png diff --git a/Third_Party/at.pimaker.ltcgi/README.md b/Third_Party/at.pimaker.ltcgi/README.md new file mode 100644 index 0000000..3da22c8 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/README.md @@ -0,0 +1,72 @@ +# [LTCGI](https://ltcgi.dev) + +LTCGI is an optimized plug-and-play realtime area light solution using the [linearly transformed cosine algorithm](#LTC) for standalone Unity and VRChat. Free to use with [attribution](#Attribution). It can utilize the Unity build-in lightmapper or [Bakery](https://assetstore.unity.com/packages/tools/level-design/bakery-gpu-lightmapper-122218) for realistic shadows on static objects. + + + +# Check out the [**official website**](https://ltcgi.dev) for documentation and a "Getting Started" guide! (https://ltcgi.dev) + +Consider the [attribution requirements](#Attribution). Check the [Releases](https://github.com/pimaker/ltcgi/releases) tab for downloads. + +You can also [download](https://github.com/PiMaker/ltcgi/raw/main/~DemoApp.zip) the standalone demo app pictured above to try it out for yourself. +Alternatively, join the [demo world](https://vrchat.com/home/launch?worldId=wrld_aa2627ec-c63a-4db2-aa3e-9078d41c6d9c) in VRChat. + +[Read the FAQ](https://ltcgi.dev/FAQ) before asking for help anywhere! Once you've done that, feel free to join my Discord and ask for help: https://discord.gg/r38vJd2DuJ + +## Download + +For VRChat, you can install LTCGI via the [Creator Companion](https://vcc.docs.vrchat.com/) from my VPM repository: + +### ⬇️ **[Creator Companion/VPM Install](https://vpm.pimaker.at/)** + +For **standalone Unity**, you can import LTCGI as a [git package](https://docs.unity3d.com/2019.4/Documentation/Manual/upm-ui-giturl.html) with the URL: `https://github.com/PiMaker/ltcgi.git` + +**Adapters** for various VRChat video players are still distributed as unitypackages from the [Releases tab](https://github.com/pimaker/ltcgi/releases). + +## Supported shaders + +To use LTCGI, all objects that should receive lighting must use a compatible shader. Currently compatible ones are listed below. If you implement LTCGI into your shader, feel free to send a PR to be included. + +* [ORL Shader Family](https://shaders.orels.sh/) by [@orels1](https://github.com/orels1) +* [Silent's Filamented](https://gitlab.com/s-ilent/filamented) +* [Mochie's Unity Shaders](https://github.com/MochiesCode/Mochies-Unity-Shaders) +* [Hekky Shaders](https://github.com/hyblocker/hekky-shaders) +* [z3y's Shaders](https://github.com/z3y/shaders) +* Basic "Unlit" Test Shader (included) +* Surface Shader (included) + +## Attribution + +According to the [License](#License) you are free to use this in your world, but you need to give credit. You are free to do so in whichever way, but you must provide a link to this GitHub repository, such as to fulfill the imported license of the LTC example code used as a base for this project. + +For your convenience, a prefab called `LTCGI Attribution` is provided in the package. + + + +If you don't want to use it, instead display text similar to the following: + +``` +This project/world uses LTCGI by _pi_, see 'github.com/pimaker/ltcgi'. +``` + +# Licensing + +## The LTC algorithm + +Based on this paper: +``` +Real-Time Polygonal-Light Shading with Linearly Transformed Cosines. +Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. +ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016. +Project page: https://eheitzresearch.wordpress.com/415-2/ +``` +[Read more](https://eheitzresearch.wordpress.com/415-2/) + +## LTCGI + +This project is made available under the terms of the MIT license, unless explicitly marked otherwise in the source files. See `LICENSE` for more. + +The following files are licensed explicitly, and may not be modified or used in commercial projects, but can be redistributed and displayed otherwise, provided this license is kept: + +* Propaganda/pi_graffiti.png +* Propaganda/ltcgi_graffiti.png diff --git a/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI.cginc b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI.cginc new file mode 100644 index 0000000..b976972 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI.cginc @@ -0,0 +1,434 @@ +#ifndef LTCGI_INCLUDED +#define LTCGI_INCLUDED + +#include "LTCGI_config.cginc" + +#ifdef LTCGI_AVATAR_MODE + #undef LTCGI_STATIC_UNIFORMS + #undef LTCGI_BICUBIC_LIGHTMAP + #define LTCGI_ALWAYS_LTC_DIFFUSE + // for perf and locality don't allow cylinders on avatars for now (it probably would be misdetected anyway) + #undef LTCGI_CYLINDER +#endif + +#ifdef LTCGI_TOGGLEABLE_SPEC_DIFF_OFF + #undef LTCGI_DIFFUSE_OFF + #undef LTCGI_SPECULAR_OFF +#endif + +#if defined(LTCGI_V2_CUSTOM_INPUT) || defined(LTCGI_V2_DIFFUSE_CALLBACK) || defined(LTCGI_V2_SPECULAR_CALLBACK) + #define LTCGI_API_V2 +#endif + +#include "LTCGI_structs.cginc" +#include "LTCGI_uniform.cginc" +#include "LTCGI_functions.cginc" +#include "LTCGI_shadowmap.cginc" + +#ifdef SHADER_TARGET_SURFACE_ANALYSIS +#define const +#endif + +// Main function - this calculates the approximated model for one pixel and one light +void LTCGI_Evaluate(ltcgi_input input, float3 worldNorm, float3 viewDir, float3x3 Minv, float roughness, const bool diffuse, out ltcgi_output output) { + output.input = input; + output.color = input.rawColor; // copy for colormode static + output.intensity = 0; + + // diffuse distance fade + #ifdef LTCGI_DISTANCE_FADE_APPROX + if (diffuse) // static branch, specular does not directly fade with distance + { + if (!input.flags.lmdOnly) { + // very approximate lol + float3 ctr = (input.Lw[0] + input.Lw[1]) * 0.5f; + if (dot(ctr, ctr) > LTCGI_DISTANCE_FADE_APPROX_MULT * LTCGI_DISTANCE_FADE_APPROX_MULT) + { + return; + } + } + } + #endif + + #define RET1_IF_LMDIFF [branch] if (/*const*/ diffuse && input.flags.diffFromLm) { output.intensity = 1.0f; return; } + + [branch] + if (input.flags.colormode == LTCGI_COLORMODE_SINGLEUV) { + float2 uv = input.uvStart; + if (uv.x < 0) uv.xy = uv.yx; + // TODO: make more configurable? + #ifdef LTCGI_VISUALIZE_SAMPLE_UV + output.color = float3(uv.xy, 0); + #elif !defined(SHADER_TARGET_SURFACE_ANALYSIS) + // sample video texture directly for accuracy + float3 sampled = _Udon_LTCGI_Texture_LOD0.SampleLevel(LTCGI_SAMPLER, uv.xy, 0).rgb; + output.color *= sampled; + #endif + + RET1_IF_LMDIFF + } + + #ifdef LTCGI_AUDIOLINK + [branch] + if (input.flags.colormode == LTCGI_COLORMODE_AUDIOLINK) { + float al = AudioLinkData(ALPASS_AUDIOLINK + uint2(0, input.flags.alBand)).r; + output.color *= al; + + RET1_IF_LMDIFF + } + #endif + + // create LTC polygon array + // note the order of source verts (keyword: winding order) + float3 L[5]; + L[0] = mul(Minv, input.Lw[0]); + L[1] = mul(Minv, input.Lw[1]); + L[2] = input.isTri ? L[1] : mul(Minv, input.Lw[3]); + L[3] = mul(Minv, input.Lw[2]); + L[4] = 0; + + // get texture coords (before clipping!) + [branch] + if (input.flags.colormode == LTCGI_COLORMODE_TEXTURE) { + float3 RN; + float2 uv = LTCGI_calculateUV(input.i, input.flags, L, input.isTri, input.uvStart, input.uvEnd, RN); + float planeAreaSquared = dot(RN, RN); + float planeDistxPlaneArea = dot(RN, L[0]); + + float3 sampled; + [branch] + if (diffuse) { // static branch + #ifdef LTCGI_BLENDED_DIFFUSE_SAMPLING + float3 sampled1; + LTCGI_sample(uv, 3, input.flags.texindex, 10, sampled1); + float3 sampled2; + LTCGI_sample(uv, 3, input.flags.texindex, 100, sampled2); + sampled = + sampled1 * 0.75 + + sampled2 * 0.25; + #else + LTCGI_sample(uv, 3, input.flags.texindex, 10, sampled); + #endif + } else { + float d = abs(planeDistxPlaneArea) / planeAreaSquared; + d *= LTCGI_UV_BLUR_DISTANCE; + d = log(d) / log(3.0); + + // a rough material must never show a perfect reflection, + // since our LOD0 texture is not prefiltered (and thus cannot + // depict any blur correctly) - without this there is artifacting + // on the border of LOD0 and LOD1 + d = clamp(d, saturate(roughness * 5.75), 1000); + + LTCGI_trilinear(uv, d, input.flags.texindex, sampled); + } + + // colorize output + output.color *= sampled; + } + + RET1_IF_LMDIFF + #undef RET1_IF_LMDIFF + + int n; + LTCGI_ClipQuadToHorizon(L, n); + + // early out if everything was clipped below horizon + [branch] + if (n == 0) + return; + + L[0] = normalize(L[0]); + L[1] = normalize(L[1]); + L[2] = normalize(L[2]); + L[3] = normalize(L[3]); + + // integrate + float sum = 0; + sum += LTCGI_IntegrateEdge(L[0], L[1]).z; + sum += LTCGI_IntegrateEdge(L[1], L[2]).z; + sum += LTCGI_IntegrateEdge(L[2], L[3]).z; + [branch] + if (n >= 4) + { + L[4] = normalize(L[4]); + sum += LTCGI_IntegrateEdge(L[3], L[4]).z; + [branch] + if (n == 5) + sum += LTCGI_IntegrateEdge(L[4], L[0]).z; + } + + // doublesided is accounted for with optimization at the start, so return abs + output.intensity = abs(sum); + return; +} + +// Calculate light contribution for all lights, +// call this from your shader and use the "diffuse" and "specular" outputs +// lmuv is the raw lightmap UV coordinate (e.g. UV1) +void LTCGI_Contribution( +#ifdef LTCGI_API_V2 + inout LTCGI_V2_CUSTOM_INPUT data, +#endif + float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv +#ifndef LTCGI_API_V2 + , inout half3 diffuse, inout half3 specular, out float totalSpecularIntensity, out float totalDiffuseIntensity +#endif +) { + #ifndef LTCGI_API_V2 + totalSpecularIntensity = 0; + totalDiffuseIntensity = 0; + #endif + + #ifdef LTCGI_SPECULAR_OFF + specular = 0; + #endif + #ifdef LTCGI_DIFFUSE_OFF + diffuse = 0; + #endif + + [branch] + if (_Udon_LTCGI_GlobalEnable == 0.0f) { + return; + } + + // sample lookup tables + float theta = LTCGI_acos_fast(dot(worldNorm, viewDir)); + float2 uv = float2(roughness, theta/(0.5*UNITY_PI)); + uv = uv*LUT_SCALE + LUT_BIAS; + + // calculate LTCGI custom lightmap UV and sample + float3 lms = LTCGI_SampleShadowmap(lmuv); + + #ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER + // sample BDRF approximation from lookup texture + float4 t = _Udon_LTCGI_lut1.SampleLevel(LTCGI_SAMPLER, uv, 0); + #endif + float3x3 Minv = float3x3( + float3( 1, 0, t.w), + float3( 0, t.z, 0), + float3(t.y, 0, t.x) + ); + + // construct orthonormal basis around N + float3 T1, T2; + T1 = normalize(viewDir - worldNorm*dot(viewDir, worldNorm)); + T2 = cross(worldNorm, T1); + + // for diffuse lighting we assume the identity matrix as BDRF, so the + // LTC approximation is directly equivalent to the orthonormal rotation matrix + float3x3 identityBrdf = float3x3(float3(T1), float3(T2), float3(worldNorm)); + // rotate area light in (T1, T2, N) basis for actual BRDF matrix as well + Minv = mul(Minv, identityBrdf); + + // specular brightness + float spec_amp = 1.0f; + #ifndef LTCGI_SPECULAR_OFF + #ifndef LTCGI_DISABLE_LUT2 + #ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER + spec_amp = _Udon_LTCGI_lut2.SampleLevel(LTCGI_SAMPLER, uv, 0).x; + #endif + #endif + #endif + + bool noLm = false; + #ifdef LTCGI_LTC_DIFFUSE_FALLBACK + #ifndef LTCGI_ALWAYS_LTC_DIFFUSE + #ifndef SHADER_TARGET_SURFACE_ANALYSIS + float2 lmSize; + _Udon_LTCGI_Lightmap.GetDimensions(lmSize.x, lmSize.y); + noLm = lmSize.x == 1; + #endif + #endif + #endif + #ifdef LTCGI_ALWAYS_LTC_DIFFUSE + noLm = true; + #endif + + // loop through all lights and add them to the output +#if MAX_SOURCES != 1 + uint count = min(_Udon_LTCGI_ScreenCount, MAX_SOURCES); + [loop] +#else + // mobile config + const uint count = 1; + [unroll(1)] +#endif + for (uint i = 0; i < count; i++) { + // skip masked and black lights + if (_Udon_LTCGI_Mask[i]) continue; + float4 extra = _Udon_LTCGI_ExtraData[i]; + float3 color = extra.rgb; + if (!any(color)) continue; + + ltcgi_flags flags = ltcgi_parse_flags(asuint(extra.w), noLm); + + #ifdef LTCGI_ALWAYS_LTC_DIFFUSE + // can't honor a lightmap-only light in this mode + if (flags.lmdOnly) continue; + #endif + + #ifdef LTCGI_TOGGLEABLE_SPEC_DIFF_OFF + // compile branches below away statically + flags.diffuse = flags.specular = true; + #endif + + // calculate (shifted) world space positions + float3 Lw[4]; + float4 uvStart = (float4)0, uvEnd = (float4)0; + bool isTri = false; + if (flags.lmdOnly) { + Lw[0] = Lw[1] = Lw[2] = Lw[3] = (float3)0; + } else { + LTCGI_GetLw(i, flags, worldPos, Lw, uvStart, uvEnd, isTri); + } + + // skip single-sided lights that face the other way + float3 screenNorm = cross(Lw[1] - Lw[0], Lw[2] - Lw[0]); + if (!flags.doublesided) { + if (dot(screenNorm, Lw[0]) < 0) + continue; + } + + float lm = 1; + if (flags.lmch) { + lm = lms[flags.lmch - 1]; + if (lm < 0.001) continue; + } + + ltcgi_input input; + input.i = i; + input.Lw = Lw; + input.isTri = isTri; + input.uvStart = uvStart; + input.uvEnd = uvEnd; + input.rawColor = color; + input.flags = flags; + input.screenNormal = screenNorm; + + // diffuse lighting + #ifndef LTCGI_DIFFUSE_OFF + [branch] + if (flags.diffuse) + { + float lmd = lm; + if (flags.lmch) { + if (flags.diffFromLm) + lmd *= _Udon_LTCGI_LightmapMult[flags.lmch - 1]; + else + lmd = smoothstep(0.0, LTCGI_SPECULAR_LIGHTMAP_STEP, saturate(lm - LTCGI_LIGHTMAP_CUTOFF)); + } + ltcgi_output diff; + diff.color = 0; + LTCGI_Evaluate(input, worldNorm, viewDir, identityBrdf, roughness, true, diff); + diff.intensity *= lmd; + + #ifdef LTCGI_API_V2 + LTCGI_V2_DIFFUSE_CALLBACK(data, diff); + #else + // simply accumulate all lights + diffuse += (diff.intensity * diff.color); + totalDiffuseIntensity += diff.intensity; + #endif + } + #endif + + // specular lighting + #ifndef LTCGI_SPECULAR_OFF + [branch] + if (flags.specular) + { + ltcgi_output spec; + spec.color = 0; + LTCGI_Evaluate(input, worldNorm, viewDir, Minv, roughness, false, spec); + spec.intensity *= spec_amp * smoothstep(0.0, LTCGI_SPECULAR_LIGHTMAP_STEP, saturate(lm - LTCGI_LIGHTMAP_CUTOFF)); + + #ifdef LTCGI_API_V2 + LTCGI_V2_SPECULAR_CALLBACK(data, spec); + #else + // simply accumulate all lights + specular += spec.intensity * spec.color; + totalSpecularIntensity += spec.intensity; + #endif + } + #endif + } +} + +// COMPATIBILITY FALLBACKS + +#ifndef LTCGI_API_V2 + +// missing totalSpecularIntensity, totalDiffuseIntensity, specular +void LTCGI_Contribution( + float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv, inout half3 diffuse +) { + half3 _u1 = (half3)0; + float _u2, _u3; + LTCGI_Contribution(worldPos, worldNorm, viewDir, roughness, lmuv, diffuse, _u1, _u2, _u3); +} + +// missing totalSpecularIntensity, totalDiffuseIntensity +void LTCGI_Contribution( + float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv, inout half3 diffuse, inout half3 specular +) { + float _u1, _u2; + LTCGI_Contribution(worldPos, worldNorm, viewDir, roughness, lmuv, diffuse, specular, _u1, _u2); +} + +// missing totalDiffuseIntensity +void LTCGI_Contribution( + float3 worldPos, float3 worldNorm, float3 viewDir, float roughness, float2 lmuv, inout half3 diffuse, inout half3 specular, out float totalSpecularIntensity +) { + float _u1; + LTCGI_Contribution(worldPos, worldNorm, viewDir, roughness, lmuv, diffuse, specular, totalSpecularIntensity, _u1); +} + +#endif + +/* + +Parts of the code in this file are adapted from the example code found here: + + https://github.com/selfshadow/ltc_code + +Modifications by _pi_ (@pimaker on GitHub), licensed under the terms of the +MIT license as far as applicable. + +Original copyright notice: + +Copyright (c) 2017, Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* If you use (or adapt) the source code in your own work, please include a + reference to the paper: + + Real-Time Polygonal-Light Shading with Linearly Transformed Cosines. + Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. + ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016. + Project page: https://eheitzresearch.wordpress.com/415-2/ + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#endif diff --git a/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_AudioLinkNoOp.cginc b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_AudioLinkNoOp.cginc new file mode 100644 index 0000000..3ae4ee9 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_AudioLinkNoOp.cginc @@ -0,0 +1 @@ +// this space intentionally left blank diff --git a/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_config.cginc b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_config.cginc new file mode 100644 index 0000000..91f6751 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_config.cginc @@ -0,0 +1,87 @@ +#ifndef LTCGI_CONFIG_INCLUDED +#define LTCGI_CONFIG_INCLUDED + +// Feel free to enable or disable (//) the options here. +// They will apply to all LTCGI materials in the project. +// Most of these can be changed in the LTCGI_Controller editor as well. + +/// No specular at all. +//#define LTCGI_SPECULAR_OFF +/// No diffuse at all. +//#define LTCGI_DIFFUSE_OFF +/// Disable the ability to toggle specular/diffuse on or off per screen. +#define LTCGI_TOGGLEABLE_SPEC_DIFF_OFF + +/// Only use LTC diffuse mode, never lightmapped diffuse. +/// This disables lightmaps entirely. +#define LTCGI_ALWAYS_LTC_DIFFUSE + +/// Double-sample screen texture for diffuse lighting to smooth resulting lighting +/// a bit more with global screen color data. Slight performance cost. +//#define LTCGI_BLENDED_DIFFUSE_SAMPLING + +/// Disable extra specular detail LUT, saves a sampler. +#define LTCGI_DISABLE_LUT2 + +/// Use bicubic filtering for LTCGI lightmap. Recommended on. +#define LTCGI_BICUBIC_LIGHTMAP + +/// Lightmap values below this will be treated as black for specular/LTC diffuse. +#define LTCGI_LIGHTMAP_CUTOFF 0.1 +/// Lightmap values above this (plus cutoff) will be treated as white. +#define LTCGI_SPECULAR_LIGHTMAP_STEP 0.3 + +/// Distance multiplier for calculating blur amount. +/// Increase to make reflections blurrier faster as distance increases. +#define LTCGI_UV_BLUR_DISTANCE 333 + +/// Fall back to LTC diffuse (from LM diffuse) on objects that are not marked static. +#define LTCGI_LTC_DIFFUSE_FALLBACK + +/// Approximation to ignore diffuse light for far away +/// lights, increase MULT or disable if you notice artifacting +#define LTCGI_DISTANCE_FADE_APPROX +/// Distance at which diffuse from screens will be ignored. +#define LTCGI_DISTANCE_FADE_APPROX_MULT 50 + + +// disabled editor from here on out +/// + + +// automatically kept in sync with LTCGI_Controller.cs +#define MAX_SOURCES 16 + +// set according to the LUT specified on CONTROLLER +#define LUT_SIZE 256 +static float LUT_SCALE = (LUT_SIZE - 1.0)/LUT_SIZE; +const float LUT_BIAS = 0.5/LUT_SIZE; + +// will be set automatically if audiolink is available and in use +//#define LTCGI_AUDIOLINK + +#ifdef LTCGI_AUDIOLINK +#ifndef AUDIOLINK_WIDTH +#ifndef AUDIOLINK_CGINC_INCLUDED +#include "LTCGI_AudioLinkNoOp.cginc" +#define AUDIOLINK_CGINC_INCLUDED +#endif +#endif +#endif + +// Bake screen data into texture for better performance. Disables moveable screens. +#define LTCGI_STATIC_UNIFORMS + +// Allow statically textured lights. +#define LTCGI_STATIC_TEXTURES + +// Enable support for cylindrical screens. +//#define LTCGI_CYLINDER + +// Activate avatar mode, which overrides certain configs from above. +//#define LTCGI_AVATAR_MODE + +// Slightly simplified and thus faster sampling for reflections at the cost of quality. +//#define LTCGI_FAST_SAMPLING + +#endif diff --git a/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_functions.cginc b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_functions.cginc new file mode 100644 index 0000000..08df1c7 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_functions.cginc @@ -0,0 +1,514 @@ +#ifndef LTCGI_FUNCTIONS_INCLUDED +#define LTCGI_FUNCTIONS_INCLUDED + +/* + LTC HELPERS +*/ + +float3 LTCGI_IntegrateEdge(float3 v1, float3 v2) +{ + float x = dot(v1, v2); + float y = abs(x); + + float a = 0.8543985 + (0.4965155 + 0.0145206*y)*y; + float b = 3.4175940 + (4.1616724 + y)*y; + float v = a / b; + float theta_sintheta = (x > 0.0) ? v : 0.5*rsqrt(max(1.0 - x*x, 1e-7)) - v; + + return cross(v1, v2) * theta_sintheta; +} + +void LTCGI_ClipQuadToHorizon(inout float3 L[5], out int n) +{ + // detect clipping config + uint config = 0; + if (L[0].z > 0.0) config += 1; + if (L[1].z > 0.0) config += 2; + if (L[2].z > 0.0) config += 4; + if (L[3].z > 0.0) config += 8; + + n = 0; + + // This [forcecase] only works when the cases are ordered in a specific manner. + // It gives like 10%-20% performance boost, so *make sure to leave it on*! + // If it breaks however, see if [branch] fixes it, and if it does, start + // reordering cases at random until it works again. + // It seems the compiler somehow optimizes away anything but setting 'n' in + // some orderings, including the ascending and descending ones. + // I wish I was joking. + [forcecase] + switch (config) { + case 13: // V1 V3 V4 clip V2 <- tl;dr: this fecker has to be first or shader go boom + n = 5; + L[4] = L[3]; + L[3] = L[2]; + L[2] = -L[1].z * L[2] + L[2].z * L[1]; + L[1] = -L[1].z * L[0] + L[0].z * L[1]; + break; + case 15: // V1 V2 V3 V4 - most common + n = 4; + break; + case 9: // V1 V4 clip V2 V3 + n = 4; + L[1] = -L[1].z * L[0] + L[0].z * L[1]; + L[2] = -L[2].z * L[3] + L[3].z * L[2]; + break; + case 0: // clip all + break; + case 1: // V1 clip V2 V3 V4 + n = 3; + L[1] = -L[1].z * L[0] + L[0].z * L[1]; + L[2] = -L[3].z * L[0] + L[0].z * L[3]; + L[3] = L[0]; + break; + case 2: // V2 clip V1 V3 V4 + n = 3; + L[0] = -L[0].z * L[1] + L[1].z * L[0]; + L[2] = -L[2].z * L[1] + L[1].z * L[2]; + L[3] = L[0]; + break; + case 3: // V1 V2 clip V3 V4 + n = 4; + L[2] = -L[2].z * L[1] + L[1].z * L[2]; + L[3] = -L[3].z * L[0] + L[0].z * L[3]; + break; + case 4: // V3 clip V1 V2 V4 + n = 3; + L[0] = -L[3].z * L[2] + L[2].z * L[3]; + L[1] = -L[1].z * L[2] + L[2].z * L[1]; + L[3] = L[0]; + break; + case 5: // V1 V3 clip V2 V4) impossible + break; + case 6: // V2 V3 clip V1 V4 + n = 4; + L[0] = -L[0].z * L[1] + L[1].z * L[0]; + L[3] = -L[3].z * L[2] + L[2].z * L[3]; + break; + case 7: // V1 V2 V3 clip V4 + n = 5; + L[4] = -L[3].z * L[0] + L[0].z * L[3]; + L[3] = -L[3].z * L[2] + L[2].z * L[3]; + break; + case 8: // V4 clip V1 V2 V3 + n = 3; + L[0] = -L[0].z * L[3] + L[3].z * L[0]; + L[1] = -L[2].z * L[3] + L[3].z * L[2]; + L[2] = L[3]; + break; + case 10: // V2 V4 clip V1 V3) impossible + break; + case 11: // V1 V2 V4 clip V3 + n = 5; + L[4] = L[3]; + L[3] = -L[2].z * L[3] + L[3].z * L[2]; + L[2] = -L[2].z * L[1] + L[1].z * L[2]; + break; + case 12: // V3 V4 clip V1 V2 + n = 4; + L[1] = -L[1].z * L[2] + L[2].z * L[1]; + L[0] = -L[0].z * L[3] + L[3].z * L[0]; + break; + case 14: // V2 V3 V4 clip V1 + n = 5; + L[4] = -L[0].z * L[3] + L[3].z * L[0]; + L[0] = -L[0].z * L[1] + L[1].z * L[0]; + break; + } + + // inlining these branches *unconditionally* breaks the [forcecase] above + // ...yeah I know + if (n == 3) + L[3] = L[0]; + if (n == 4) + L[4] = L[0]; +} + +/* + TEXTURE SAMPLING +*/ + +float2 LTCGI_inset_uv(float2 uv) +{ + return uv * 0.75 + float2(0.125, 0.125); +} + +half3 premul_alpha(half4 i) +{ + return i.rgb * i.a; +} + +half max2(half2 v) +{ + return max(v.x, v.y); +} + +void LTCGI_sample(float2 uv, uint lod, uint idx, float blend, out float3 result) +{ +#ifndef LTCGI_STATIC_TEXTURES + idx = 0; // optimize away the branches below +#endif + +#ifdef LTCGI_FAST_SAMPLING + #ifndef SHADER_TARGET_SURFACE_ANALYSIS + float outside = max2(abs(uv - 0.5f) - 0.5f); + float outmod = smoothstep(-0.1f, 0.1f, outside) * 2.5f; + blend = blend * 2.5f + outmod; + [branch] + if (idx == 0) + { + result = premul_alpha(_Udon_LTCGI_Texture_LOD0.SampleLevel(LTCGI_SAMPLER, uv, blend)); + } + #ifdef LTCGI_STATIC_TEXTURES + else + { + result = UNITY_SAMPLE_TEX2DARRAY_SAMPLER_LOD( + _Udon_LTCGI_Texture_LOD0_arr, + LTCGI_SAMPLER_RAW, + float3(uv, idx - 1), + blend + ).rgb; + } + #endif + #else + result = 0; + #endif +#else + result = 0; + + [branch] + if (lod == 0) + { + // if we're outside of the 0-1 UV space we must sample a prefiltered texture + [branch] + if(any(saturate(abs(uv - 0.5) - 0.5))) + { + lod = 1; + } + else + { + // LOD0 is the original texture itself, so not prefiltered, but we can + // approximate it a bit with trilinear lod + float lod = (1 - blend) * 1.5; + [branch] + if (idx == 0) + { + #ifndef SHADER_TARGET_SURFACE_ANALYSIS + result = premul_alpha(_Udon_LTCGI_Texture_LOD0.SampleLevel(LTCGI_SAMPLER, uv, lod)); + return; + #else + result = 0; + return; + #endif + } + #ifdef LTCGI_STATIC_TEXTURES + else + { + result = premul_alpha(UNITY_SAMPLE_TEX2DARRAY_SAMPLER_LOD( + _Udon_LTCGI_Texture_LOD0_arr, + LTCGI_SAMPLER_RAW, + float3(uv, idx - 1), + lod + )); + return; + } + #endif + } + } + + float2 ruv = LTCGI_inset_uv(uv); + + [branch] + if (idx == 0) + { + #ifndef SHADER_TARGET_SURFACE_ANALYSIS + switch (lod) + { + case 1: + result = _Udon_LTCGI_Texture_LOD1.SampleLevel(LTCGI_SAMPLER, ruv, 0).rgb; + return; + case 2: + result = _Udon_LTCGI_Texture_LOD2.SampleLevel(LTCGI_SAMPLER, ruv, 0).rgb; + return; + default: + result = _Udon_LTCGI_Texture_LOD3.SampleLevel(LTCGI_SAMPLER, ruv, blend*0.72).rgb; + return; + } + #else + result = 0; + return; + #endif + } + #ifdef LTCGI_STATIC_TEXTURES + else + { + [forcecase] + switch (lod) + { + case 1: + result = UNITY_SAMPLE_TEX2DARRAY_SAMPLER_LOD( + _Udon_LTCGI_Texture_LOD1_arr, + LTCGI_SAMPLER_RAW, + float3(ruv, idx - 1), + 0 + ).rgb; + return; + case 2: + result = UNITY_SAMPLE_TEX2DARRAY_SAMPLER_LOD( + _Udon_LTCGI_Texture_LOD2_arr, + LTCGI_SAMPLER_RAW, + float3(ruv, idx - 1), + 0 + ).rgb; + return; + default: + result = UNITY_SAMPLE_TEX2DARRAY_SAMPLER_LOD( + _Udon_LTCGI_Texture_LOD3_arr, + LTCGI_SAMPLER_RAW, + float3(ruv, idx - 1), + blend + ).rgb; + return; + } + } + #endif +#endif +} + +void LTCGI_trilinear(float2 uv, float d, uint idx, out float3 result) +{ +#ifdef LTCGI_FAST_SAMPLING + LTCGI_sample(uv, 0, idx, d, result); +#else + uint low = (uint)d; + uint high = low + 1; + + // DEBUG: colorize d/lod + //return float3(low == 0, low == 1, low == 2); + + if (low >= 3) + { + LTCGI_sample(uv, 3, idx, d - 3, result); + } + else + { + float amount = saturate(high - d); + float3 low_sample; + LTCGI_sample(uv, low, idx, amount, low_sample); + float3 high_sample; + LTCGI_sample(uv, high, idx, 0, high_sample); + + result = lerp(high_sample, low_sample, amount); + } +#endif +} + +/* + GENERIC HELPERS +*/ + +// from: https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/ +// max absolute error 9.0x10^-3 +// Eberly's polynomial degree 1 - respect bounds +// 4 VGPR, 12 FR (8 FR, 1 QR), 1 scalar +// input [-1, 1] and output [0, PI] +float LTCGI_acos_fast(float inX) +{ + float x = abs(inX); + float res = -0.156583f * x + UNITY_HALF_PI; + res *= sqrt(1.0f - x); + return (inX >= 0) ? res : UNITY_PI - res; +} + +bool LTCGI_tri_ray(float3 orig, float3 dir, float3 v0, float3 v1, float3 v2, out float2 bary) { + float3 v0v1 = v1 - v0; + float3 v0v2 = v2 - v0; + float3 pvec = cross(dir, v0v2); + float det = dot(v0v1, pvec); + float invDet = 1 / det; + + float3 tvec = orig - v0; + bary.x = dot(tvec, pvec) * invDet; + + float3 qvec = cross(tvec, v0v1); + bary.y = dot(dir, qvec) * invDet; + + // return false when other triangle of quad should be sampled, + // i.e. we went over the diagonal line + return bary.x >= 0; +} + +float2 LTCGI_rotateVector(float2 x, float angle) +{ + float c = cos(angle); + float s = sin(angle); + return mul(float2x2(c,s,-s,c), x); +} + +/*float LTCGI_remap(float3 from, float3 to, float2 targetFrom, float2 targetTo, float3 value) +{ + float rel = (value - from) / (to - from); + return lerp(targetFrom, targetTo, rel); +}*/ + +float2 LTCGI_calculateUV(uint i, ltcgi_flags flags, float3 L[5], bool isTri, float4 uvStart, float4 uvEnd, out float3 ray) +{ + // calculate perpendicular vector to plane defined by area light + float3 E1 = L[1] - L[0]; + float3 E2 = L[3] - L[0]; + ray = cross(E1, E2); + + // raycast it against the two triangles formed by the quad + float2 bary; + bool hit0 = LTCGI_tri_ray(0, ray, L[0], L[2], L[3], bary) || isTri; + if (!hit0) { + LTCGI_tri_ray(0, ray, L[0], L[1], L[2], bary); + } + + float3 bary3 = float3(bary, 1 - bary.x - bary.y); + float2 uv; + if (hit0) + uv = uvEnd.zw * bary3.x + uvEnd.xy * bary3.y; + else + uv = uvStart.zw * bary3.x + uvEnd.zw * bary3.y; + return uv + uvStart.xy * bary3.z; +} + +/* + EXPERIMENTAL: CYLINDER HELPER +*/ + +void LTCGI_GetLw(uint i, ltcgi_flags flags, float3 worldPos, out float3 Lw[4], out float4 uvStart, out float4 uvEnd, out bool isTri) { + bool cylinder = false; + #ifdef LTCGI_CYLINDER + // statically optimize out branch below in case disabled + cylinder = flags.cylinder; + #endif + + float4 v0 = _Udon_LTCGI_Vertices_0_get(i); + float4 v1 = _Udon_LTCGI_Vertices_1_get(i); + float4 v2 = _Udon_LTCGI_Vertices_2_get(i); + float4 v3 = _Udon_LTCGI_Vertices_3_get(i); + + [branch] + if (cylinder) { + // construct data according to worldPos to create aligned + // rectangle tangent to the cylinder + + float3 in_base = v0.xyz; + float in_height = v0.w; + float in_radius = v1.w; + float in_size = v2.w; + float in_angle = v3.w; + + // get angle between 2D unit plane and vector pointing from cylinder to shade point + float2 towardsCylinder = LTCGI_rotateVector((in_base - worldPos).xz, -in_angle); + float angle = atan2(towardsCylinder.x, towardsCylinder.y); + // clamp angle to size parameter, i.e. "width" of lit surface area + float angleClamped = clamp(angle, -in_size, in_size) + in_angle; + // construct vector that *most* faces shade point + float2 facing = float2(sin(angleClamped), cos(angleClamped)); + // tangent of rectangular screen on cylinder surface used for calculating lighting for shade point + float2 tangent = float2(facing.y, -facing.x); + float2 onCylinderFacing = facing * in_radius; + + // clip ends, approximately + float rclip = saturate(lerp(1, 0, (angleClamped - in_angle) - (in_size - UNITY_HALF_PI*0.5f))); + float lclip = saturate(lerp(1, 0, -(angleClamped - in_angle) - (in_size - UNITY_HALF_PI*0.5f))); + + float2 p1 = in_base.xz - onCylinderFacing + tangent * in_radius * lclip; + float2 p2 = in_base.xz - onCylinderFacing - tangent * in_radius * rclip; + + Lw[0] = float3(p1.x, in_base.y, p1.y) - worldPos; + Lw[1] = float3(p1.x, in_base.y + in_height, p1.y) - worldPos; + Lw[2] = float3(p2.x, in_base.y, p2.y) - worldPos; + Lw[3] = float3(p2.x, in_base.y + in_height, p2.y) - worldPos; + + isTri = false; + + // UV depends on "viewing" angle of the shade point towards the cylinder + float2 viewDir = normalize((in_base - worldPos).xz); + // forwardAngle == atan2(cos(in_angle), sin(in_angle)); but only negative + float forwardAngle = -in_angle + UNITY_HALF_PI; + // offset from center of screen forward to the side ends, positive goes left/ccw fpv top, + // sine to account for the fact we're rotating around a cylinder which has depth + float viewAngle = forwardAngle - atan2(viewDir.y, viewDir.x); + // prevent rollover, since we need to clamp we must stay withing [-pi, pi] + if (viewAngle < -UNITY_PI) + viewAngle += UNITY_TWO_PI; + if (viewAngle > UNITY_PI) + viewAngle -= UNITY_TWO_PI; + viewAngle = clamp(viewAngle * 0.5f, -in_size, in_size); + viewAngle = sin(viewAngle); + // full view UVs, but shifted left/right depending on view angle + float2 uvStart2 = float2(1 - saturate(viewAngle), 0); + float2 uvEnd2 = float2(1 - saturate(viewAngle + 1), 1); + uvStart = float4(uvStart2.x, uvStart2.y, uvStart2.x, uvEnd2.y); + uvEnd = float4(uvEnd2.x, uvStart2.y, uvEnd2.x, uvEnd2.y); + + } else { + // use passed in data, offset around worldPos + Lw[0] = v0.xyz - worldPos; + Lw[1] = v1.xyz - worldPos; + Lw[2] = v2.xyz - worldPos; + Lw[3] = v3.xyz - worldPos; + #ifndef SHADER_TARGET_SURFACE_ANALYSIS + uvStart = _Udon_LTCGI_static_uniforms[uint2(4, i)]; + uvEnd = _Udon_LTCGI_static_uniforms[uint2(5, i)]; + #else + uvStart = float4(0, 0, 1, 0); + uvEnd = float4(1, 1, 0, 1); + #endif + + // we only detect triangles for "blender" import configuration, as those are the only + // ones that can actually be triangles (I think?) + isTri = /*distance(Lw[2], Lw[3]) < 0.001 || */distance(Lw[1], Lw[3]) < 0.001; + } +} + +#endif + +/* + +Parts of the code in this file are adapted from the example code found here: + + https://github.com/selfshadow/ltc_code + +Modifications by _pi_ (@pimaker on GitHub), licensed under the terms of the +MIT license as far as applicable. + +Original copyright notice: + +Copyright (c) 2017, Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* If you use (or adapt) the source code in your own work, please include a + reference to the paper: + + Real-Time Polygonal-Light Shading with Linearly Transformed Cosines. + Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. + ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016. + Project page: https://eheitzresearch.wordpress.com/415-2/ + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/
\ No newline at end of file diff --git a/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_shadowmap.cginc b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_shadowmap.cginc new file mode 100644 index 0000000..2c4a598 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_shadowmap.cginc @@ -0,0 +1,93 @@ +#ifndef LTCGI_SHADOWMAP_INCLUDED +#define LTCGI_SHADOWMAP_INCLUDED + +// Adapted from: https://gitlab.com/s-ilent/filamented +// Licensed under the terms of the Apache License 2.0 +// Full text: https://gitlab.com/s-ilent/filamented/-/blob/master/LICENSE +// +// Conforming to the terms of the above license, this file is redistributed +// under the terms of the MIT license as part of the LTCGI shader package, +// provided this notice is kept. + +#ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER + +float4 LTCGI_cubic(float v) +{ + float4 n = float4(1.0, 2.0, 3.0, 4.0) - v; + float4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return float4(x, y, z, w); +} + +// Unity's SampleTexture2DBicubic doesn't exist in 2018, which is our target here. +// So this is a similar function with tweaks to have similar semantics. + +float4 LTCGI_SampleTexture2DBicubicFilter(Texture2D tex, SamplerState smp, float2 coord, float4 texSize, bool lightmap = false) +{ + coord = coord * texSize.xy - 0.5; + float fx = frac(coord.x); + float fy = frac(coord.y); + coord.x -= fx; + coord.y -= fy; + + float4 xcubic = LTCGI_cubic(fx); + float4 ycubic = LTCGI_cubic(fy); + + float4 c = float4(coord.x - 0.5, coord.x + 1.5, coord.y - 0.5, coord.y + 1.5); + float4 s = float4(xcubic.x + xcubic.y, xcubic.z + xcubic.w, ycubic.x + ycubic.y, ycubic.z + ycubic.w); + float4 offset = c + float4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) / s; + + float4 sample0 = tex.Sample(smp, float2(offset.x, offset.z) * texSize.zw); + float4 sample1 = tex.Sample(smp, float2(offset.y, offset.z) * texSize.zw); + float4 sample2 = tex.Sample(smp, float2(offset.x, offset.w) * texSize.zw); + float4 sample3 = tex.Sample(smp, float2(offset.y, offset.w) * texSize.zw); + + if (lightmap) { + sample0 = float4(DecodeLightmap(sample0), 1.0); + sample1 = float4(DecodeLightmap(sample1), 1.0); + sample2 = float4(DecodeLightmap(sample2), 1.0); + sample3 = float4(DecodeLightmap(sample3), 1.0); + } + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return lerp( + lerp(sample3, sample2, sx), + lerp(sample1, sample0, sx), sy); +} + +float4 LTCGI_SampleShadowmap(float2 lmuv) +{ + #ifdef LTCGI_ALWAYS_LTC_DIFFUSE + return 1; + #else + lmuv = lmuv * _Udon_LTCGI_LightmapST.xy + _Udon_LTCGI_LightmapST.zw; + + #ifdef LTCGI_BICUBIC_LIGHTMAP + float width, height; + _Udon_LTCGI_Lightmap.GetDimensions(width, height); + + float4 _Udon_LTCGI_Lightmap_TexelSize = float4(width, height, 1.0/width, 1.0/height); + + return LTCGI_SampleTexture2DBicubicFilter( + _Udon_LTCGI_Lightmap, LTCGI_SAMPLER, + lmuv, _Udon_LTCGI_Lightmap_TexelSize, + true + ); + #else + fixed4 sample = _Udon_LTCGI_Lightmap.Sample(LTCGI_SAMPLER, lmuv); + return float4(DecodeLightmap(sample), 1.0); + #endif + #endif +} + +#else +// surface shader analysis stub +float4 LTCGI_SampleShadowmap(float2 lmuv) { return 1; } +#endif + +#endif
\ No newline at end of file diff --git a/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_structs.cginc b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_structs.cginc new file mode 100644 index 0000000..164887f --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_structs.cginc @@ -0,0 +1,43 @@ +#ifndef LTCGI_STRUCTS_INCLUDED +#define LTCGI_STRUCTS_INCLUDED + +#define LTCGI_COLORMODE_STATIC 0 +#define LTCGI_COLORMODE_TEXTURE 1 +#define LTCGI_COLORMODE_SINGLEUV 2 +#define LTCGI_COLORMODE_AUDIOLINK 3 + +struct ltcgi_flags +{ + bool doublesided; // if the light is doublesided or only illuminates the front face + bool diffFromLm; // diffuse lighting intensity will not be calculated via LTC but taken directly from the lightmap + bool specular; // if the light has a specular component + bool diffuse; // if the light has a diffuse component + uint colormode; // colormode, see above + uint texindex; // index of the texture to shade with, if colormode == LTCGI_COLORMODE_TEXTURE + uint lmch, lmidx; // lightmap channel and index + bool cylinder; // is this light a cylinder + uint alBand; // audiolink band if colormode == LTCGI_COLORMODE_AUDIOLINK + bool lmdOnly; // if this light is lightmap-diffuse _only_, that is, no LTC will be run (Lw will be all 0 in that case) - this will never be true on avatars (with LTCGI_ALWAYS_LTC_DIFFUSE) +}; + +struct ltcgi_input +{ + uint i; // light number + float3 Lw[4]; // world space area light vertices, Lw[1] == Lw[3] for triangle lights, shifted by input worldPos (i.e. world space position as seen from (0, 0, 0)) + bool isTri; // if this is a triangle light, quad if false + float4 uvStart; // defines the UV layout of the area (xy = top-left, zw=top-right) + float4 uvEnd; // defines the UV layout of the area (xy = bottom-left, zw=bottom-right), different use for cylinders + float3 rawColor; // the raw light color, unaffected by any colormode calculations (i.e. exactly what's given as "color" in editor) + float3 screenNormal; // world space normal direction of area light + ltcgi_flags flags; // flags, see above +}; + +struct ltcgi_output +{ + ltcgi_input input; // input data that resulted in this output + + float intensity; // intensity output by LTC calculation + float3 color; // color output by LTCGI colormode calculation +}; + +#endif
\ No newline at end of file diff --git a/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_uniform.cginc b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_uniform.cginc new file mode 100644 index 0000000..2bf4673 --- /dev/null +++ b/Third_Party/at.pimaker.ltcgi/Shaders/LTCGI_uniform.cginc @@ -0,0 +1,146 @@ +#ifndef LTCGI_UNIFORM_INCLUDED +#define LTCGI_UNIFORM_INCLUDED + +// global sampler (trilinear) +#ifndef LTCGI_SAMPLER +SamplerState sampler_LTCGI_trilinear_clamp_sampler; +#define LTCGI_SAMPLER sampler_LTCGI_trilinear_clamp_sampler +#define LTCGI_SAMPLER_RAW _LTCGI_trilinear_clamp_sampler +#endif + +// LUTs +#ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER +#ifndef LTCGI_DISABLE_LUT2 +uniform Texture2D<float4> _Udon_LTCGI_lut2; +#endif +uniform Texture2D<float4> _Udon_LTCGI_lut1; +#endif + +#ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER +uniform Texture2D<float4> _Udon_LTCGI_static_uniforms; +#endif + +#ifdef LTCGI_STATIC_UNIFORMS + +float4 _Udon_LTCGI_Vertices_0_get(uint i) { + return _Udon_LTCGI_static_uniforms[uint2(0, i)]; +} +float4 _Udon_LTCGI_Vertices_1_get(uint i) { + return _Udon_LTCGI_static_uniforms[uint2(1, i)]; +} +float4 _Udon_LTCGI_Vertices_2_get(uint i) { + return _Udon_LTCGI_static_uniforms[uint2(2, i)]; +} +float4 _Udon_LTCGI_Vertices_3_get(uint i) { + return _Udon_LTCGI_static_uniforms[uint2(3, i)]; +} + +#else + +// vertices in object space; w component is UV (legacy) +uniform float4 _Udon_LTCGI_Vertices_0[MAX_SOURCES]; +uniform float4 _Udon_LTCGI_Vertices_1[MAX_SOURCES]; +uniform float4 _Udon_LTCGI_Vertices_2[MAX_SOURCES]; +uniform float4 _Udon_LTCGI_Vertices_3[MAX_SOURCES]; + +float4 _Udon_LTCGI_Vertices_0_get(uint i) { + return _Udon_LTCGI_Vertices_0[i]; +} +float4 _Udon_LTCGI_Vertices_1_get(uint i) { + return _Udon_LTCGI_Vertices_1[i]; +} +float4 _Udon_LTCGI_Vertices_2_get(uint i) { + return _Udon_LTCGI_Vertices_2[i]; +} +float4 _Udon_LTCGI_Vertices_3_get(uint i) { + return _Udon_LTCGI_Vertices_3[i]; +} + +#endif + +// light source count, maximum is MAX_SOURCES +uniform uint _Udon_LTCGI_ScreenCount; + +// per-renderer mask to select sources, +// for max perf update _Udon_LTCGI_ScreenCount too +uniform bool _Udon_LTCGI_Mask[MAX_SOURCES]; + +// extra data per light source, layout: +// color.r color.g color.b flags* +// * b0=double-sided, b1=diffuse-from-lightmap, b2=specular, b3=diffuse, +// b4-b7=texture index (0=video, (n>0)=n-1) +// b8-b9=color mode +// b10-b11=lightmap channel (0=disabled, 1=r, 2=g, 3=b) +// b12=cylinder +// b13-14=audio link band +// b15=lightmap diffuse only +// (color black = fully disabled) +uniform float4 _Udon_LTCGI_ExtraData[MAX_SOURCES]; + +ltcgi_flags ltcgi_parse_flags(uint val, bool noLmDiff) +{ + ltcgi_flags ret = (ltcgi_flags)0; + ret.doublesided = (val & 1) == 1; + + #ifdef LTCGI_ALWAYS_LTC_DIFFUSE + ret.diffFromLm = false; + #else + ret.diffFromLm = !noLmDiff && (val & 2) == 2; + #endif + + ret.diffuse = (val & 8) == 8; + + ret.specular = (val & 4) == 4; + ret.texindex = (val & 0xf0) >> 4; + ret.colormode = (val & 0x300) >> 8; + + #ifdef LTCGI_ALWAYS_LTC_DIFFUSE + ret.lmch = 0; + #else + ret.lmch = (val & 0xC00) >> 10; + #endif + + ret.cylinder = (val & (1 << 12)) == (1 << 12); + + #ifdef LTCGI_AUDIOLINK + ret.alBand = (val & 0x6000) >> 13; + #endif + + ret.lmdOnly = (val & (1 << 15)) == (1 << 15); + + return ret; +} + +// video input +#ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER +uniform Texture2D<float4> _Udon_LTCGI_Texture_LOD0; +#ifndef LTCGI_FAST_SAMPLING +uniform Texture2D<float4> _Udon_LTCGI_Texture_LOD1; +uniform Texture2D<float4> _Udon_LTCGI_Texture_LOD2; +uniform Texture2D<float4> _Udon_LTCGI_Texture_LOD3; +#endif +#endif + +// static textures +#ifdef LTCGI_STATIC_TEXTURES +UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(_Udon_LTCGI_Texture_LOD0_arr); +#ifndef LTCGI_FAST_SAMPLING +UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(_Udon_LTCGI_Texture_LOD1_arr); +UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(_Udon_LTCGI_Texture_LOD2_arr); +UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(_Udon_LTCGI_Texture_LOD3_arr); +#endif +#endif + +// lightmap +#ifndef SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER +#ifndef LTCGI_ALWAYS_LTC_DIFFUSE +uniform Texture2D<float4> _Udon_LTCGI_Lightmap; +#endif +#endif +uniform float3 _Udon_LTCGI_LightmapMult; +uniform float4 _Udon_LTCGI_LightmapST; + +// global toggle +uniform float _Udon_LTCGI_GlobalEnable; + +#endif
\ No newline at end of file |
