├── .gitignore ├── Assets └── Shaders │ ├── SekaiToon-inputs.hlsli │ ├── SekaiToon-outlines.hlsl │ ├── SekaiToon.shader │ ├── SekaiToon-base.hlsl │ ├── SekaiToon-helpers.hlsl │ └── OpenLit-core.hlsl └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Assets/Hai/ 2 | Assets/LightboxScenes/ 3 | Assets/Materials/ 4 | Assets/Models/ 5 | Assets/Scenes/ 6 | Assets/Scripts/ 7 | Assets/Shaders/OpenToonLit.shader 8 | Library/ 9 | Logs/ 10 | Packages/ 11 | ProjectSettings/ 12 | Temp/ 13 | Thry/ 14 | *.meta 15 | -------------------------------------------------------------------------------- /Assets/Shaders/SekaiToon-inputs.hlsli: -------------------------------------------------------------------------------- 1 | struct vsIn{ 2 | vector vertex : POSITION; // object space 3 | vector normal : NORMAL; // object space 4 | vector tangent : TANGENT; 5 | vector uv : TEXCOORD0; 6 | vector vertexcol : COLOR0; 7 | }; 8 | 9 | struct vsOut{ 10 | vector pos : SV_POSITION; // clip space vertex positions 11 | vector normalOS : NORMAL; 12 | vector tangent : TANGENT; 13 | vector vertexLight : TEXCOORD0; 14 | vector vertexOS : TEXCOORD1; 15 | vector uv : TEXCOORD2; 16 | UNITY_FOG_COORDS(3) 17 | UNITY_SHADOW_COORDS(4) 18 | OpenLitLightDatas lightData : TEXCOORD5; 19 | vector vertexcol : COLOR0; 20 | }; 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 |

This README.md is WIP

4 | 5 | --- 6 | 7 | ## Contact / Issues 8 | - [Discord server](https://discord.gg/85rP9SpAkF) 9 | - [Twitter](https://twitter.com/festivizing) 10 | - Either contact me or [create an issue](https://github.com/festivize/SekaiToon/issues/new/choose) for any problems that may arise. 11 | - Please don't bother me for assets. 12 | 13 | ## Rules 14 | - If you use this shader as is in avatars for VRChat, renders, animations or any form of medium that does not directly modify the shader, I'd appreciate being credited - **you don't have to do it though.** 15 | - If you use this shader as the main reference for your own shader, please give credit where it's due. 16 | 17 | ## Contributing 18 | Feel free to create a pull request and I'll do my best to get to it ^^ 19 | 20 | ## Special thanks 21 | All of this wouldn't be possible if it weren't for: 22 | - [KH40](https://github.com/KH40-khoast40/)'s [Shadekai](https://github.com/KH40-khoast40/Shadekai/) 23 | - [Manashiku](https://github.com/Manashiku/) 24 | - Yukikami 25 | - TsunBun 26 | - [pjsek.ai](https://pjsek.ai/) & [sekai.best](https://sekai.best/) 27 | 28 | ## Disclaimer 29 | This shader isn't meant to be 100% accurate - what I only aim for is to replicate the in-game looks to the best of my ability. Some calculations are exactly how the game does things, some are my own thrown into the mix. 30 | 31 | While the shader is developed primarily for datamined assets, this repository does not endorse datamining in any way whatsoever and will never directly provide the assets nor tools in extracting from game files. 32 | -------------------------------------------------------------------------------- /Assets/Shaders/SekaiToon-outlines.hlsl: -------------------------------------------------------------------------------- 1 | #include "SekaiToon-inputs.hlsli" 2 | 3 | #include "SekaiToon-helpers.hlsl" 4 | 5 | vsOut vert(vsIn i){ 6 | vsOut o; 7 | //o.pos = mul(UNITY_MATRIX_MVP, i.vertex); // transform to clip space 8 | o.normalOS = i.normal; 9 | o.tangent = i.tangent; 10 | o.vertexOS = i.vertex; 11 | o.uv = (_ToggleLongTex != 0) ? vector(i.uv.x * 0.5, i.uv.y) : i.uv; // vs_TEXCOORD1 12 | o.vertexcol.x = i.vertexcol.x; // outline direction, EdgeScale_view @ line 246 13 | o.vertexcol.y = i.vertexcol.y; // rim intensity, RimScale_view @ line 247 14 | o.vertexcol.z = i.vertexcol.z; // eyebrow mask, used for making them appear in front of the hair at all times 15 | o.vertexcol.w = i.vertexcol.w; // UNUSED 16 | 17 | const vector vertexWS = mul(UNITY_MATRIX_M, i.vertex); // transform to world space 18 | 19 | if(_OutlineType != 0){ 20 | const half _OutlineCorrectionWidth = 2.25; 21 | const vector _OutlineWidthAdjustScales = vector(0.01, 0.245, 0.6, 0.0); 22 | const vector _OutlineWidthAdjustZs = vector(0.001, 2.0, 6.0, 0.0); 23 | 24 | // formula is literally just how miHoYo does outlines 25 | vector vViewPosition = mul(UNITY_MATRIX_MV, o.vertexOS); 26 | half fovScale = (2.41400003 / unity_CameraProjection[1][1]) * -vViewPosition.z; 27 | 28 | vector zRange, scales; 29 | if (fovScale < _OutlineWidthAdjustZs.y){ 30 | zRange = _OutlineWidthAdjustZs.xy; 31 | scales = _OutlineWidthAdjustScales.xy; 32 | } 33 | else{ 34 | zRange = _OutlineWidthAdjustZs.yz; 35 | scales = _OutlineWidthAdjustScales.yz; 36 | } 37 | fovScale = lerpByZ(scales.x, scales.y, zRange.x, zRange.y, fovScale); 38 | vector scale; 39 | scale = _OutlineWidth * _OutlineCorrectionWidth; 40 | fovScale *= scale.x; 41 | fovScale *= 4000.0; 42 | fovScale *= 0.414250195; 43 | // base outline thickness 44 | fovScale *= o.vertexcol.x; 45 | 46 | half zOffset = 0.000125; 47 | 48 | // get outline direction, can be either the raw normals (HORRIBLE) or the custom tangents 49 | vector outlineDirection; 50 | switch(_OutlineType){ 51 | case 1: 52 | outlineDirection = o.normalOS; 53 | break; 54 | case 2: 55 | outlineDirection = o.tangent.xyz; 56 | break; 57 | default: 58 | break; 59 | } 60 | 61 | // get camera view direction 62 | vector viewDirWS = normalize(_WorldSpaceCameraPos - vertexWS); 63 | 64 | vViewPosition = vector(scale.xyz, 0); 65 | vViewPosition.xyz = vViewPosition.xyz * outlineDirection.xyz * fovScale; 66 | vViewPosition = vViewPosition - mul(unity_WorldToObject, viewDirWS) * zOffset; 67 | vViewPosition += o.vertexOS; 68 | // convert to clip space 69 | vViewPosition = mul(UNITY_MATRIX_MVP, vViewPosition); 70 | 71 | o.pos = vViewPosition; 72 | } 73 | else{ 74 | o.pos = vector(0.0, 0.0, 0.0, 0.0); 75 | } 76 | 77 | // compute vertex lights 78 | o.vertexLight = ComputeAdditionalLights(vertexWS, o.pos); 79 | o.vertexLight = min(o.vertexLight, 10); 80 | 81 | // compute light direction 82 | OpenLitLightDatas lightData; 83 | ComputeLights(lightData, vector(1.0, 1.0, 0.0, 0.0) * 0.001); 84 | o.lightData = lightData; 85 | 86 | UNITY_TRANSFER_FOG(o, o.pos); 87 | 88 | return o; 89 | } 90 | 91 | vector frag(vsOut i) : SV_Target{ 92 | /* TEXTURE CREATION */ 93 | 94 | const vector mainTex = _MainTex.Sample(sampler_MainTex, i.uv); 95 | 96 | /* END OF TEXTURE CREATION */ 97 | 98 | 99 | /* ENVIRONMENT LIGHTING */ 100 | 101 | vector envLighting = max(max(i.lightData.directLight, i.lightData.indirectLight), i.vertexLight); 102 | 103 | /* END OF ENVIRONMENT LIGHTING */ 104 | 105 | 106 | /* COLOR CREATION */ 107 | 108 | vector finalColor = mainTex * 0.375; 109 | 110 | // apply environment lighting 111 | finalColor.xyz = lerp(finalColor.xyz, (blendColorBurn(finalColor.xyz, 112 | min(envLighting, 1), 0.6) * min(envLighting, 1)), _envLightingStrength); 113 | 114 | // apply fog 115 | UNITY_APPLY_FOG(i.fogCoord, finalColor); 116 | 117 | /* END OF COLOR CREATION */ 118 | 119 | 120 | return finalColor; 121 | } 122 | -------------------------------------------------------------------------------- /Assets/Shaders/SekaiToon.shader: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // SekaiToon [Project Sekai shader for Unity (Built-in Rendering Pipeline)] 4 | // 5 | // Originally for MMD by: KH40 (khoast40) - https://github.com/KH40-khoast40/Shadekai/ 6 | // Fork by: festivity - https://github.com/festivize/SekaiToon/ 7 | // Base shader: 舞力介入P 8 | // Special thanks: Yukikami, lilxyzw 9 | // 10 | //////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | Shader ".festivity/SekaiToon/SekaiToon-main"{ 13 | Properties{ 14 | [Header(Textures)] [MainTex] [NoScaleOffset] [HDR] [Space(10)] _MainTex ("Diffuse", 2D) = "white"{} 15 | [NoScaleOffset] [HDR] _ShadowTex ("Shadow", 2D) = "black"{} 16 | [NoScaleOffset] _ValueTex ("Lightmap", 2D) = "white"{} 17 | [Toggle] _ToggleLongTex ("Long Textures? (turn this on if textures have an aspect ratio of 2:1", Range(0.0, 1.0)) = 0.0 18 | 19 | [Header(Character Selection)] [Space(10)] [IntRange] _CharacterId ("Select Character", Range(1.0, 21.0)) = 1.0 20 | 21 | [Header(Main Settings)] [Space(10)] [Gamma] [HideInInspector] _DefaultSkinColor ("Default Skin Color", Color) = (0.9921875, 0.9609375, 0.921875, 1.0) 22 | [Gamma] [HideInInspector] _Shadow1SkinColor ("Shadow 1 Skin Color", Color) = (0.890625, 0.7695313, 0.796875, 1.0) 23 | [Gamma] [HideInInspector] _Shadow2SkinColor ("Shadow 2 Skin Color", Color) = (0.796875, 0.59375, 0.6367188, 1.0) 24 | 25 | [Header(Lighting)] [Space(10)] _ShadowStrength ("Shadow Strength", Range(0.0, 1.0)) = 1.0 26 | _ShadowPush ("Shadow Push", Range(-2.0, 2.0)) = 0.0 27 | _ShadowSmoothness ("Shadow Smoothness", Range(0.0, 1.0)) = 0.01 28 | _envLightingStrength ("Environment Lighting Strength", Range(0.0, 1.0)) = 1.0 29 | _RimIntensity ("Rim Light Strength", Range(0.0, 1.0)) = 0.5 30 | _RimLength ("Rim Light Length", Range(0.0, 1.0)) = 0.25 31 | _RimThickness ("Rim Light Thickness", Range(0.0, 1.0)) = 0.65 32 | [Gamma] _RimColor ("Rim Light Color", Color) = (1.0, 1.0, 1.0, 1.0) 33 | 34 | [Header(Outlines)] [Space(10)] [KeywordEnum(None, Normal, Tangent)] _OutlineType ("Outline Type", Float) = 1.0 35 | _OutlineWidth ("Outline Thickness", Float) = 0.001 36 | 37 | [Header(Debugging)] [Space(10)] [Toggle] _ReturnLightmapR ("Show Lightmap Red", Range(0.0, 1.0)) = 0.0 38 | [Toggle] _ReturnLightmapG ("Show Lightmap Green", Range(0.0, 1.0)) = 0.0 39 | [Toggle] _ReturnLightmapB ("Show Lightmap Blue", Range(0.0, 1.0)) = 0.0 40 | [Toggle] _ReturnVertexColorR ("Show Vertex Color Red", Range(0.0, 1.0)) = 0.0 41 | [Toggle] _ReturnVertexColorG ("Show Vertex Color Green", Range(0.0, 1.0)) = 0.0 42 | [Toggle] _ReturnVertexColorB ("Show Vertex Color Blue", Range(0.0, 1.0)) = 0.0 43 | [Toggle] _ReturnNormals ("Show Normals", Range(0.0, 1.0)) = 0.0 44 | [Toggle] _ReturnTangents ("Show Tangents", Range(0.0, 1.0)) = 0.0 45 | } 46 | SubShader{ 47 | Tags{ 48 | "RenderType" = "Opaque" 49 | "Queue" = "Geometry+70" 50 | } 51 | 52 | HLSLINCLUDE 53 | 54 | #pragma vertex vert 55 | #pragma fragment frag 56 | 57 | #pragma multi_compile _ UNITY_HDR_ON 58 | #pragma multi_compile_fog 59 | #pragma skip_variants LIGHTMAP_ON DYNAMICLIGHTMAP_ON LIGHTMAP_SHADOW_MIXING SHADOWS_SHADOWMASK DIRLIGHTMAP_COMBINED 60 | 61 | #include "UnityCG.cginc" 62 | #include "Lighting.cginc" 63 | #include "AutoLight.cginc" 64 | #include "UnityLightingCommon.cginc" 65 | #include "UnityShaderVariables.cginc" 66 | 67 | #include "OpenLit-core.hlsl" // https://github.com/lilxyzw/OpenLit/ 68 | 69 | 70 | /* properties */ 71 | 72 | Texture2D _MainTex; SamplerState sampler_MainTex; 73 | Texture2D _ShadowTex; SamplerState sampler_ShadowTex; 74 | Texture2D _ValueTex; SamplerState sampler_ValueTex; 75 | float _ToggleLongTex; 76 | 77 | float _CharacterId; 78 | 79 | vector _DefaultSkinColor; 80 | vector _Shadow1SkinColor; 81 | vector _Shadow2SkinColor; 82 | 83 | float _ShadowStrength; 84 | float _ShadowPush; 85 | float _ShadowSmoothness; 86 | float _envLightingStrength; 87 | float _RimIntensity; 88 | float _RimLength; 89 | float _RimThickness; 90 | vector _RimColor; 91 | 92 | float _OutlineType; 93 | float _OutlineWidth; 94 | 95 | float _ReturnLightmapR; 96 | float _ReturnLightmapG; 97 | float _ReturnLightmapB; 98 | float _ReturnVertexColorR; 99 | float _ReturnVertexColorG; 100 | float _ReturnVertexColorB; 101 | float _ReturnNormals; 102 | float _ReturnTangents; 103 | 104 | /* end of properties */ 105 | 106 | 107 | ENDHLSL 108 | 109 | Pass{ 110 | Name "ForwardBase" 111 | 112 | Tags{ "LightMode" = "ForwardBase" } 113 | 114 | HLSLPROGRAM 115 | 116 | #pragma multi_compile_fwdbase 117 | 118 | #include "SekaiToon-base.hlsl" 119 | 120 | ENDHLSL 121 | } 122 | Pass{ 123 | Name "OutlinePass" 124 | 125 | Cull Front 126 | 127 | HLSLPROGRAM 128 | 129 | #pragma multi_compile_fwdbase 130 | 131 | #include "SekaiToon-outlines.hlsl" 132 | 133 | ENDHLSL 134 | } 135 | UsePass "Standard/SHADOWCASTER" 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Assets/Shaders/SekaiToon-base.hlsl: -------------------------------------------------------------------------------- 1 | #include "SekaiToon-inputs.hlsli" 2 | 3 | #include "SekaiToon-helpers.hlsl" 4 | 5 | vsOut vert(vsIn i){ 6 | vsOut o; 7 | o.pos = mul(UNITY_MATRIX_MVP, i.vertex); // transform to clip space 8 | o.normalOS = i.normal; 9 | o.tangent = i.tangent; 10 | o.vertexOS = i.vertex; 11 | o.uv = (_ToggleLongTex != 0) ? vector(i.uv.x * 0.5, i.uv.y) : i.uv; // vs_TEXCOORD1 12 | o.vertexcol.x = i.vertexcol.x; // outline direction, EdgeScale_view @ line 246 13 | o.vertexcol.y = i.vertexcol.y; // rim intensity, RimScale_view @ line 247 14 | o.vertexcol.z = i.vertexcol.z; // eyebrow mask, used for making them appear in front of the hair at all times 15 | o.vertexcol.w = i.vertexcol.w; // UNUSED 16 | 17 | const vector vertexWS = mul(UNITY_MATRIX_M, i.vertex); // transform to world space 18 | const vector viewDirOS = normalize(ObjSpaceViewDir(o.vertexOS)); 19 | 20 | vector zOffset = 1.0; 21 | zOffset.xyz = o.vertexcol.z * 0.015 * viewDirOS; 22 | zOffset.xyz += o.vertexOS; 23 | 24 | o.pos = mul(UNITY_MATRIX_MVP, zOffset); 25 | 26 | // compute vertex lights 27 | o.vertexLight = ComputeAdditionalLights(vertexWS, o.pos); 28 | o.vertexLight = min(o.vertexLight, 10); 29 | 30 | // compute light direction 31 | OpenLitLightDatas lightData; 32 | ComputeLights(lightData, vector(1.0, 1.0, 0.0, 0.0) * 0.001); 33 | o.lightData = lightData; 34 | 35 | UNITY_TRANSFER_SHADOW(o, o.pos); 36 | UNITY_TRANSFER_FOG(o, o.pos); 37 | 38 | return o; 39 | } 40 | 41 | vector frag(vsOut i) : SV_Target{ 42 | const vector normalWS = UnityObjectToWorldNormal(i.normalOS); 43 | const vector vertexWS = mul(UNITY_MATRIX_M, i.vertexOS); 44 | const vector viewDirWS = normalize(WorldSpaceViewDir(i.vertexOS)); // vs_TEXCOORD2? 45 | 46 | 47 | /* TEXTURE CREATION */ 48 | 49 | const vector mainTex = _MainTex.Sample(sampler_MainTex, i.uv); 50 | const vector valueTex = _ValueTex.Sample(sampler_ValueTex, i.uv); 51 | const vector shadowTex = _ShadowTex.Sample(sampler_ShadowTex, i.uv); 52 | 53 | /* END OF TEXTURE CREATION */ 54 | 55 | 56 | /* DOT CREATION */ 57 | 58 | // NdotL 59 | half NdotL = dot(i.lightData.lightDirection, normalWS); 60 | // we need NdotL untouched 61 | half buffer = NdotL; 62 | // convert from a range of { -1, 1 } to { 0, 1 } 63 | NdotL = NdotL * 0.5 + 0.5; 64 | // control shadow push 65 | NdotL += _ShadowPush; 66 | // use blue channel of lightmap texture 67 | NdotL = saturate(NdotL + (valueTex.z * 2.0 - 1.0)); 68 | // control shadow smoothness, 0.0925 is arbitrary - I wanted to visually match line 217 69 | NdotL = smoothstep(0.0 + (1.0 - _ShadowSmoothness) * 0.5, 1.0 - (1.0 - _ShadowSmoothness) * 0.5, NdotL + 0.0925); 70 | 71 | // NdotV, probably not needed 72 | half NdotV = dot(viewDirWS, normalWS); 73 | // convert from a range of { -1, 1 } to { 0, 1 } 74 | //NdotV = NdotV * 0.5 + 0.5; 75 | 76 | // NdotH, probably not needed 77 | // form the halfVector 78 | vector halfVector = normalize(viewDirWS + i.lightData.lightDirection); 79 | half NdotH = dot(halfVector, normalWS); 80 | 81 | // shadows 82 | UNITY_LIGHT_ATTENUATION(attenuation, i, vertexWS); 83 | NdotL *= lerp(1.0, attenuation, _ShadowStrength); 84 | 85 | /* END OF DOT CREATION */ 86 | 87 | 88 | /* RAMP CREATION */ 89 | 90 | // diffuse ramp, lines 218-221 91 | vector diffuseRamp = lerp(shadowTex, mainTex, NdotL); 92 | // apply _Shadow1SkinColor, lines 222-224 93 | vector skinColor = lerp(Shadow1SkinColor[_CharacterId - 1.0], DefaultSkinColor[_CharacterId - 1.0], saturate(diffuseRamp.x * 2.0 - 1.0)); 94 | // apply _Shadow1SkinColor, lines 225 95 | skinColor = lerp(Shadow2SkinColor[_CharacterId - 1.0], skinColor, saturate(diffuseRamp.x + diffuseRamp.x)); 96 | // apply skinColor 97 | diffuseRamp = lerp(diffuseRamp, skinColor, valueTex.x >= 0.5); 98 | 99 | // rim light ramp 100 | half rim = pow(saturate(buffer), _RimLength); 101 | rim *= (1 - saturate(NdotV)); 102 | rim = smoothstep(_RimThickness - 0.01, _RimThickness + 0.01, rim); 103 | 104 | /* END OF RAMP CREATION */ 105 | 106 | 107 | /* ENVIRONMENT LIGHTING */ 108 | 109 | vector envLighting = max(max(i.lightData.directLight, i.lightData.indirectLight), i.vertexLight); 110 | 111 | /* END OF ENVIRONMENT LIGHTING */ 112 | 113 | 114 | /* DEBUGGING */ 115 | 116 | if(_ReturnLightmapR != 0){ return vector(valueTex.xxx, 1.0); } 117 | if(_ReturnLightmapG != 0){ return vector(valueTex.yyy, 1.0); } 118 | if(_ReturnLightmapB != 0){ return vector(valueTex.zzz, 1.0); } 119 | if(_ReturnVertexColorR != 0){ return vector(i.vertexcol.xxx, 1.0); } 120 | if(_ReturnVertexColorG != 0){ return vector(i.vertexcol.yyy, 1.0); } 121 | if(_ReturnVertexColorB != 0){ return vector(i.vertexcol.zzz, 1.0); } 122 | if(_ReturnNormals != 0){ return vector(i.normalOS, 1.0); } 123 | if(_ReturnTangents != 0){ return i.tangent; } 124 | 125 | /* END OF DEBUGGING */ 126 | 127 | 128 | /* COLOR CREATION */ 129 | 130 | vector finalColor = vector(diffuseRamp, 1.0); 131 | 132 | // apply environment lighting 133 | finalColor.xyz = lerp(finalColor.xyz, (blendColorBurn(finalColor.xyz, envLighting, 0.6) * envLighting), _envLightingStrength); 134 | 135 | // apply rim light 136 | finalColor.xyz += rim * _RimColor * lerp(1.0, envLighting, 0.5) * _RimIntensity * i.vertexcol.y; 137 | 138 | // apply fog 139 | UNITY_APPLY_FOG(i.fogCoord, finalColor); 140 | 141 | /* END OF COLOR CREATION */ 142 | 143 | 144 | return finalColor; 145 | } 146 | -------------------------------------------------------------------------------- /Assets/Shaders/SekaiToon-helpers.hlsl: -------------------------------------------------------------------------------- 1 | #ifndef SEKAITOON_HELPERS 2 | #define SEKAITOON_HELPERS 3 | 4 | //------------------------------------------------------------------------------- 5 | 6 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 7 | // 1.Ichika 5.Minori 9.Kohane 13.Tsukasa 17.Kanade 21.Vocaloids 8 | // 2.Saki 6.Haruka 10.An 14.Emu 18.Mafuyu 9 | // 3.Honami 7.Airi 11.Akito 15.Nene 19.Ena 10 | // 4.Shiho 8.Shizuku 12.Touya 16.Rui 20.Mizuki 11 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 | 14 | // default skin colors, I have literally no idea as to why this needs to be static 15 | const static vector DefaultSkinColor[21] = { 16 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Ichika// 1 17 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Saki// 2 18 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Honami// 3 19 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Shiho// 4 20 | vector(0.996, 0.937, 0.878, 1.0), //Minori// 5 21 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Haruka// 6 22 | vector(0.996, 0.937, 0.878, 1.0), //Airi// 7 23 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Shizuku// 8 24 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Kohane// 9 25 | vector(0.996, 0.937, 0.878, 1.0), //An// 10 26 | vector(0.996, 0.937, 0.878, 1.0), //Akito// 11 27 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Touya// 12 28 | vector(0.996, 0.937, 0.878, 1.0), //Tsukasa// 13 29 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Emu// 14 30 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Nene// 15 31 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Rui// 16 32 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Kanade// 17 33 | vector(0.996, 0.937, 0.878, 1.0), //Mafuyu// 18 34 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Ena// 19 35 | vector(0.9921875, 0.9609375, 0.921875, 1.0), //Mizuki// 20 36 | vector(0.9921875, 0.9609375, 0.921875, 1.0) //Vocaloids// 21 37 | }; 38 | 39 | // shadow 1 skin colors, I have literally no idea as to why this needs to be static 40 | const static vector Shadow1SkinColor[21] = { 41 | vector(0.957, 0.714, 0.804, 1.0), //Ichika// 1 42 | vector(0.957, 0.714, 0.804, 1.0), //Saki// 2 43 | vector(0.957, 0.714, 0.804, 1.0), //Honami// 3 44 | vector(0.957, 0.714, 0.804, 1.0), //Shiho// 4 45 | vector(0.937, 0.686, 0.733, 1.0), //Minori// 5 46 | vector(0.957, 0.714, 0.804, 1.0), //Haruka// 6 47 | vector(0.937, 0.686, 0.733, 1.0), //Airi// 7 48 | vector(0.957, 0.714, 0.804, 1.0), //Shizuku// 8 49 | vector(0.957, 0.714, 0.804, 1.0), //Kohane// 9 50 | vector(0.937, 0.686, 0.733, 1.0), //An// 10 51 | vector(0.925, 0.663, 0.667, 1.0), //Akito// 11 52 | vector(0.957, 0.714, 0.804, 1.0), //Touya// 12 53 | vector(0.925, 0.663, 0.667, 1.0), //Tsukasa// 13 54 | vector(0.957, 0.714, 0.804, 1.0), //Emu// 14 55 | vector(0.957, 0.714, 0.804, 1.0), //Nene// 15 56 | vector(0.957, 0.714, 0.804, 1.0), //Rui// 16 57 | vector(0.957, 0.714, 0.804, 1.0), //Kanade// 17 58 | vector(0.937, 0.686, 0.733, 1.0), //Mafuyu// 18 59 | vector(0.957, 0.714, 0.804, 1.0), //Ena// 19 60 | vector(0.957, 0.714, 0.804, 1.0), //Mizuki// 20 61 | vector(0.890625, 0.7695313, 0.796875, 1.0) //Vocaloids// 21 62 | }; 63 | 64 | // shadow 2 skin colors, I have literally no idea as to why this needs to be static 65 | const static vector Shadow2SkinColor[21] = { 66 | vector(0.914, 0.51, 0.647, 1.0), //Ichika// 1 67 | vector(0.914, 0.51, 0.647, 1.0), //Saki// 2 68 | vector(0.914, 0.51, 0.647, 1.0), //Honami// 3 69 | vector(0.914, 0.51, 0.647, 1.0), //Shiho// 4 70 | vector(0.878, 0.471, 0.537, 1.0), //Minori// 5 71 | vector(0.914, 0.51, 0.647, 1.0), //Haruka// 6 72 | vector(0.878, 0.471, 0.537, 1.0), //Airi// 7 73 | vector(0.914, 0.51, 0.647, 1.0), //Shizuku// 8 74 | vector(0.914, 0.51, 0.647, 1.0), //Kohane// 9 75 | vector(0.878, 0.471, 0.537, 1.0), //An// 10 76 | vector(0.855, 0.439, 0.443, 1.0), //Akito// 11 77 | vector(0.914, 0.51, 0.647, 1.0), //Touya// 12 78 | vector(0.855, 0.439, 0.443, 1.0), //Tsukasa// 13 79 | vector(0.914, 0.51, 0.545, 1.0), //Emu// 14 80 | vector(0.914, 0.51, 0.647, 1.0), //Nene// 15 81 | vector(0.914, 0.51, 0.545, 1.0), //Rui// 16 82 | vector(0.914, 0.51, 0.647, 1.0), //Kanade// 17 83 | vector(0.878, 0.471, 0.537, 1.0), //Mafuyu// 18 84 | vector(0.914, 0.51, 0.647, 1.0), //Ena// 19 85 | vector(0.914, 0.51, 0.647, 1.0), //Mizuki// 20 86 | vector(0.796875, 0.59375, 0.6367188, 1.0) //Vocaloids// 21 87 | }; 88 | 89 | //------------------------------------------------------------------------------- 90 | 91 | /* helper functions */ 92 | 93 | float blendColorBurn(const float base, const float blend){ 94 | return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0); 95 | } 96 | 97 | vector blendColorBurn(const vector base, const vector blend){ 98 | return vector(blendColorBurn(base.r,blend.r),blendColorBurn(base.g,blend.g),blendColorBurn(base.b,blend.b)); 99 | } 100 | 101 | vector blendColorBurn(const vector base, const vector blend, float opacity){ 102 | return (blendColorBurn(base, blend) * opacity + base * (1.0 - opacity)); 103 | } 104 | 105 | float blendLinearBurn(const float base, const float blend){ 106 | // Note : Same implementation as BlendSubtractf 107 | return max(base+blend-1.0,0.0); 108 | } 109 | 110 | vector blendLinearBurn(const vector base, const vector blend){ 111 | // Note : Same implementation as BlendSubtract 112 | return max(base+blend-1.0,0.0); 113 | } 114 | 115 | vector blendLinearBurn(const vector base, const vector blend, const float opacity){ 116 | return (blendLinearBurn(base, blend) * opacity + base * (1.0 - opacity)); 117 | } 118 | 119 | // map range functions 120 | float mapRange(const float min_in, const float max_in, const float min_out, const float max_out, const float value){ 121 | float slope = (max_out - min_out) / (max_in - min_in); 122 | 123 | return min_out + slope * (value - min_in); 124 | } 125 | 126 | float lerpByZ(const float startScale, const float endScale, const float startZ, const float endZ, const float z){ 127 | float t = (z - startZ) / max(endZ - startZ, 0.001); 128 | t = saturate(t); 129 | return lerp(startScale, endScale, t); 130 | } 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /Assets/Shaders/OpenLit-core.hlsl: -------------------------------------------------------------------------------- 1 | // OpenLit Library 1.0.2 2 | // This code is licensed under CC0 1.0 Universal. 3 | // https://creativecommons.org/publicdomain/zero/1.0/ 4 | 5 | #if !defined(OPENLIT_CORE_INCLUDED) 6 | #define OPENLIT_CORE_INCLUDED 7 | 8 | //------------------------------------------------------------------------------------------------------------------------------ 9 | // Macro 10 | #define OPENLIT_LIGHT_COLOR _LightColor0.rgb 11 | #define OPENLIT_LIGHT_DIRECTION _WorldSpaceLightPos0.xyz 12 | #define OPENLIT_MATRIX_M unity_ObjectToWorld 13 | #define OPENLIT_FALLBACK_DIRECTION float4(0.001,0.002,0.001,0) 14 | 15 | //------------------------------------------------------------------------------------------------------------------------------ 16 | // SRGB <-> Linear 17 | float3 OpenLitLinearToSRGB(float3 col) 18 | { 19 | return LinearToGammaSpace(col); 20 | } 21 | 22 | float3 OpenLitSRGBToLinear(float3 col) 23 | { 24 | return GammaToLinearSpace(col); 25 | } 26 | 27 | //------------------------------------------------------------------------------------------------------------------------------ 28 | // Color 29 | float OpenLitLuminance(float3 rgb) 30 | { 31 | #if defined(UNITY_COLORSPACE_GAMMA) 32 | return dot(rgb, float3(0.22, 0.707, 0.071)); 33 | #else 34 | return dot(rgb, float3(0.0396819152, 0.458021790, 0.00609653955)); 35 | #endif 36 | } 37 | 38 | float OpenLitGray(float3 rgb) 39 | { 40 | return dot(rgb, float3(1.0/3.0, 1.0/3.0, 1.0/3.0)); 41 | } 42 | 43 | //------------------------------------------------------------------------------------------------------------------------------ 44 | // Structure 45 | struct OpenLitLightDatas 46 | { 47 | float3 lightDirection; 48 | float3 directLight; 49 | float3 indirectLight; 50 | }; 51 | 52 | //------------------------------------------------------------------------------------------------------------------------------ 53 | // Light Direction 54 | // Use `UnityWorldSpaceLightDir(float3 positionWS)` for ForwardAdd passes 55 | float3 ComputeCustomLightDirection(float4 lightDirectionOverride) 56 | { 57 | float3 customDir = length(lightDirectionOverride.xyz) * normalize(mul((float3x3)OPENLIT_MATRIX_M, lightDirectionOverride.xyz)); 58 | return lightDirectionOverride.w ? customDir : lightDirectionOverride.xyz; 59 | } 60 | 61 | void ComputeLightDirection(out float3 lightDirection, out float3 lightDirectionForSH9, float4 lightDirectionOverride) 62 | { 63 | float3 mainDir = OPENLIT_LIGHT_DIRECTION * OpenLitLuminance(OPENLIT_LIGHT_COLOR); 64 | #if !defined(LIGHTMAP_ON) && UNITY_SHOULD_SAMPLE_SH 65 | float3 sh9Dir = unity_SHAr.xyz * 0.333333 + unity_SHAg.xyz * 0.333333 + unity_SHAb.xyz * 0.333333; 66 | float3 sh9DirAbs = float3(sh9Dir.x, abs(sh9Dir.y), sh9Dir.z); 67 | #else 68 | float3 sh9Dir = 0; 69 | float3 sh9DirAbs = 0; 70 | #endif 71 | float3 customDir = ComputeCustomLightDirection(lightDirectionOverride); 72 | 73 | lightDirection = normalize(sh9DirAbs + mainDir + customDir); 74 | lightDirectionForSH9 = sh9Dir + mainDir; 75 | lightDirectionForSH9 = dot(lightDirectionForSH9,lightDirectionForSH9) < 0.000001 ? 0 : normalize(lightDirectionForSH9); 76 | } 77 | 78 | void ComputeLightDirection(out float3 lightDirection, out float3 lightDirectionForSH9) 79 | { 80 | ComputeLightDirection(lightDirection, lightDirectionForSH9, OPENLIT_FALLBACK_DIRECTION); 81 | } 82 | 83 | //------------------------------------------------------------------------------------------------------------------------------ 84 | // ShadeSH9 85 | void ShadeSH9ToonDouble(float3 lightDirection, out float3 shMax, out float3 shMin) 86 | { 87 | #if !defined(LIGHTMAP_ON) && UNITY_SHOULD_SAMPLE_SH 88 | float3 N = lightDirection * 0.666666; 89 | float4 vB = N.xyzz * N.yzzx; 90 | // L0 L2 91 | float3 res = float3(unity_SHAr.w,unity_SHAg.w,unity_SHAb.w); 92 | res.r += dot(unity_SHBr, vB); 93 | res.g += dot(unity_SHBg, vB); 94 | res.b += dot(unity_SHBb, vB); 95 | res += unity_SHC.rgb * (N.x * N.x - N.y * N.y); 96 | // L1 97 | float3 l1; 98 | l1.r = dot(unity_SHAr.rgb, N); 99 | l1.g = dot(unity_SHAg.rgb, N); 100 | l1.b = dot(unity_SHAb.rgb, N); 101 | shMax = res + l1; 102 | shMin = res - l1; 103 | #if defined(UNITY_COLORSPACE_GAMMA) 104 | shMax = OpenLitLinearToSRGB(shMax); 105 | shMin = OpenLitLinearToSRGB(shMin); 106 | #endif 107 | #else 108 | shMax = 0.0; 109 | shMin = 0.0; 110 | #endif 111 | } 112 | 113 | void ShadeSH9ToonDouble(out float3 shMax, out float3 shMin) 114 | { 115 | float3 lightDirection, lightDirectionForSH9; 116 | ComputeLightDirection(lightDirection, lightDirectionForSH9, OPENLIT_FALLBACK_DIRECTION); 117 | ShadeSH9ToonDouble(lightDirectionForSH9, shMax, shMin); 118 | } 119 | 120 | float3 ShadeSH9Toon() 121 | { 122 | float3 shMax, shMin; 123 | ShadeSH9ToonDouble(shMax, shMin); 124 | return shMax; 125 | } 126 | 127 | float3 ShadeSH9ToonIndirect() 128 | { 129 | float3 shMax, shMin; 130 | ShadeSH9ToonDouble(shMax, shMin); 131 | return shMin; 132 | } 133 | 134 | //------------------------------------------------------------------------------------------------------------------------------ 135 | // Lighting 136 | void ComputeSHLightsAndDirection(out float3 lightDirection, out float3 directLight, out float3 indirectLight, float4 lightDirectionOverride) 137 | { 138 | float3 lightDirectionForSH9; 139 | ComputeLightDirection(lightDirection, lightDirectionForSH9, lightDirectionOverride); 140 | ShadeSH9ToonDouble(lightDirectionForSH9, directLight, indirectLight); 141 | } 142 | 143 | void ComputeSHLightsAndDirection(out float3 lightDirection, out float3 directLight, out float3 indirectLight) 144 | { 145 | ComputeSHLightsAndDirection(lightDirection, directLight, indirectLight, OPENLIT_FALLBACK_DIRECTION); 146 | } 147 | 148 | void ComputeLights(out float3 lightDirection, out float3 directLight, out float3 indirectLight, float4 lightDirectionOverride) 149 | { 150 | ComputeSHLightsAndDirection(lightDirection, directLight, indirectLight, lightDirectionOverride); 151 | directLight += OPENLIT_LIGHT_COLOR; 152 | } 153 | 154 | void ComputeLights(out float3 lightDirection, out float3 directLight, out float3 indirectLight) 155 | { 156 | ComputeSHLightsAndDirection(lightDirection, directLight, indirectLight); 157 | directLight += OPENLIT_LIGHT_COLOR; 158 | } 159 | 160 | void ComputeLights(out OpenLitLightDatas lightDatas, float4 lightDirectionOverride) 161 | { 162 | ComputeLights(lightDatas.lightDirection, lightDatas.directLight, lightDatas.indirectLight, lightDirectionOverride); 163 | } 164 | 165 | void ComputeLights(out OpenLitLightDatas lightDatas) 166 | { 167 | ComputeLights(lightDatas.lightDirection, lightDatas.directLight, lightDatas.indirectLight); 168 | } 169 | 170 | //------------------------------------------------------------------------------------------------------------------------------ 171 | // Correct 172 | void CorrectLights(inout OpenLitLightDatas lightDatas, float lightMinLimit, float lightMaxLimit, float monochromeLighting, float asUnlit) 173 | { 174 | lightDatas.directLight = clamp(lightDatas.directLight, lightMinLimit, lightMaxLimit); 175 | lightDatas.directLight = lerp(lightDatas.directLight, OpenLitGray(lightDatas.directLight), monochromeLighting); 176 | lightDatas.directLight = lerp(lightDatas.directLight, 1.0, asUnlit); 177 | lightDatas.indirectLight = clamp(lightDatas.indirectLight, 0.0, lightMaxLimit); 178 | } 179 | 180 | //------------------------------------------------------------------------------------------------------------------------------ 181 | // Vertex Lighting 182 | float3 ComputeAdditionalLights(float3 positionWS, float3 positionCS) 183 | { 184 | float4 toLightX = unity_4LightPosX0 - positionWS.x; 185 | float4 toLightY = unity_4LightPosY0 - positionWS.y; 186 | float4 toLightZ = unity_4LightPosZ0 - positionWS.z; 187 | 188 | float4 lengthSq = toLightX * toLightX + 0.000001; 189 | lengthSq += toLightY * toLightY; 190 | lengthSq += toLightZ * toLightZ; 191 | 192 | //float4 atten = 1.0 / (1.0 + lengthSq * unity_4LightAtten0); 193 | float4 atten = saturate(saturate((25.0 - lengthSq * unity_4LightAtten0) * 0.111375) / (0.987725 + lengthSq * unity_4LightAtten0)); 194 | 195 | float3 additionalLightColor; 196 | additionalLightColor = unity_LightColor[0].rgb * atten.x; 197 | additionalLightColor = additionalLightColor + unity_LightColor[1].rgb * atten.y; 198 | additionalLightColor = additionalLightColor + unity_LightColor[2].rgb * atten.z; 199 | additionalLightColor = additionalLightColor + unity_LightColor[3].rgb * atten.w; 200 | 201 | return additionalLightColor; 202 | } 203 | 204 | //------------------------------------------------------------------------------------------------------------------------------ 205 | // Encode and decode 206 | #if !defined(SHADER_API_GLES) 207 | // -1 - 1 208 | uint EncodeNormalizedFloat3ToUint(float3 vec) 209 | { 210 | uint valx = abs(vec.x) >= 1 ? 511 : abs(vec.x) * 511; 211 | uint valy = abs(vec.y) >= 1 ? 511 : abs(vec.y) * 511; 212 | uint valz = abs(vec.z) >= 1 ? 511 : abs(vec.z) * 511; 213 | valx = valx & 0x000001ffu; 214 | valy = valy & 0x000001ffu; 215 | valz = valz & 0x000001ffu; 216 | valx += vec.x > 0 ? 0 : 512; 217 | valy += vec.y > 0 ? 0 : 512; 218 | valz += vec.z > 0 ? 0 : 512; 219 | 220 | valy = valy << 10; 221 | valz = valz << 20; 222 | return valx | valy | valz; 223 | } 224 | 225 | float3 DecodeNormalizedFloat3FromUint(uint val) 226 | { 227 | // 5 math in target 5.0 228 | uint3 val3 = val >> uint3(0,10,20); 229 | float3 vec = val3 & 0x000001ffu; 230 | vec /= (val3 & 0x00000200u) == 0x00000200u ? -511.0 : 511.0; 231 | return vec; 232 | } 233 | 234 | // 0 - 999 235 | uint EncodeHDRColorToUint(float3 col) 236 | { 237 | col = clamp(col, 0, 999); 238 | float maxcol = max(col.r,max(col.g,col.b)); 239 | 240 | float floatDigit = maxcol == 0 ? 0 : log10(maxcol); 241 | uint digit = floatDigit >= 0 ? floatDigit + 1 : 0; 242 | if(digit > 3) digit = 3; 243 | float scale = pow(10,digit); 244 | col /= scale; 245 | 246 | uint R = col.r * 1023; 247 | uint G = col.g * 1023; 248 | uint B = col.b * 1023; 249 | uint M = digit; 250 | R = R & 0x000003ffu; 251 | G = G & 0x000003ffu; 252 | B = B & 0x000003ffu; 253 | 254 | G = G << 10; 255 | B = B << 20; 256 | M = M << 30; 257 | return R | G | B | M; 258 | } 259 | 260 | float3 DecodeHDRColorFromUint(uint val) 261 | { 262 | // 5 math in target 5.0 263 | uint4 RGBM = val >> uint4(0,10,20,30); 264 | return float3(RGBM.rgb & 0x000003ffu) / 1023.0 * pow(10,RGBM.a); 265 | } 266 | 267 | void PackLightDatas(out uint3 pack, OpenLitLightDatas lightDatas) 268 | { 269 | pack = uint3( 270 | EncodeNormalizedFloat3ToUint(lightDatas.lightDirection), 271 | EncodeHDRColorToUint(lightDatas.directLight), 272 | EncodeHDRColorToUint(lightDatas.indirectLight) 273 | ); 274 | } 275 | 276 | void UnpackLightDatas(out OpenLitLightDatas lightDatas, uint3 pack) 277 | { 278 | lightDatas.lightDirection = DecodeNormalizedFloat3FromUint(pack.x); 279 | lightDatas.directLight = DecodeHDRColorFromUint(pack.y); 280 | lightDatas.indirectLight = DecodeHDRColorFromUint(pack.z); 281 | } 282 | #endif // #if !defined(SHADER_API_GLES) 283 | 284 | #endif // #if !defined(OPENLIT_CORE_INCLUDED) --------------------------------------------------------------------------------