- NiloToonURP 외부 에셋 업데이트 - 배경 씬 썸네일 16:9 해상도로 갱신 - 렌더 파이프라인 설정 업데이트 - 외부 셰이더 그래프 업데이트 (LEDScreen, PIDI Planar Reflections) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4739 lines
236 KiB
HLSL
4739 lines
236 KiB
HLSL
// SPDX-License-Identifier: (Not available for this version, you are only allowed to use this software if you have express permission from the copyright holder and agreed to the latest NiloToonURP EULA)
|
||
// Copyright (c) 2021 Kuroneko ShaderLab Limited
|
||
|
||
// For more information, visit -> https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
|
||
|
||
// #pragma once is a safeguard best practice in almost every .hlsl,
|
||
// doing this can make sure your .hlsl's user can include this .hlsl anywhere anytime without producing any multi include conflict
|
||
#pragma once
|
||
|
||
#include "NiloToonCharacter_RiderSupport.hlsl"
|
||
|
||
// note:
|
||
// suffix OS means object space (e.g. positionOS = position object space)
|
||
// suffix WS means world space (e.g. positionWS = position world space)
|
||
// suffix VS means view space (e.g. positionVS = position view space)
|
||
// suffix CS means clip space (e.g. positionCS = position clip space)
|
||
|
||
// just helper defines to help us write less #if define code
|
||
// we always write #define XXX 1, instead of just #define XXX, so we can use both #if or #ifdef in shader code without problem
|
||
#if NiloToonSelfOutlinePass || NiloToonExtraThickOutlinePass || NiloToonCharacterAreaStencilBufferFillPass || NiloToonDepthOnlyOrDepthNormalPass || NiloToonPrepassBufferPass
|
||
#define NiloToonIsAnyOutlinePass 1
|
||
#endif
|
||
#if NiloToonForwardLitPass || NiloToonSelfOutlinePass
|
||
#define NiloToonIsAnyLitColorPass 1
|
||
#endif
|
||
#if NiloToonIsAnyLitColorPass || NiloToonDepthOnlyOrDepthNormalPass || NiloToonExtraThickOutlinePass || NiloToonPrepassBufferPass
|
||
#define ApplyZOffset 1 // foreach pass that will edit "ZOffsetFinalSum", will be included in "ApplyZOffset"
|
||
#endif
|
||
#if NiloToonIsAnyLitColorPass || NiloToonDepthOnlyOrDepthNormalPass || NiloToonExtraThickOutlinePass || NiloToonPrepassBufferPass || NiloToonCharacterAreaStencilBufferFillPass || NiloToonCharacterAreaColorFillPass
|
||
#define ApplyPerspectiveRemoval 1
|
||
#endif
|
||
|
||
// because _DETAIL always sample detail normal map
|
||
#if _NORMALMAP || _DETAIL || _KAJIYAKAY_SPECULAR || _DYNAMIC_EYE || _PARALLAXMAP
|
||
#define VaryingsHasTangentWS 1
|
||
#endif
|
||
#if _PARALLAXMAP
|
||
#define VaryingsHasViewDirTS 1
|
||
#endif
|
||
|
||
#if _ISFACE && _FACE_MASK_ON
|
||
#define NeedFaceMaskArea 1
|
||
#endif
|
||
|
||
#if _OCCLUSIONMAP || _MATCAP_OCCLUSION
|
||
#define AnyOcclusionEnabled 1
|
||
#endif
|
||
|
||
#if _BASEMAP_STACKING_LAYER1 || _BASEMAP_STACKING_LAYER2 || _BASEMAP_STACKING_LAYER3 || _BASEMAP_STACKING_LAYER4 || _BASEMAP_STACKING_LAYER5 || _BASEMAP_STACKING_LAYER6 || _BASEMAP_STACKING_LAYER7 || _BASEMAP_STACKING_LAYER8 || _BASEMAP_STACKING_LAYER9 || _BASEMAP_STACKING_LAYER10
|
||
#define AnyBaseMapStackingLayerEnabled 1
|
||
#endif
|
||
|
||
#if _NILOTOON_RECEIVE_URP_SHADOWMAPPING && _RECEIVE_URP_SHADOW
|
||
#define ShouldReceiveURPShadow 1
|
||
#endif
|
||
|
||
// to support change in Unity6.1 (The _FORWARD_PLUS shader keyword is deprecated in Unity6.1)
|
||
#if UNITY_VERSION >= 60010000
|
||
#define NILO_USE_CLUSTER_LIGHT_LOOP USE_CLUSTER_LIGHT_LOOP
|
||
#else
|
||
#define NILO_USE_CLUSTER_LIGHT_LOOP USE_FORWARD_PLUS
|
||
#endif
|
||
|
||
// We don't have "UnityCG.cginc" in SRP/URP's package anymore, so:
|
||
// Including the following two hlsl files is enough for shading with Universal Pipeline. Everything is included in them.
|
||
// - Core.hlsl will include SRP shader library, all constant buffers not related to materials (perobject, percamera, perframe).
|
||
// It also includes matrix/space conversion functions and fog.
|
||
// - Lighting.hlsl will include the light functions/data to abstract light constants. You should use GetMainLight and GetLight functions
|
||
// that initialize Light struct. Lighting.hlsl also include GI, Light BDRF functions. It also includes Shadows.
|
||
|
||
// Required by all Universal Render Pipeline shaders.
|
||
// It will include Unity built-in shader variables (except the lighting variables)
|
||
// (https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html)
|
||
// It will also include many utilitary functions.
|
||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||
|
||
// Include this if you are doing a lit shader. This includes lighting shader variables,
|
||
// lighting and shadow functions
|
||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
|
||
|
||
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ParallaxMapping.hlsl"
|
||
|
||
// include a bundle of small utility .hlsl files to help us write less code
|
||
#include "../../ShaderLibrary/NiloUtilityHLSL/NiloAllUtilIncludes.hlsl"
|
||
|
||
// we need to define this AFTER including Lighting.hlsl
|
||
#if _ADDITIONAL_LIGHTS || _ADDITIONAL_LIGHTS_VERTEX
|
||
#define NeedCalculateAdditionalLight 1
|
||
#endif
|
||
|
||
// all pass will share this Attributes struct (define data needed from Unity app to our vertex shader)
|
||
// TODO: optimize struct size for each pass. There maybe some pass that don't need some attributes
|
||
struct Attributes
|
||
{
|
||
float3 positionOS : POSITION; // vertex position in object space
|
||
float3 normalOS : NORMAL; // GetVertexNormalInputs(...) expect float3 normalOS input, don't write half3 to produce unneeded type conversion cost
|
||
float4 tangentOS : TANGENT; // GetVertexNormalInputs(...) expect float4 tangentOS input, don't write half4 to produce unneeded type conversion cost
|
||
float2 uv : TEXCOORD0; // the first uv (UV#0)
|
||
float2 uv2 : TEXCOORD1;
|
||
float2 uv3 : TEXCOORD2;
|
||
float2 uv4 : TEXCOORD3;
|
||
float3 uv8 : TEXCOORD7; // generated by NiloToon's editor script, store angle smoothed tangent space normal in mesh uv8((TEXCOORD7)), for improving outline and shadowmap normal bias
|
||
float4 color : COLOR; // vertex color, used for outline width mask, not as a color, so use float not half
|
||
uint vertexID : SV_VertexID; // SV_VertexID needs to be uint (https://docs.unity3d.com/Manual/SL-ShaderSemantics.html)
|
||
|
||
// to support GPU instancing and Single Pass Stereo rendering(VR), add the following section
|
||
//------------------------------------------------------------------------------------------------------------------------------
|
||
UNITY_VERTEX_INPUT_INSTANCE_ID // For non PSSL, equals to -> uint instanceID : SV_InstanceID;
|
||
//------------------------------------------------------------------------------------------------------------------------------
|
||
};
|
||
|
||
// all pass will share this Varyings struct (define data needed from vertex shader to fragment shader)
|
||
// Note: once a field is written here, no matter fragment shader use it or not
|
||
// you will pay for the rasterization interpolation cost,
|
||
// compiler can't help you in this case, you can check Unity's compiled shader code to confirm.
|
||
// so using #if to remove a field in this "v2f" Varyings struct is a meaningful optimization here
|
||
// Also, try to pack data into a single vec4 TEXCOORD
|
||
// "Pack values together as all varyings have four components, whether they are used or not. Putting two vec2 texture coordinates into a single vec4 value is a common practice"
|
||
// see https://developer.qualcomm.com/sites/default/files/docs/adreno-gpu/snapdragon-game-toolkit/gdg/gpu/best_practices_shaders.html#pack-shader-interpolators
|
||
struct Varyings
|
||
{
|
||
float4 positionCS : SV_POSITION;
|
||
|
||
float4 uv01 : TEXCOORD0; // need float for uv (half is not enough), if texture size is larger than 2048, half is not enough (https://forum.unity.com/threads/why-does-unity-recommend-us-to-use-float-type-for-texture-coordinates-in-the-shader.732920/#post-4894619)
|
||
float4 uv23 : TEXCOORD1;
|
||
|
||
float4 positionWS_ZOffsetFinalSum : TEXCOORD2; // xyz = positionWS, w = ZOffsetFinalSum
|
||
|
||
half4 SH_fogFactor : TEXCOORD3; // (4 half pack into 1 TEXCOORD) xyz: SampleSH(normalWS) * multipliers, w: fog factor
|
||
float4 normalWS_averageShadowAttenuation : TEXCOORD4; // (4 float pack into 1 TEXCOORD) xyz: normalWS, w: averageShadowAttenuation. URP14 LitForwardPass.hlsl define normalWS as float3 instead of half3, so we follow it.
|
||
|
||
float3 smoothedNormalWS : TEXCOORD5;
|
||
|
||
#if VaryingsHasViewDirTS
|
||
half3 viewDirTS : TEXCOORD6; // URP14 LitForwardPass.hlsl's viewDirTS is half3, we follow it.
|
||
#endif
|
||
|
||
#if VaryingsHasTangentWS
|
||
half4 tangentWS : TANGENT; // xyz: tangent, w: sign. (TODO: is TANGENT safe to use? In URP's LitForwardPass.hlsl, it is TEXCOORD, not TANGENT.)
|
||
#endif
|
||
|
||
half4 color : COLOR; // vertex color rgba, currently only for "control depth texture rimlight and shadow width by vertex color" or "debug shading"
|
||
|
||
// Note: unlike URP's LitForwardPass.hlsl, NiloToon didn't have
|
||
// float4 shadowCoord
|
||
// shadowCoord in NiloToon is always calculated in fragment shader
|
||
|
||
// debug use
|
||
//--------------------------------------------------------------------
|
||
#if _NILOTOON_DEBUG_SHADING
|
||
float3 uv8 : TEXCOORD7;
|
||
#endif
|
||
//--------------------------------------------------------------------
|
||
|
||
// to support GPU instancing and Single Pass Stereo rendering(VR), add the following section
|
||
//------------------------------------------------------------------------------------------------------------------------------
|
||
// see ShadowCasterPass.hlsl for why these lines are removed for NiloToonCharSelfShadowCasterPass
|
||
#if !NiloToonCharSelfShadowCasterPass
|
||
UNITY_VERTEX_INPUT_INSTANCE_ID // For non PSSL, equals to -> uint instanceID : SV_InstanceID;
|
||
UNITY_VERTEX_OUTPUT_STEREO // For non OpenGL and non PSSL, equals to -> uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex; (when UNITY_STEREO_INSTANCING_ENABLED)
|
||
#endif
|
||
//------------------------------------------------------------------------------------------------------------------------------
|
||
};
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// local(per material) samplers and textures
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// note(1):
|
||
// all sampler2D, SAMPLER and TEXTURE2D do not need to put inside UnityPerMaterial CBUFFER{}
|
||
|
||
// note(2):
|
||
// > how to avoid [sampler max count = 16] limit(error message: Shader error in 'X': maximum ps_5_0 sampler register index (16) exceeded)?
|
||
// Everytime you write a sampler2D/SAMPLER, the sampler count for that shader will +1, when the sampler count reach the hardware limit,
|
||
// the shader will not be able to compile.
|
||
// Usually the sampler count limit is 16 for PC, 8(can be more for some devices?) for mobile.
|
||
// The solution to avoid sampler max count limit = reuse SAMPLER in shader, don't declare new SAMPLER/sampler2D for each texture if SAMPLER setting can be reused.
|
||
// But a disadvantage of reusing sampler is, reusing sampler will prevent user to change sampler setting from each texture's texture inspector
|
||
// Useful resources:
|
||
// - https://docs.unity3d.com/Manual/SL-SamplerStates.html
|
||
// - URP's TerrainLitPasses.hlsl -> SplatmapMix(...), you will see 4 different splat textures were sampled by 1 sampler(sampler reused)
|
||
// - https://forum.unity.com/threads/texture-samplers-limit-per-pass.605395/
|
||
// - https://forum.unity.com/threads/sampler-count-limit-for-pixel-shader-in-shader-model-5-0.452957/
|
||
|
||
// ref: https://docs.unity3d.com/Manual/SL-SamplerStates.html
|
||
// "Just like separate texture + sampler syntax, inline sampler states are not supported on some platforms.
|
||
// Currently they are implemented on Direct3D 11/12, PS4, XboxOne and Metal."
|
||
// *Note:
|
||
// We tested the above statement on some android mobile devices (GLES / Vulkan),
|
||
// we found that inline sampler is OK to use even GLES/Vulkan are not listed in supported platforms,
|
||
// so we will use inline sampler to help reducing sampler count.
|
||
|
||
// [sampler_linear_clamp, this sampler will be shared by some texture read if sampler state is linear & clamp]
|
||
// (see: https://docs.unity3d.com/Manual/SL-SamplerStates.html)
|
||
// use cases:
|
||
// - force linear clamp sampler to prevent ramp lighting / ramp specular sampling out of bound pixels of ramp texture using repeat wrap mode
|
||
SAMPLER(sampler_linear_clamp);
|
||
|
||
// [sampler_linear_repeat, this sampler will be shared by some texture read if sampler state is linear & repeat]
|
||
// (see: https://docs.unity3d.com/Manual/SL-SamplerStates.html)
|
||
// use cases:
|
||
// - force half of the base stacking layer sharing sampler_linear_repeat to prevent [sampler max count = 16] limit
|
||
SAMPLER(sampler_linear_repeat);
|
||
|
||
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
|
||
|
||
#if _ALPHAOVERRIDEMAP
|
||
sampler2D _AlphaOverrideTex;
|
||
#endif
|
||
|
||
#if _PARALLAXMAP
|
||
TEXTURE2D(_ParallaxMap); SAMPLER(sampler_ParallaxMap);
|
||
#endif
|
||
|
||
#if _BASEMAP_STACKING_LAYER1
|
||
// only the first 2 layers use it's own sampler, to allow more control for user
|
||
TEXTURE2D(_BaseMapStackingLayer1Tex); SAMPLER(sampler_BaseMapStackingLayer1Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer1MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER2
|
||
// only the first 2 layers use it's own sampler, to allow more control for user
|
||
TEXTURE2D(_BaseMapStackingLayer2Tex); SAMPLER(sampler_BaseMapStackingLayer2Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer2MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER3
|
||
TEXTURE2D(_BaseMapStackingLayer3Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer3MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER4
|
||
TEXTURE2D(_BaseMapStackingLayer4Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer4MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER5
|
||
TEXTURE2D(_BaseMapStackingLayer5Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer5MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER6
|
||
TEXTURE2D(_BaseMapStackingLayer6Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer6MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER7
|
||
TEXTURE2D(_BaseMapStackingLayer7Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer7MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER8
|
||
TEXTURE2D(_BaseMapStackingLayer8Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer8MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER9
|
||
TEXTURE2D(_BaseMapStackingLayer9Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer9MaskTex);
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER10
|
||
TEXTURE2D(_BaseMapStackingLayer10Tex);
|
||
TEXTURE2D(_BaseMapStackingLayer10MaskTex);
|
||
#endif
|
||
|
||
#if _NORMALMAP
|
||
sampler2D _BumpMap;
|
||
#endif
|
||
#if _EMISSION
|
||
sampler2D _EmissionMap;
|
||
TEXTURE2D(_EmissionMaskMap);
|
||
#if _EMISSION_ANIM_TINT_RAMPMAP
|
||
TEXTURE2D(_EmissionAnimTintRampMap);
|
||
#endif
|
||
#endif
|
||
#if _ENVIRONMENTREFLECTIONS
|
||
TEXTURE2D(_EnvironmentReflectionMaskMap);
|
||
#endif
|
||
#if _MATCAP_BLEND
|
||
sampler2D _MatCapAlphaBlendMap;
|
||
TEXTURE2D(_MatCapAlphaBlendMaskMap);
|
||
#endif
|
||
#if _MATCAP_ADD
|
||
sampler2D _MatCapAdditiveMap;
|
||
TEXTURE2D(_MatCapAdditiveMaskMap);
|
||
#endif
|
||
#if _MATCAP_OCCLUSION
|
||
sampler2D _MatCapOcclusionMap;
|
||
TEXTURE2D(_MatCapOcclusionMaskMap);
|
||
#endif
|
||
#if _RAMP_LIGHTING
|
||
TEXTURE2D(_DynamicRampLightingTex);
|
||
TEXTURE2D(_RampLightingTex);
|
||
#endif
|
||
#if _RAMP_LIGHTING_SAMPLE_UVY_TEX
|
||
sampler2D _RampLightingSampleUvYTex;
|
||
#endif
|
||
#if _RAMP_SPECULAR
|
||
TEXTURE2D(_RampSpecularTex);
|
||
#endif
|
||
#if _RAMP_SPECULAR_SAMPLE_UVY_TEX
|
||
sampler2D _RampSpecularSampleUvYTex;
|
||
#endif
|
||
#if _OCCLUSIONMAP
|
||
sampler2D _OcclusionMap;
|
||
#endif
|
||
#if _SHADING_GRADEMAP
|
||
sampler2D _ShadingGradeMap;
|
||
#endif
|
||
#if _SMOOTHNESSMAP
|
||
sampler2D _SmoothnessMap;
|
||
#endif
|
||
#if _SPECULARHIGHLIGHTS
|
||
sampler2D _SpecularMap;
|
||
#if _SPECULARHIGHLIGHTS_TEX_TINT
|
||
sampler2D _SpecularColorTintMap;
|
||
#endif
|
||
#endif
|
||
#if _NILOGLITTER
|
||
sampler2D _NiloGlitterTintColorTex;
|
||
#endif
|
||
#if _KAJIYAKAY_SPECULAR
|
||
#if _KAJIYAKAY_SPECULAR_TEX_TINT
|
||
sampler2D _HairStrandSpecularTintMap;
|
||
#endif
|
||
#endif
|
||
#if _ZOFFSETMAP
|
||
sampler2D _ZOffsetMaskTex;
|
||
#endif
|
||
#if _OUTLINEWIDTHMAP
|
||
sampler2D _OutlineWidthTex;
|
||
#endif
|
||
#if _OUTLINETINTCOLORMAP
|
||
sampler2D _OutlineTintColorMap;
|
||
#endif
|
||
#if _OUTLINEZOFFSETMAP
|
||
sampler2D _OutlineZOffsetMaskTex;
|
||
#endif
|
||
#if NeedFaceMaskArea
|
||
sampler2D _FaceMaskMap;
|
||
#endif
|
||
#if _SKIN_MASK_ON
|
||
TEXTURE2D(_SkinMaskMap);
|
||
#endif
|
||
#if _DYNAMIC_EYE
|
||
sampler2D _DynamicEyePupilMap;
|
||
sampler2D _DynamicEyePupilMaskTex;
|
||
sampler2D _DynamicEyeWhiteMap;
|
||
#endif
|
||
#if _DETAIL
|
||
TEXTURE2D(_DetailMask); SAMPLER(sampler_DetailMask);
|
||
TEXTURE2D(_DetailAlbedoMap); SAMPLER(sampler_DetailAlbedoMap);
|
||
TEXTURE2D(_DetailNormalMap); SAMPLER(sampler_DetailNormalMap);
|
||
#endif
|
||
#if _OVERRIDE_SHADOWCOLOR_BY_TEXTURE
|
||
sampler2D _OverrideShadowColorTex;
|
||
sampler2D _OverrideShadowColorMaskMap;
|
||
#endif
|
||
#if _OVERRIDE_OUTLINECOLOR_BY_TEXTURE
|
||
sampler2D _OverrideOutlineColorTex;
|
||
#endif
|
||
#if _SCREENSPACE_OUTLINE
|
||
sampler2D _ScreenSpaceOutlineDepthSensitivityTex;
|
||
sampler2D _ScreenSpaceOutlineNormalsSensitivityTex;
|
||
#endif
|
||
#if _DEPTHTEX_RIMLIGHT_SHADOW_WIDTHMAP
|
||
sampler2D _DepthTexRimLightAndShadowWidthTex;
|
||
#endif
|
||
#if _DEPTHTEX_RIMLIGHT_OPACITY_MASKMAP
|
||
sampler2D _DepthTexRimLightMaskTex;
|
||
#endif
|
||
#if _NILOTOON_SELFSHADOW_INTENSITY_MAP
|
||
sampler2D _NiloToonSelfShadowIntensityMultiplierTex;
|
||
#endif
|
||
#if _FACE_SHADOW_GRADIENTMAP
|
||
sampler2D _FaceShadowGradientMap;
|
||
sampler2D _FaceShadowGradientMaskMap;
|
||
#endif
|
||
#if _FACE_3D_RIMLIGHT_AND_SHADOW
|
||
sampler2D _Face3DRimLightAndShadow_NoseRimLightMaskMap;
|
||
sampler2D _Face3DRimLightAndShadow_NoseShadowMaskMap;
|
||
sampler2D _Face3DRimLightAndShadow_CheekRimLightMaskMap;
|
||
sampler2D _Face3DRimLightAndShadow_CheekShadowMaskMap;
|
||
#endif
|
||
#if _NILOTOON_DISSOLVE
|
||
sampler2D _DissolveThresholdMap;
|
||
#endif
|
||
#if _PER_MAT_DISSOLVE_ON
|
||
sampler2D _PerMaterialDissolveThresholdMap;
|
||
sampler2D _PerMaterialDissolvePatternMap;
|
||
#endif
|
||
#if _NILOTOON_PERCHARACTER_BASEMAP_OVERRIDE
|
||
TEXTURE2D(_PerCharacterBaseMapOverrideMap);
|
||
#endif
|
||
#if NiloToonCharacterAreaColorFillPass
|
||
TEXTURE2D(_CharacterAreaColorFillTexture);
|
||
#endif
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// NiloToon's global textures
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#if NiloToonIsAnyLitColorPass
|
||
TEXTURE2D(_NiloToonAverageShadowMapRT);
|
||
#endif
|
||
|
||
#if _NILOTOON_RECEIVE_SELF_SHADOW
|
||
TEXTURE2D(_NiloToonCharSelfShadowMapRT);
|
||
SAMPLER_CMP(sampler_NiloToonCharSelfShadowMapRT_LinearClampCompare); // need LinearClampCompare, else Unity6 will sample the shadow map in point filter
|
||
#endif
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// CBUFFER (material local Uniforms)
|
||
// *you should put all per material uniforms of ALL passes inside the following UnityPerMaterial CBUFFER!
|
||
// else SRP batching is not possible!
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// Material shader variables are not defined in SRP or URP shader library.
|
||
// This means _BaseColor, _BaseMap, _BaseMap_ST, and all variables in the Properties section of a shader
|
||
// must be defined by the shader itself. If you define all those properties in CBUFFER named
|
||
// UnityPerMaterial, SRP can cache the material properties between frames and reduce significantly the cost
|
||
// of each drawcall.
|
||
// In this case, although URP's LitInput.hlsl contains the CBUFFER for some of the material
|
||
// properties defined in .shader file. As one can see this is not part of the ShaderLibrary, it specific to the
|
||
// URP Lit shader.
|
||
// So we are not going to use LitInput.hlsl, we will implement everything by ourself.
|
||
// #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl" (we don't use it)
|
||
|
||
// put all your uniforms(usually things inside .shader file's properties{}) inside this CBUFFER, in order to make SRP batcher compatible
|
||
// see -> https://blogs.unity3d.com/2019/02/28/srp-batcher-speed-up-your-rendering/
|
||
|
||
// IMPORTANT NOTE: Do not #ifdef #endif the properties inside CBUFFER as SRP batcher can NOT handle different CBUFFER layouts.
|
||
// In order to be SRP Batcher compatible,
|
||
// the "UnityPerMaterial" cbuffer has to have the exact same size and layout across all variants of one subshader.
|
||
// Because in SRP Batcher, UnityPerMaterial data are persistent in GPU memory (so we don't want to have tons of different layout to update in GPU memory ).
|
||
// Just use the same declaration for this cbuffer. It won't hurt performance because we won't upload the data to GPU per drawcall
|
||
// (won't hurt performance as long as material properties are not changing between frames, else a CBUFFER re upload to GPU is needed once any material properties changed,
|
||
// since this CBUFFER is huge, the cost of editing material properties is huge!)
|
||
// https://forum.unity.com/threads/cbuffer-inconsistent-size-inside-a-subshader.784994/#post-5725762
|
||
|
||
// IMPORTANT NOTE:
|
||
// [Vulkan SRP Batching note]
|
||
// Due to constant buffer(UnityPerMaterial CBUFFER) size limitation of vulkan (only in some android Mali GPU devices),
|
||
// if we make the (UnityPerMaterial CBUFFER) size too large, character rendering will be completely wrong(disappear / render as random color).
|
||
// We hardcode removed SRP batching on VULKAN for now (using macro SHADER_API_VULKAN), until we find a new solution.
|
||
// buggy devices list:
|
||
// - (vivo Y73s) (vivo Y72) (TAS_AN00) (mate40) (Huawei Honor 9x), all are Mali-G__ GPU + vulkan.
|
||
// safe devices list:
|
||
// - "Adreno GPU + vulkan" is safe (Xiaomi8)(Samsung A70)(RedmiNote9Pro)
|
||
// - "OpenGLES + any GPU" is safe also
|
||
// *Split a big CBUFFER into 2 CBUFFER(UnityPerMaterial, UnityPerMaterialTwo) doesn't work
|
||
#ifndef SHADER_API_VULKAN
|
||
CBUFFER_START(UnityPerMaterial)
|
||
#endif
|
||
|
||
float _RenderCharacter;
|
||
|
||
// identity if material is currently controlled by NiloToonPerCharacterRenderController script
|
||
float _ControlledByNiloToonPerCharacterRenderController;
|
||
|
||
// enable rendering
|
||
float _EnableRendering;
|
||
|
||
// UV Control
|
||
float _EnableUVEditGroup;
|
||
float4 _UV0ScaleOffset;
|
||
float4 _UV1ScaleOffset;
|
||
float4 _UV2ScaleOffset;
|
||
float4 _UV3ScaleOffset;
|
||
float4 _UV0CenterPivotScalePos;
|
||
float4 _UV1CenterPivotScalePos;
|
||
float4 _UV2CenterPivotScalePos;
|
||
float4 _UV3CenterPivotScalePos;
|
||
float2 _UV0ScrollSpeed;
|
||
float2 _UV1ScrollSpeed;
|
||
float2 _UV2ScrollSpeed;
|
||
float2 _UV3ScrollSpeed;
|
||
float _UV0RotatedAngle;
|
||
float _UV1RotatedAngle;
|
||
float _UV2RotatedAngle;
|
||
float _UV3RotatedAngle;
|
||
float _UV0RotateSpeed;
|
||
float _UV1RotateSpeed;
|
||
float _UV2RotateSpeed;
|
||
float _UV3RotateSpeed;
|
||
float2 _MatCapUVTiling;
|
||
|
||
// base color
|
||
float4 _BaseMap_ST;
|
||
float _BaseMapUVIndex;
|
||
half4 _BaseColor;
|
||
half4 _BaseColor2;
|
||
half _BaseMapBrightness;
|
||
half3 _PerCharacterBaseColorTint;
|
||
|
||
half _MultiplyBRPColor;
|
||
half4 _Color; // BRP _Color
|
||
|
||
// back face tint color, force shadow
|
||
half4 _BackFaceBaseMapReplaceColor;
|
||
half3 _BackFaceTintColor;
|
||
half _BackFaceForceShadow;
|
||
|
||
// alpha override
|
||
half _AlphaOverrideStrength;
|
||
half _AlphaOverrideMode;
|
||
float _AlphaOverrideTexUVIndex;
|
||
half _AlphaOverrideTexInvertColor;
|
||
half _AlphaOverrideTexValueScale;
|
||
half _AlphaOverrideTexValueOffset;
|
||
half _ApplyAlphaOverrideOnlyWhenFaceForwardIsPointingToCamera;
|
||
half _ApplyAlphaOverrideOnlyWhenFaceForwardIsPointingToCameraRemapStart;
|
||
half _ApplyAlphaOverrideOnlyWhenFaceForwardIsPointingToCameraRemapEnd;
|
||
half4 _AlphaOverrideTexChannelMask;
|
||
|
||
// alpha test
|
||
half _Cutoff;
|
||
|
||
// fina output alpha
|
||
half _EditFinalOutputAlphaEnable;
|
||
half _ForceFinalOutputAlphaEqualsOne;
|
||
|
||
// z offset
|
||
float _ZOffsetEnable;
|
||
float _ZOffset;
|
||
float _ZOffsetMultiplierForTraditionalOutlinePass;
|
||
float4 _ZOffsetMaskMapChannelMask;
|
||
float _ZOffsetMaskMapInvertColor;
|
||
|
||
// Parallax
|
||
half _Parallax;
|
||
float _ParallaxSampleUVIndex;
|
||
uint _ParallaxApplyToUVIndex;
|
||
|
||
// BaseMap Alpha Blending Layer (1-10)
|
||
half _BaseMapStackingLayer1MasterStrength;
|
||
float _BaseMapStackingLayer1TexUVIndex;
|
||
half4 _BaseMapStackingLayer1TintColor;
|
||
half _BaseMapStackingLayer1TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer1TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer1TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer1TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer1MaskTexChannel;
|
||
float _BaseMapStackingLayer1ApplytoFaces;
|
||
uint _BaseMapStackingLayer1MaskUVIndex;
|
||
half _BaseMapStackingLayer1MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer1MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer1MaskInvertColor;
|
||
half _BaseMapStackingLayer1MaskRemapStart;
|
||
half _BaseMapStackingLayer1MaskRemapEnd;
|
||
uint _BaseMapStackingLayer1ColorBlendMode;
|
||
float _BaseMapStackingLayer1TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer1TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer2MasterStrength;
|
||
float _BaseMapStackingLayer2TexUVIndex;
|
||
half4 _BaseMapStackingLayer2TintColor;
|
||
half _BaseMapStackingLayer2TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer2TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer2TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer2TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer2MaskTexChannel;
|
||
float _BaseMapStackingLayer2ApplytoFaces;
|
||
uint _BaseMapStackingLayer2MaskUVIndex;
|
||
half _BaseMapStackingLayer2MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer2MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer2MaskInvertColor;
|
||
half _BaseMapStackingLayer2MaskRemapStart;
|
||
half _BaseMapStackingLayer2MaskRemapEnd;
|
||
uint _BaseMapStackingLayer2ColorBlendMode;
|
||
float _BaseMapStackingLayer2TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer2TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer3MasterStrength;
|
||
float _BaseMapStackingLayer3TexUVIndex;
|
||
half4 _BaseMapStackingLayer3TintColor;
|
||
half _BaseMapStackingLayer3TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer3TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer3TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer3TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer3MaskTexChannel;
|
||
float _BaseMapStackingLayer3ApplytoFaces;
|
||
uint _BaseMapStackingLayer3MaskUVIndex;
|
||
half _BaseMapStackingLayer3MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer3MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer3MaskInvertColor;
|
||
half _BaseMapStackingLayer3MaskRemapStart;
|
||
half _BaseMapStackingLayer3MaskRemapEnd;
|
||
uint _BaseMapStackingLayer3ColorBlendMode;
|
||
float _BaseMapStackingLayer3TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer3TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer4MasterStrength;
|
||
float _BaseMapStackingLayer4TexUVIndex;
|
||
half4 _BaseMapStackingLayer4TintColor;
|
||
half _BaseMapStackingLayer4TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer4TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer4TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer4TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer4MaskTexChannel;
|
||
float _BaseMapStackingLayer4ApplytoFaces;
|
||
uint _BaseMapStackingLayer4MaskUVIndex;
|
||
half _BaseMapStackingLayer4MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer4MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer4MaskInvertColor;
|
||
half _BaseMapStackingLayer4MaskRemapStart;
|
||
half _BaseMapStackingLayer4MaskRemapEnd;
|
||
uint _BaseMapStackingLayer4ColorBlendMode;
|
||
float _BaseMapStackingLayer4TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer4TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer5MasterStrength;
|
||
float _BaseMapStackingLayer5TexUVIndex;
|
||
half4 _BaseMapStackingLayer5TintColor;
|
||
half _BaseMapStackingLayer5TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer5TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer5TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer5TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer5MaskTexChannel;
|
||
float _BaseMapStackingLayer5ApplytoFaces;
|
||
uint _BaseMapStackingLayer5MaskUVIndex;
|
||
half _BaseMapStackingLayer5MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer5MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer5MaskInvertColor;
|
||
half _BaseMapStackingLayer5MaskRemapStart;
|
||
half _BaseMapStackingLayer5MaskRemapEnd;
|
||
uint _BaseMapStackingLayer5ColorBlendMode;
|
||
float _BaseMapStackingLayer5TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer5TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer6MasterStrength;
|
||
float _BaseMapStackingLayer6TexUVIndex;
|
||
half4 _BaseMapStackingLayer6TintColor;
|
||
half _BaseMapStackingLayer6TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer6TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer6TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer6TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer6MaskTexChannel;
|
||
float _BaseMapStackingLayer6ApplytoFaces;
|
||
uint _BaseMapStackingLayer6MaskUVIndex;
|
||
half _BaseMapStackingLayer6MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer6MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer6MaskInvertColor;
|
||
half _BaseMapStackingLayer6MaskRemapStart;
|
||
half _BaseMapStackingLayer6MaskRemapEnd;
|
||
uint _BaseMapStackingLayer6ColorBlendMode;
|
||
float _BaseMapStackingLayer6TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer6TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer7MasterStrength;
|
||
float _BaseMapStackingLayer7TexUVIndex;
|
||
half4 _BaseMapStackingLayer7TintColor;
|
||
half _BaseMapStackingLayer7TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer7TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer7TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer7TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer7MaskTexChannel;
|
||
float _BaseMapStackingLayer7ApplytoFaces;
|
||
uint _BaseMapStackingLayer7MaskUVIndex;
|
||
half _BaseMapStackingLayer7MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer7MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer7MaskInvertColor;
|
||
half _BaseMapStackingLayer7MaskRemapStart;
|
||
half _BaseMapStackingLayer7MaskRemapEnd;
|
||
uint _BaseMapStackingLayer7ColorBlendMode;
|
||
float _BaseMapStackingLayer7TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer7TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer8MasterStrength;
|
||
float _BaseMapStackingLayer8TexUVIndex;
|
||
half4 _BaseMapStackingLayer8TintColor;
|
||
half _BaseMapStackingLayer8TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer8TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer8TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer8TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer8MaskTexChannel;
|
||
float _BaseMapStackingLayer8ApplytoFaces;
|
||
uint _BaseMapStackingLayer8MaskUVIndex;
|
||
half _BaseMapStackingLayer8MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer8MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer8MaskInvertColor;
|
||
half _BaseMapStackingLayer8MaskRemapStart;
|
||
half _BaseMapStackingLayer8MaskRemapEnd;
|
||
uint _BaseMapStackingLayer8ColorBlendMode;
|
||
float _BaseMapStackingLayer8TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer8TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer9MasterStrength;
|
||
float _BaseMapStackingLayer9TexUVIndex;
|
||
half4 _BaseMapStackingLayer9TintColor;
|
||
half _BaseMapStackingLayer9TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer9TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer9TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer9TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer9MaskTexChannel;
|
||
float _BaseMapStackingLayer9ApplytoFaces;
|
||
uint _BaseMapStackingLayer9MaskUVIndex;
|
||
half _BaseMapStackingLayer9MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer9MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer9MaskInvertColor;
|
||
half _BaseMapStackingLayer9MaskRemapStart;
|
||
half _BaseMapStackingLayer9MaskRemapEnd;
|
||
uint _BaseMapStackingLayer9ColorBlendMode;
|
||
float _BaseMapStackingLayer9TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer9TexUVRotateSpeed;
|
||
|
||
half _BaseMapStackingLayer10MasterStrength;
|
||
float _BaseMapStackingLayer10TexUVIndex;
|
||
half4 _BaseMapStackingLayer10TintColor;
|
||
half _BaseMapStackingLayer10TexIgnoreAlpha;
|
||
float2 _BaseMapStackingLayer10TexUVAnimSpeed;
|
||
float4 _BaseMapStackingLayer10TexUVScaleOffset;
|
||
float4 _BaseMapStackingLayer10TexUVCenterPivotScalePos;
|
||
half4 _BaseMapStackingLayer10MaskTexChannel;
|
||
float _BaseMapStackingLayer10ApplytoFaces;
|
||
uint _BaseMapStackingLayer10MaskUVIndex;
|
||
half _BaseMapStackingLayer10MaskTexAsIDMap;
|
||
half _BaseMapStackingLayer10MaskTexExtractFromID;
|
||
float _BaseMapStackingLayer10MaskInvertColor;
|
||
half _BaseMapStackingLayer10MaskRemapStart;
|
||
half _BaseMapStackingLayer10MaskRemapEnd;
|
||
uint _BaseMapStackingLayer10ColorBlendMode;
|
||
float _BaseMapStackingLayer10TexUVRotatedAngle;
|
||
float _BaseMapStackingLayer10TexUVRotateSpeed;
|
||
|
||
// normal map
|
||
half _BumpScale;
|
||
float _BumpMapApplytoFaces;
|
||
float _BumpMapUVIndex;
|
||
float4 _BumpMapUVScaleOffset;
|
||
float2 _BumpMapUVScrollSpeed;
|
||
|
||
// emission
|
||
float4 _EmissionMapTilingXyOffsetZw;
|
||
float2 _EmissionMapUVScrollSpeed;
|
||
half _EmissionIntensity;
|
||
half3 _EmissionColor;
|
||
half _MultiplyBaseColorToEmissionColor;
|
||
half _MultiplyLightColorToEmissionColor;
|
||
float _EmissionAnimTintRampMapSpeed;
|
||
half _EmissionMapUseSingleChannelOnly;
|
||
half4 _EmissionMapSingleChannelMask;
|
||
half4 _EmissionMaskMapChannelMask;
|
||
half _EmissionMaskMapInvertColor;
|
||
half _EmissionMaskMapRemapStart;
|
||
half _EmissionMaskMapRemapEnd;
|
||
|
||
// mat cap (alpha blend)
|
||
half _MatCapAlphaBlendUsage;
|
||
half4 _MatCapAlphaBlendTintColor;
|
||
float _MatCapAlphaBlendUvScale;
|
||
half _MatCapAlphaBlendMapAlphaAsMask;
|
||
half4 _MatCapAlphaBlendMaskMapChannelMask;
|
||
half _MatCapAlphaBlendMaskMapInvertColor;
|
||
half _MatCapAlphaBlendMaskMapRemapStart;
|
||
half _MatCapAlphaBlendMaskMapRemapEnd;
|
||
|
||
// mat cap (additive)
|
||
half _MatCapAdditiveMapAlphaAsMask;
|
||
half _MatCapAdditiveIntensity;
|
||
half _MatCapAdditiveExtractBrightArea;
|
||
half4 _MatCapAdditiveColor;
|
||
half _MatCapAdditiveMixWithBaseMapColor;
|
||
float _MatCapAdditiveUvScale;
|
||
half4 _MatCapAdditiveMaskMapChannelMask;
|
||
half _MatCapAdditiveMaskMapInvertColor;
|
||
half _MatCapAdditiveMaskMapRemapStart;
|
||
half _MatCapAdditiveMaskMapRemapEnd;
|
||
float _MatCapAdditiveApplytoFaces;
|
||
|
||
// mat cap (occlusion)
|
||
half _MatCapOcclusionIntensity;
|
||
half4 _MatCapOcclusionMapChannelMask;
|
||
half _MatCapOcclusionMapRemapStart;
|
||
half _MatCapOcclusionMapRemapEnd;
|
||
half _MatCapOcclusionMapAlphaAsMask;
|
||
float _MatCapOcclusionUvScale;
|
||
half4 _MatCapOcclusionMaskMapChannelMask;
|
||
half _MatCapOcclusionMaskMapInvert;
|
||
half _MatCapOcclusionMaskMapRemapStart;
|
||
half _MatCapOcclusionMaskMapRemapEnd;
|
||
|
||
// occlusion
|
||
half _OcclusionStrength;
|
||
half _OcclusionStrengthIndirectMultiplier;
|
||
half4 _OcclusionMapChannelMask;
|
||
float _OcclusionMapUVIndex;
|
||
half _OcclusionMapInvertColor;
|
||
half _OcclusionRemapStart;
|
||
half _OcclusionRemapEnd;
|
||
float _OcclusionMapApplytoFaces;
|
||
|
||
// shading grade map
|
||
half _ShadingGradeMapStrength;
|
||
half _ShadingGradeMapApplyRange;
|
||
half _ShadingGradeMapMidPointOffset;
|
||
half4 _ShadingGradeMapChannelMask;
|
||
half _ShadingGradeMapInvertColor;
|
||
half _ShadingGradeMapRemapStart;
|
||
half _ShadingGradeMapRemapEnd;
|
||
|
||
// Face Shadow Gradient Map
|
||
half4 _FaceShadowGradientMapChannel;
|
||
float _FaceShadowGradientMapUVIndex;
|
||
half _FaceShadowGradientMapInvertColor;
|
||
half _FaceShadowGradientOffset;
|
||
float _FaceShadowGradientMapUVxInvert;
|
||
float4 _FaceShadowGradientMapUVCenterPivotScalePos;
|
||
float4 _FaceShadowGradientMapUVScaleOffset;
|
||
float _DebugFaceShadowGradientMap;
|
||
half _FaceShadowGradientIntensity;
|
||
half _FaceShadowGradientMapFaceMidPoint;
|
||
half _FaceShadowGradientResultSoftness;
|
||
half4 _FaceShadowGradientMaskMapChannel;
|
||
half _FaceShadowGradientMaskMapInvertColor;
|
||
float _FaceShadowGradientMaskMapUVIndex;
|
||
half _FaceShadowGradientThresholdMin;
|
||
half _FaceShadowGradientThresholdMax;
|
||
half _IgnoreDefaultMainLightFaceShadow;
|
||
|
||
// Face 3D Rim Light and Shadow
|
||
half _Face3DRimLightAndShadow_NoseRimLightIntensity;
|
||
half3 _Face3DRimLightAndShadow_NoseRimLightTintColor;
|
||
half4 _Face3DRimLightAndShadow_NoseRimLightMaskMapChannel;
|
||
|
||
half _Face3DRimLightAndShadow_NoseShadowIntensity;
|
||
half3 _Face3DRimLightAndShadow_NoseShadowTintColor;
|
||
half4 _Face3DRimLightAndShadow_NoseShadowMaskMapChannel;
|
||
|
||
half _Face3DRimLightAndShadow_CheekRimLightIntensity;
|
||
half3 _Face3DRimLightAndShadow_CheekRimLightTintColor;
|
||
half4 _Face3DRimLightAndShadow_CheekRimLightMaskMapChannel;
|
||
half _Face3DRimLightAndShadow_CheekRimLightThreshold;
|
||
half _Face3DRimLightAndShadow_CheekRimLightSoftness;
|
||
|
||
half _Face3DRimLightAndShadow_CheekShadowIntensity;
|
||
half3 _Face3DRimLightAndShadow_CheekShadowTintColor;
|
||
half4 _Face3DRimLightAndShadow_CheekShadowMaskMapChannel;
|
||
half _Face3DRimLightAndShadow_CheekShadowThreshold;
|
||
half _Face3DRimLightAndShadow_CheekShadowSoftness;
|
||
|
||
// smoothness
|
||
half _Smoothness;
|
||
half _SmoothnessMapInputIsRoughnessMap;
|
||
half4 _SmoothnessMapChannelMask;
|
||
half _SmoothnessMapRemapStart;
|
||
half _SmoothnessMapRemapEnd;
|
||
|
||
// specular
|
||
float _SpecularMapUVIndex;
|
||
half4 _SpecularMapChannelMask;
|
||
half _SpecularMapAsIDMap;
|
||
half _SpecularMapExtractFromID;
|
||
half _SpecularMapInvertColor;
|
||
half _SpecularMapRemapStart;
|
||
half _SpecularMapRemapEnd;
|
||
half _UseGGXDirectSpecular;
|
||
half _SpecularReactToLightDirMode;
|
||
half _SpecularIntensity;
|
||
half3 _SpecularColor;
|
||
half _SpecularUseReplaceBlending;
|
||
half _MultiplyBaseColorToSpecularColor;
|
||
half _GGXDirectSpecularSmoothnessMultiplier;
|
||
half _SpecularColorTintMapUsage;
|
||
float _SpecularColorTintMapUseSecondUv;
|
||
float4 _SpecularColorTintMapTilingXyOffsetZw;
|
||
half _SpecularAreaRemapUsage;
|
||
half _SpecularAreaRemapMidPoint;
|
||
half _SpecularAreaRemapRange;
|
||
half _SpecularShowInShadowArea;
|
||
float _SpecularApplytoFaces;
|
||
|
||
// Glitter (NiloToon)
|
||
float _NiloGlitterIntensity;
|
||
float _NiloGlitterDensity;
|
||
float _NiloGlitterSize;
|
||
float _NiloGlitterRndomNormalStrength;
|
||
half3 _NiloGlitterTintColor;
|
||
uint _NiloGlitterUVIndex;
|
||
uint _NiloGlitterTintColorTexUVIndex;
|
||
|
||
// Environment Reflections
|
||
half _EnvironmentReflectionUsage;
|
||
half _EnvironmentReflectionShouldApplyToFaceArea;
|
||
half _EnvironmentReflectionBrightness;
|
||
half3 _EnvironmentReflectionColor;
|
||
half _EnvironmentReflectionTintAlbedo;
|
||
half _EnvironmentReflectionApplyReplaceBlending;
|
||
half _EnvironmentReflectionApplyAddBlending;
|
||
half _EnvironmentReflectionSmoothnessMultiplier;
|
||
half _EnvironmentReflectionFresnelEffect;
|
||
half _EnvironmentReflectionFresnelPower;
|
||
half _EnvironmentReflectionFresnelRemapStart;
|
||
half _EnvironmentReflectionFresnelRemapEnd;
|
||
half4 _EnvironmentReflectionMaskMapChannelMask;
|
||
half _EnvironmentReflectionMaskMapInvertColor;
|
||
half _EnvironmentReflectionMaskMapRemapStart;
|
||
half _EnvironmentReflectionMaskMapRemapEnd;
|
||
float _EnvironmentReflectionApplytoFaces;
|
||
|
||
// kayjiya-kay hair specular
|
||
half _HairStrandSpecularMixWithBaseMapColor;
|
||
half _HairStrandSpecularTintMapUsage;
|
||
float4 _HairStrandSpecularTintMapTilingXyOffsetZw;
|
||
half _HairStrandSpecularOverallIntensity;
|
||
half _HairStrandSpecularShapeFrequency;
|
||
half _HairStrandSpecularShapeShift;
|
||
half _HairStrandSpecularShapePositionOffset;
|
||
half _HairStrandSpecularMainIntensity;
|
||
half _HairStrandSpecularSecondIntensity;
|
||
half3 _HairStrandSpecularMainColor;
|
||
half3 _HairStrandSpecularSecondColor;
|
||
half _HairStrandSpecularMainExponent;
|
||
half _HairStrandSpecularSecondExponent;
|
||
uint _HairStrandSpecularUVIndex;
|
||
uint _HairStrandSpecularUVDirection;
|
||
|
||
// detail map
|
||
float _DetailUseSecondUv;
|
||
half4 _DetailMaskChannelMask;
|
||
half _DetailMaskInvertColor;
|
||
float4 _DetailMapsScaleTiling;
|
||
half _DetailAlbedoWhitePoint;
|
||
half _DetailAlbedoMapScale;
|
||
half _DetailNormalMapScale;
|
||
|
||
// shadow color
|
||
half _EnableShadowColor;
|
||
half _SelfShadowAreaHSVStrength;
|
||
half _SelfShadowAreaHueOffset;
|
||
half _SelfShadowAreaSaturationBoost;
|
||
half _SelfShadowAreaValueMul;
|
||
half3 _SelfShadowTintColor;
|
||
half _LitToShadowTransitionAreaIntensity;
|
||
half _LitToShadowTransitionAreaHueOffset;
|
||
half _LitToShadowTransitionAreaSaturationBoost;
|
||
half _LitToShadowTransitionAreaValueMul;
|
||
half3 _LitToShadowTransitionAreaTintColor;
|
||
half4 _LowSaturationFallbackColor;
|
||
half _OverrideBySkinShadowTintColor;
|
||
half3 _SkinShadowTintColor;
|
||
half3 _SkinShadowTintColor2;
|
||
half _SkinShadowBrightness;
|
||
half _OverrideByFaceShadowTintColor;
|
||
half3 _FaceShadowTintColor;
|
||
half3 _FaceShadowTintColor2;
|
||
half _FaceShadowBrightness;
|
||
|
||
// override shadow color by tex
|
||
half _OverrideShadowColorByTexMode;
|
||
half _OverrideShadowColorByTexIntensity;
|
||
half4 _OverrideShadowColorTexTintColor;
|
||
half _OverrideShadowColorTexIgnoreAlphaChannel;
|
||
half4 _OverrideShadowColorMaskMapChannelMask;
|
||
half _OverrideShadowColorMaskMapInvertColor;
|
||
|
||
// ramp lighting texture
|
||
float _RampLightTexMode;
|
||
half _RampLightingTexSampleUvY;
|
||
half _RampLightingNdotLRemapStart;
|
||
half _RampLightingNdotLRemapEnd;
|
||
half4 _RampLightingSampleUvYTexChannelMask;
|
||
half _RampLightingSampleUvYTexInvertColor;
|
||
half _RampLightingUvYRemapStart;
|
||
half _RampLightingUvYRemapEnd;
|
||
half _RampLightingFaceAreaRemoveEffect;
|
||
|
||
// ramp specular texture
|
||
half _RampSpecularTexSampleUvY;
|
||
half _RampSpecularWhitePoint;
|
||
|
||
// lighting style
|
||
half _AsUnlit;
|
||
half _CelShadeMidPoint;
|
||
half _CelShadeSoftness;
|
||
half _MainLightIgnoreCelShade;
|
||
half _MainLightSkinDiffuseNormalMapStrength;
|
||
half _MainLightNonSkinDiffuseNormalMapStrength;
|
||
half _IndirectLightFlatten;
|
||
half _AdditionalLightCelShadeMidPoint;
|
||
half _AdditionalLightCelShadeSoftness;
|
||
half _AdditionalLightIgnoreCelShade;
|
||
half _AdditionalLightIgnoreOcclusion;
|
||
half _AdditionalLightDistanceAttenuationClamp;
|
||
|
||
// lighting style for face area
|
||
float _OverrideCelShadeParamForFaceArea;
|
||
half _CelShadeMidPointForFaceArea;
|
||
half _CelShadeSoftnessForFaceArea;
|
||
half _MainLightIgnoreCelShadeForFaceArea;
|
||
float _OverrideAdditionalLightCelShadeParamForFaceArea;
|
||
half _AdditionalLightCelShadeMidPointForFaceArea;
|
||
half _AdditionalLightCelShadeSoftnessForFaceArea;
|
||
half _AdditionalLightIgnoreCelShadeForFaceArea;
|
||
|
||
// URP shadow mapping (directional light)
|
||
float _ReceiveURPShadowMapping; // on/off toggle per material
|
||
half _ReceiveURPShadowMappingAmount;
|
||
half _ReceiveURPShadowMappingAmountForFace;
|
||
half _ReceiveURPShadowMappingAmountForNonFace;
|
||
float _ReceiveSelfShadowMappingPosOffset;
|
||
float _ReceiveSelfShadowMappingPosOffsetForFaceArea;
|
||
half3 _URPShadowMappingTintColor;
|
||
|
||
// URP shadow mapping (additional light)
|
||
half _ReceiveURPAdditionalLightShadowMapping;
|
||
half _ReceiveURPAdditionalLightShadowMappingAmount;
|
||
half _ReceiveURPAdditionalLightShadowMappingAmountForNonFace;
|
||
half _ReceiveURPAdditionalLightShadowMappingAmountForFace;
|
||
|
||
// depth texture rim light and shadow
|
||
float _PerMaterialEnableDepthTextureRimLightAndShadow;
|
||
|
||
float _DepthTexRimLightAndShadowWidthMultiplier;
|
||
float _DepthTexRimLight3DRimMaskEnable;
|
||
float _DepthTexRimLight3DRimMaskThreshold;
|
||
float _DepthTexRimLightAndShadowWidthExtraMultiplier;
|
||
float _DepthTexRimLightAndShadowReduceWidthWhenCameraIsClose;
|
||
float _DepthTexRimLightAndShadowSafeViewDistance;
|
||
|
||
float _DepthTexRimLightIgnoreLightDir;
|
||
float _DepthTexShadowFixedDirectionForFace;
|
||
|
||
float _DepthTexRimLightWidthMultiplier;
|
||
float _DepthTexRimLightWidthClampForFace;
|
||
float _DepthTexShadowWidthMultiplier;
|
||
|
||
float _UseDepthTexRimLightAndShadowWidthMultiplierFromVertexColor;
|
||
float4 _DepthTexRimLightAndShadowWidthMultiplierFromVertexColorChannelMask;
|
||
float4 _DepthTexRimLightAndShadowWidthTexChannelMask;
|
||
|
||
half _DepthTexRimLightUsage;
|
||
half4 _DepthTexRimLightMaskTexChannelMask;
|
||
half _DepthTexRimLightMaskTexInvertColor;
|
||
half _DepthTexRimLightIntensity;
|
||
half3 _DepthTexRimLightTintColor;
|
||
half _DepthTexRimLightMixWithBaseMapColor;
|
||
half _DepthTexRimLightBlockByShadow;
|
||
float _DepthTexRimLightThresholdOffset;
|
||
float _DepthTexRimLightMinimumThresholdOffsetForFace;
|
||
float _DepthTexRimLightFadeoutRange;
|
||
|
||
half _DepthTexRimLight3DFallbackMidPoint;
|
||
half _DepthTexRimLight3DFallbackSoftness;
|
||
half _DepthTexRimLight3DFallbackRemoveFlatPolygonRimLight;
|
||
|
||
float _DepthTexRimLightFixDottedLineArtifactsExtendMultiplier;
|
||
|
||
half _DepthTexShadowUsage;
|
||
half _DepthTexShadowBrightness;
|
||
half _DepthTexShadowBrightnessForFace;
|
||
half3 _DepthTexShadowTintColor;
|
||
half3 _DepthTexShadowTintColorForFace;
|
||
float _DepthTexShadowThresholdOffset;
|
||
float _DepthTexShadowFadeoutRange;
|
||
|
||
float _FaceAreaCameraDepthTextureZWriteOffset; // a just enough ZOffset for face area when writing depth into _CameraDepthTexture.Good ZOffset range is about 0.04 to 0.06, which makes depth texture shadow easier to appear
|
||
|
||
// Character ID for read global 1D array or load 1D texture
|
||
uint _CharacterID;
|
||
|
||
// NiloToon self shadow mapping
|
||
float _EnableNiloToonSelfShadowMapping;
|
||
float _NiloToonSelfShadowMappingDepthBias;
|
||
float _NiloToonSelfShadowMappingNormalBias;
|
||
half _NiloToonSelfShadowIntensity;
|
||
half _NiloToonSelfShadowIntensityForNonFace;
|
||
half _NiloToonSelfShadowIntensityForFace;
|
||
half3 _NiloToonSelfShadowMappingTintColor;
|
||
float _EnableNiloToonSelfShadowMappingDepthBias;
|
||
float _EnableNiloToonSelfShadowMappingNormalBias;
|
||
|
||
// per char receive shadows
|
||
half _PerCharReceiveAverageURPShadowMap;
|
||
half _PerCharReceiveStandardURPShadowMap;
|
||
half _PerCharReceiveNiloToonSelfShadowMap;
|
||
|
||
// outline
|
||
float _RenderOutline;
|
||
float _PerCharacterRenderOutline;
|
||
float _OutlineUseBakedSmoothNormal;
|
||
// float _UnityCameraDepthTextureWriteOutlineExtrudedPosition; // removed since NiloToon0.17.10
|
||
float _OutlineUniformLengthInViewSpace;
|
||
half3 _OutlineTintColor;
|
||
half4 _OutlineTintColorSkinAreaOverride;
|
||
half3 _OutlineOcclusionAreaTintColor;
|
||
half _OutlineUseReplaceColor;
|
||
half3 _OutlineReplaceColor;
|
||
half _OutlineUsePreLightingReplaceColor;
|
||
half3 _OutlinePreLightingReplaceColor;
|
||
|
||
float _OutlineWidth;
|
||
float _OutlineWidthExtraMultiplier;
|
||
|
||
float _OutlineApplyAutoWidthAdjustment;
|
||
|
||
float _OutlineBaseZOffset;
|
||
float _OutlineZOffset;
|
||
float _OutlineZOffsetForFaceArea;
|
||
float _UseOutlineZOffsetTex;
|
||
float4 _OutlineZOffsetMaskTexChannelMask;
|
||
float _OutlineZOffsetMaskTexInvertColor;
|
||
float _OutlineZOffsetMaskRemapStart;
|
||
float _OutlineZOffsetMaskRemapEnd;
|
||
float _UseOutlineZOffsetMaskFromVertexColor;
|
||
float4 _OutlineZOffsetMaskFromVertexColor;
|
||
float _OutlineZOffsetMaskTexFromVertexColorInvertColor;
|
||
float _UseOutlineWidthMaskFromVertexColor;
|
||
float4 _OutlineWidthMaskFromVertexColor;
|
||
float4 _OutlineWidthTexChannelMask;
|
||
|
||
float _PerCharacterOutlineWidthMultiply;
|
||
half3 _PerCharacterOutlineColorTint;
|
||
half4 _PerCharacterOutlineColorLerp;
|
||
|
||
// override outline color by tex
|
||
half4 _OverrideOutlineColorTexTintColor;
|
||
half _OverrideOutlineColorTexIgnoreAlphaChannel;
|
||
half _OverrideOutlineColorByTexIntensity;
|
||
|
||
// screen space outline
|
||
float _ScreenSpaceOutlineWidth;
|
||
float _ScreenSpaceOutlineWidthIfFace;
|
||
float _ScreenSpaceOutlineDepthSensitivity;
|
||
float _ScreenSpaceOutlineDepthSensitivityIfFace;
|
||
float _ScreenSpaceOutlineNormalsSensitivity;
|
||
float _ScreenSpaceOutlineNormalsSensitivityIfFace;
|
||
half3 _ScreenSpaceOutlineTintColor;
|
||
half3 _ScreenSpaceOutlineOcclusionAreaTintColor;
|
||
half _ScreenSpaceOutlineUseReplaceColor;
|
||
half3 _ScreenSpaceOutlineReplaceColor;
|
||
half4 _ScreenSpaceOutlineDepthSensitivityTexChannelMask;
|
||
half4 _ScreenSpaceOutlineNormalsSensitivityTexChannelMask;
|
||
half _ScreenSpaceOutlineDepthSensitivityTexRemapStart;
|
||
half _ScreenSpaceOutlineDepthSensitivityTexRemapEnd;
|
||
half _ScreenSpaceOutlineNormalsSensitivityTexRemapStart;
|
||
half _ScreenSpaceOutlineNormalsSensitivityTexRemapEnd;
|
||
|
||
// dynamic eye
|
||
float _DynamicEyeSize;
|
||
half _DynamicEyeFinalBrightness;
|
||
half3 _DynamicEyeFinalTintColor;
|
||
half4 _DynamicEyePupilMaskTexChannelMask;
|
||
half3 _DynamicEyePupilColor;
|
||
float _DynamicEyePupilDepthScale;
|
||
float _DynamicEyePupilSize;
|
||
float _DynamicEyePupilMaskSoftness;
|
||
float4 _DynamicEyeWhiteMap_ST;
|
||
|
||
// color fill
|
||
half _CharacterAreaColorFillEnabled;
|
||
half4 _CharacterAreaColorFillColor;
|
||
uint _CharacterAreaColorFillTextureUVIndex;
|
||
float4 _CharacterAreaColorFillTextureUVTilingOffset;
|
||
float2 _CharacterAreaColorFillTextureUVScrollSpeed;
|
||
half _CharacterAreaColorFillRendersVisibleArea;
|
||
half _CharacterAreaColorFillRendersBlockedArea;
|
||
|
||
// extra thick outline
|
||
float _ExtraThickOutlineEnabled;
|
||
float _ExtraThickOutlineWidth;
|
||
float _ExtraThickOutlineMaxFinalWidth;
|
||
float3 _ExtraThickOutlineViewSpacePosOffset;
|
||
half4 _ExtraThickOutlineColor;
|
||
float _ExtraThickOutlineZOffset;
|
||
float _ExtraThickOutlineZWrite;
|
||
float _ExtraThickOutlineWriteIntoDepthTexture;
|
||
|
||
// gameplay effect
|
||
half3 _PerCharEffectTintColor;
|
||
half3 _PerCharEffectAddColor;
|
||
half4 _PerCharEffectLerpColor;
|
||
half3 _PerCharEffectRimColor;
|
||
half _PerCharEffectRimSharpnessPower;
|
||
half _PerCharEffectDesaturatePercentage;
|
||
|
||
// skin
|
||
float _IsSkin;
|
||
half4 _SkinMaskMapChannelMask;
|
||
half _SkinMaskMapInvertColor;
|
||
half _SkinMaskMapRemapStart;
|
||
half _SkinMaskMapRemapEnd;
|
||
half _SkinMaskMapAsIDMap;
|
||
half _SkinMaskMapExtractFromID;
|
||
|
||
// face
|
||
half4 _FaceMaskMapChannelMask;
|
||
half _FaceMaskMapInvertColor;
|
||
half _FaceMaskMapRemapStart;
|
||
half _FaceMaskMapRemapEnd;
|
||
//half3 _FaceForwardDirection; // converted to global array
|
||
//half3 _FaceUpDirection; // converted to global array
|
||
half _FixFaceNormalAmount;
|
||
half _FixFaceNormalAmountPerMaterial;
|
||
half _FixFaceNormalUseFlattenOrProxySphereMethod;
|
||
|
||
// per char bounding sphere
|
||
//float3 _CharacterBoundCenterPosWS; // converted to global array
|
||
float _CharacterBoundRadius;
|
||
|
||
// dither fadeout
|
||
float _DitherFadeoutAmount; // clip() only accept float
|
||
float _DitherFadeoutNormalScaleFix;
|
||
|
||
// perspective removal
|
||
float _PerspectiveRemovalAmount; // total amount
|
||
|
||
// perspective removal(sphere)
|
||
float _PerspectiveRemovalRadius;
|
||
//float3 _HeadBonePositionWS; // converted to global array
|
||
|
||
// perspective removal(world height)
|
||
float _PerspectiveRemovalStartHeight; // usually is world space pos.y 0
|
||
float _PerspectiveRemovalEndHeight;
|
||
|
||
// ZWrite (for disabling all _CameraDepthTexture related effect when _ZWrite = off)
|
||
float _ZWrite;
|
||
// Cull (for fixing outline problem when rendering planar reflection pass)
|
||
float _Cull;
|
||
// Blending (for outputing correct alpha at the exit of fragment shader)
|
||
float _SrcBlend;
|
||
float _DstBlend;
|
||
|
||
// _AllowNiloToonBloomOverrideGroup
|
||
float _AllowNiloToonBloomCharacterAreaOverride;
|
||
float _AllowedNiloToonBloomOverrideStrength;
|
||
|
||
// _NILOTOON_DISSOLVE
|
||
float _DissolveAmount;
|
||
uint _DissolveMode; // C# uses material.SetInteger(_DissolveMode,value), we must match with uint here, do not use float! else dissolve may not work at all in Unity2021.3.x
|
||
float _DissolveThresholdMapTilingX;
|
||
float _DissolveThresholdMapTilingY;
|
||
float _DissolveNoiseStrength;
|
||
float _DissolveBorderRange;
|
||
half3 _DissolveBorderTintColor;
|
||
|
||
// per material dissolve
|
||
half _PerMaterialDissolveCutoff;
|
||
half _PerMaterialDissolveThresholdMapUVTiling;
|
||
uint _PerMaterialDissolveThresholdMapUVIndex;
|
||
half _PerMaterialDissolvePatternMapStrength;
|
||
half4 _PerMaterialDissolveThresholdMapChannelMask;
|
||
half _PerMaterialDissolveThresholdMapInvertColor;
|
||
half _PerMaterialDissolvePatternMapUVTiling;
|
||
uint _PerMaterialDissolvePatternMapUVIndex;
|
||
half4 _PerMaterialDissolvePatternMapChannelMask;
|
||
half _PerMaterialDissolvePatternMapInvertColor;
|
||
half _PerMaterialDissolveEdgeWidth;
|
||
half3 _PerMaterialDissolveEdgeColor;
|
||
|
||
// per character basemap override
|
||
half _PerCharacterBaseMapOverrideAmount;
|
||
float4 _PerCharacterBaseMapOverrideTilingOffset;
|
||
float2 _PerCharacterBaseMapOverrideUVScrollSpeed;
|
||
half3 _PerCharacterBaseMapOverrideTintColor;
|
||
uint _PerCharacterBaseMapOverrideUVIndex;
|
||
uint _PerCharacterBaseMapOverrideBlendMode;
|
||
|
||
// per character ZOffset
|
||
float _PerCharZOffset;
|
||
|
||
// Decal
|
||
half _DecalAlbedoApplyStrength;
|
||
half _DecalNormalApplyStrength;
|
||
half _DecalOcclusionApplyStrength;
|
||
half _DecalSmoothnessApplyStrength;
|
||
half _DecalSpecularApplyStrength;
|
||
|
||
// Pass On/Off
|
||
float _AllowRenderURPShadowCasterPass;
|
||
float _AllowRenderDepthOnlyOrDepthNormalsPass;
|
||
|
||
float _AllowRenderNiloToonSelfShadowPass;
|
||
float _AllowRenderExtraThickOutlinePass;
|
||
float _AllowRenderNiloToonCharacterAreaStencilBufferFillPass;
|
||
float _AllowRenderNiloToonCharacterAreaColorFillPass;
|
||
|
||
// Per character effect on/off
|
||
float _AllowPerCharacterDissolve;
|
||
float _AllowPerCharacterDitherFadeout;
|
||
|
||
// Pre-Multiply alpha options
|
||
half _PreMultiplyAlphaIntoRGBOutput;
|
||
|
||
#ifndef SHADER_API_VULKAN
|
||
CBUFFER_END
|
||
#endif
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Global uniforms
|
||
// if an uniform is not a per material uniform,
|
||
// it is fine to write it outside of CBUFFER_START(UnityPerMaterial)
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// a special uniform for applyShadowBiasFixToHClipPos() only
|
||
float3 _LightDirection;
|
||
float3 _LightPosition;
|
||
|
||
// global outline uniforms
|
||
float _GlobalShouldRenderOutline;
|
||
float _GlobalOutlineWidthMultiplier;
|
||
half3 _GlobalOutlineTintColor;
|
||
float _GlobalOutlineWidthAutoAdjustToCameraDistanceAndFOV;
|
||
|
||
// global shadow mapping uniforms
|
||
float _GlobalShouldReceiveShadowMapping;
|
||
half _GlobalReceiveShadowMappingAmount;
|
||
float _GlobalToonShaderNormalBiasMultiplier;
|
||
half3 _GlobalMainLightURPShadowAsDirectResultTintColor;
|
||
half _GlobalURPShadowAsDirectLightTintIgnoreMaterialURPUsageSetting;
|
||
half _GlobalNiloToonReceiveURPShadowblurriness;
|
||
|
||
// global self shadow mapping
|
||
float4x4 _NiloToonSelfShadowWorldToClip;
|
||
float4 _NiloToonSelfShadowParam;
|
||
float4 _NiloToonSelfShadowSoftShadowParam; // x: soft shadow quality | y: should reshape | z: reshape blur size
|
||
float _NiloToonSelfShadowRange;
|
||
float _NiloToonGlobalSelfShadowCasterDepthBias;
|
||
float _NiloToonGlobalSelfShadowCasterNormalBias;
|
||
float _NiloToonGlobalSelfShadowReceiverDepthBias;
|
||
float _NiloToonGlobalSelfShadowReceiverNormalBias;
|
||
half3 _NiloToonSelfShadowLightDirection;
|
||
half _NiloToonSelfShadowUseNdotLFix;
|
||
half _GlobalReceiveNiloToonSelfShadowMap;
|
||
float _GlobalReceiveSelfShadowMappingPosOffset;
|
||
|
||
// global occlusion uniforms
|
||
half _GlobalOcclusionStrength;
|
||
|
||
// global lighting uniforms
|
||
half _GlobalIndirectLightMultiplier;
|
||
half3 _GlobalIndirectLightMinColor;
|
||
|
||
// global depth diff rim light and shadow uniforms
|
||
float _GlobalEnableDepthTextureRimLigthAndShadow;
|
||
float _GlobalDepthTexRimLightAndShadowWidthMultiplier;
|
||
float _GlobalDepthTexRimLightDepthDiffThresholdOffset;
|
||
float _GlobalDepthTexRimLightCameraDistanceFadeoutStartDistance;
|
||
float _GlobalDepthTexRimLightCameraDistanceFadeoutEndDistance;
|
||
|
||
// global light uniforms
|
||
half3 _GlobalUserOverriddenMainLightDirVS;
|
||
half3 _GlobalUserOverriddenMainLightDirWS;
|
||
half3 _GlobalUserOverriddenMainLightColor;
|
||
half4 _GlobalUserOverriddenFinalMainLightDirWSParam; // xyz: final main light direction, w: 1 if enabled, else 0
|
||
half4 _GlobalUserOverriddenFinalMainLightColorParam; // rgb: final main light color, w: 1 if enabled, else 0
|
||
|
||
// global camera uniforms
|
||
float _CurrentCameraFOV;
|
||
|
||
// global camera fix
|
||
float2 _GlobalAspectFix;
|
||
float _GlobalFOVorOrthoSizeFix;
|
||
|
||
// global volume
|
||
half3 _GlobalVolumeMulColor;
|
||
half4 _GlobalVolumeLerpColor;
|
||
half3 _GlobalRimLightMultiplier;
|
||
half3 _GlobalRimLightMultiplierForOutlineArea;
|
||
half3 _GlobalIndirectLightMaxColor;
|
||
half3 _GlobalMainDirectionalLightMaxContribution;
|
||
half3 _GlobalAdditionalLightMaxContribution;
|
||
half3 _GlobalSpecularTintColor;
|
||
half _GlobalSpecularMinIntensity;
|
||
half _GlobalSpecularReactToLightDirectionChange;
|
||
half3 _GlobalMainDirectionalLightMultiplier;
|
||
half3 _GlobalAdditionalLightMultiplier;
|
||
half3 _GlobalAdditionalLightMultiplierForFaceArea;
|
||
half3 _GlobalAdditionalLightMultiplierForOutlineArea;
|
||
half _GlobalAdditionalLightApplyRimMask;
|
||
half _GlobalAdditionalLightRimMaskPower;
|
||
half _GlobalAdditionalLightRimMaskSoftness;
|
||
half3 _GlobalVolumeBaseColorTintColor;
|
||
half3 _GlobalCharacterOverallShadowTintColor;
|
||
half3 _GlobalCharacterOverallShadowTintColorForSkinFace;
|
||
half3 _GlobalCharacterOverallShadowTintColorForNonSkinFace;
|
||
half _GlobalCharacterOverallShadowStrength;
|
||
|
||
half _GlobalCinematic3DRimMaskEnabled;
|
||
half _GlobalCinematic3DRimMaskStrength_ClassicStyle;
|
||
half _GlobalCinematic3DRimMaskSharpness_ClassicStyle;
|
||
half _GlobalCinematic3DRimMaskBlur_ClassicStyle;
|
||
half _GlobalCinematic3DRimMaskWidth_ClassicStyle;
|
||
half _GlobalCinematic3DRimMaskStrength_DynamicStyle;
|
||
half _GlobalCinematic3DRimMaskWidth_DynamicStyle;
|
||
half _GlobalCinematic3DRimMaskBlur_DynamicStyle;
|
||
half _GlobalCinematic3DRimMaskSharpness_DynamicStyle;
|
||
half _GlobalCinematic3DRimMaskStrength_StableStyle;
|
||
half _GlobalCinematic3DRimMaskSharpness_StableStyle;
|
||
half _GlobalCinematic2DRimMaskStrength;
|
||
half _GlobalCinematicRimTintBaseMap;
|
||
|
||
half _GlobalAdditionalLightInjectIntoMainLightColor_Strength;
|
||
half _GlobalAdditionalLightInjectIntoMainLightColor_AllowCloseLightOverBright;
|
||
half _GlobalAdditionalLightInjectIntoMainLightColor_Desaturate;
|
||
half _GlobalAdditionalLightInjectIntoMainLightDirection_Strength;
|
||
|
||
// global screen space outline settings
|
||
float _GlobalScreenSpaceOutlineIntensityForChar;
|
||
float _GlobalScreenSpaceOutlineWidthMultiplierForChar;
|
||
float _GlobalScreenSpaceOutlineNormalsSensitivityOffsetForChar;
|
||
float _GlobalScreenSpaceOutlineDepthSensitivityOffsetForChar;
|
||
float _GlobalScreenSpaceOutlineDepthSensitivityDistanceFadeoutStrengthForChar;
|
||
half3 _GlobalScreenSpaceOutlineTintColorForChar;
|
||
|
||
// global screen space outline V2 settings
|
||
float _GlobalScreenSpaceOutlineV2IntensityForChar;
|
||
float _GlobalScreenSpaceOutlineV2WidthMultiplierForChar;
|
||
float _GlobalScreenSpaceOutlineV2EnableGeometryEdgeForChar;
|
||
float _GlobalScreenSpaceOutlineV2GeometryEdgeThresholdForChar;
|
||
float _GlobalScreenSpaceOutlineV2EnableNormalAngleEdgeForChar;
|
||
float _GlobalScreenSpaceOutlineV2NormalAngleCosThetaThresholdForChar;
|
||
|
||
// global utility
|
||
float _GlobalShouldDisableNiloToonDepthTextureRimLightAndShadow;
|
||
|
||
// rendering path related
|
||
float _NiloToonGlobalAllowUnityCameraDepthTextureWriteOutlineExtrudedPosition;
|
||
float _NiloToonGlobalAllowUnityCameraDepthTextureWriteFaceZOffset;
|
||
|
||
// global debug
|
||
float _GlobalToonShadeDebugCase;
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Global Arrays
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Must match: NiloToonSetToonParamPass.k_MaxCharacterCount***** values!
|
||
#if defined(SHADER_API_MOBILE) && (defined(SHADER_API_GLES) || defined(SHADER_API_GLES30))
|
||
#define MAX_CHARACTER_COUNT 16
|
||
#elif defined(SHADER_API_MOBILE) || (defined(SHADER_API_GLCORE) && !defined(SHADER_API_SWITCH)) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3) // Workaround because SHADER_API_GLCORE is also defined when SHADER_API_SWITCH is
|
||
#define MAX_CHARACTER_COUNT 32
|
||
#else
|
||
#define MAX_CHARACTER_COUNT 128
|
||
#endif
|
||
|
||
// For properties that will change every frame, we don't put those properties in CBUFFER due to performance cost,
|
||
// so we use a global array instead (each character will using _CharacterID as index to get the data for that character)
|
||
// this method is similar to how URP get per additional light data from constant size global arrays (256 size light data arrays)
|
||
float3 _NiloToonGlobalPerCharFaceForwardDirWSArray[MAX_CHARACTER_COUNT];
|
||
float3 _NiloToonGlobalPerCharFaceUpwardDirWSArray[MAX_CHARACTER_COUNT];
|
||
float3 _NiloToonGlobalPerCharBoundCenterPosWSArray[MAX_CHARACTER_COUNT];
|
||
float3 _NiloToonGlobalPerCharHeadBonePosWSArray[MAX_CHARACTER_COUNT];
|
||
half3 _NiloToonGlobalPerCharMainDirectionalLightTintColorArray[MAX_CHARACTER_COUNT];
|
||
half3 _NiloToonGlobalPerCharMainDirectionalLightAddColorArray[MAX_CHARACTER_COUNT];
|
||
|
||
// max array size decided by URP's MAX_VISIBLE_LIGHTS,
|
||
// it matches C#'s UniversalRenderPipeline.maxVisibleAdditionalLights
|
||
// 16 (low-end mobile)
|
||
// 32 (mobile | SWITCH)
|
||
// 256 (other)
|
||
#if NeedCalculateAdditionalLight
|
||
half4 _NiloToonGlobalPerUnityLightDataArray[MAX_VISIBLE_LIGHTS];
|
||
half4 _NiloToonGlobalPerUnityLightDataArray2[MAX_VISIBLE_LIGHTS];
|
||
#endif
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Data Structs
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Passing multiple data between functions can be difficult if the param count is too large,
|
||
// so we write some struct to help us organize function calls param
|
||
struct UVData
|
||
{
|
||
// index 0: UV0
|
||
// index 1: UV1
|
||
// index 2: UV2
|
||
// index 3: UV3
|
||
// index 4: MatCapUV
|
||
// index 5: CharBoundUV
|
||
// index 6: ScreenSpaceUV
|
||
float2 allUVs[7];
|
||
|
||
// Performance warning of "dynamic index":
|
||
// dynamic index accessing an array may force the compiler to store the element values in off-chip memory!
|
||
// because you can't do dynamic indexing into registers.
|
||
// if we want the element values store in register for quick read, we need to use compile time constant index.
|
||
// it means a "ifelse chain or switch case that looks stupid" may perform better than a clean code dynamic index array access..!
|
||
// see:
|
||
// - https://developer.nvidia.com/blog/identifying-shader-limiters-with-the-shader-profiler-in-nvidia-nsight-graphics/
|
||
// - https://www.reddit.com/r/opengl/comments/ryig28/struct_switchindexing_with_20_values_faster_than/
|
||
float2 GetUV(uint index)
|
||
{
|
||
// In mobile test (Adreno612,Adreno619,MaliG57MC2):
|
||
// - UVsMethod 0 (constant index) is debug control, it is expected to be the fastest
|
||
// - UVsMethod 1/2 (ifelse chain / switch) is faster than UVsMethod 3
|
||
// - UVsMethod 3 (dynamic index) is the worst (NiloToonSampleScene)
|
||
// -------------------------------------
|
||
// NiloToon 0.16.16 (Method3, control):
|
||
// - low = 60fps (adreno619), 60fps (MaliG57), 25fps(adreno612)
|
||
// - mid = 38fps (adreno619), 60fps (MaliG57), 14fps(adreno612)
|
||
// - high = 25fps (adreno619), 44fps (MaliG57), 9fps(adreno612)
|
||
// - highest = 8fps (adreno619), 15fps (MaliG57), 3fps(adreno612)
|
||
// -------------------------------------
|
||
// NiloToon 0.16.16 (Method2, switch case):
|
||
// - low = 60fps (adreno619), 60fps (MaliG57), 22fps(adreno612)
|
||
// - mid = 35fps (adreno619), 60fps (MaliG57), 13fps(adreno612)
|
||
// - high = 21fps (adreno619), 41fps (MaliG57), 7fps(adreno612)
|
||
// - highest = 7fps (adreno619), 15fps (MaliG57), 3fps(adreno612)
|
||
// -------------------------------------
|
||
// NiloToon 0.16.16 (Method1, ifelse chain):
|
||
// - low = 60fps (adreno619), 60fps (MaliG57), 23fps(adreno612)
|
||
// - mid = 35fps (adreno619), 60fps (MaliG57), 13fps(adreno612)
|
||
// - high = 21fps (adreno619), 47fps (MaliG57), 7fps(adreno612)
|
||
// - highest = 8fps (adreno619), 11fps (MaliG57), 3fps(adreno612)
|
||
// -------------------------------------
|
||
// NiloToon 0.16.16 (Method0, dynamic index):
|
||
// - low = 60fps (adreno619), 60fps (MaliG57), 22fps(adreno612)
|
||
// - mid = 33fps (adreno619), 60fps (MaliG57), 12fps(adreno612)
|
||
// - high = 17fps (adreno619), 46fps (MaliG57), 6fps(adreno612)
|
||
// - highest = 6fps (adreno619), 10fps (MaliG57), 3fps(adreno612)
|
||
#define UVsMethod 1
|
||
|
||
#if UVsMethod == 0
|
||
// dynamic index array access, clean code but slower
|
||
return allUVs[index];
|
||
#elif UVsMethod == 1
|
||
// ifelse chain
|
||
if(index == 0) return allUVs[0];
|
||
if(index == 1) return allUVs[1];
|
||
if(index == 2) return allUVs[2];
|
||
if(index == 3) return allUVs[3];
|
||
if(index == 4) return allUVs[4];
|
||
if(index == 5) return allUVs[5];
|
||
if(index == 6) return allUVs[6];
|
||
return 0;
|
||
#elif UVsMethod == 2
|
||
// switch case
|
||
switch (index)
|
||
{
|
||
case 0: return allUVs[0];
|
||
case 1: return allUVs[1];
|
||
case 2: return allUVs[2];
|
||
case 3: return allUVs[3];
|
||
case 4: return allUVs[4];
|
||
case 5: return allUVs[5];
|
||
case 6: return allUVs[6];
|
||
}
|
||
return 0;
|
||
#elif UVsMethod == 3
|
||
// constant index array access (debug control)
|
||
return allUVs[0];
|
||
#endif
|
||
}
|
||
};
|
||
// this struct is similar to URP's SurfaceData.hlsl -> "SurfaceData" struct
|
||
// Note: don't name it "SurfaceData", because it will conflict with URP's own "SurfaceData" struct in URP's SurfaceData.hlsl
|
||
struct ToonSurfaceData
|
||
{
|
||
half3 albedo;
|
||
half alpha;
|
||
half3 emission;
|
||
half occlusion;
|
||
half3 specular;
|
||
half specularMask; // store user's remapped specular mask texture, for ramp specular
|
||
half3 normalTS;
|
||
half smoothness;
|
||
};
|
||
// this struct is similar to URP's Input.hlsl -> "InputData" struct
|
||
// any useful data for lighting will be put into this struct
|
||
struct ToonLightingData
|
||
{
|
||
float2 uv;
|
||
|
||
half3 normalWS;
|
||
half3 normalWS_NoNormalMap;
|
||
float3 positionWS;
|
||
half3 viewDirectionWS;
|
||
float selfLinearEyeDepth;
|
||
half averageShadowAttenuation;
|
||
half3 SH;
|
||
half isFaceArea;
|
||
half isSkinArea;
|
||
float2 SV_POSITIONxy;
|
||
half3 normalVS;
|
||
half3 reflectionVectorWS;
|
||
half NdotV;
|
||
half NdotV_NoNormalMap;
|
||
half rawURPShadowAttenuation;
|
||
|
||
#if VaryingsHasTangentWS
|
||
half3x3 TBN_WS;
|
||
half3 viewDirectionTS;
|
||
#endif
|
||
|
||
half4 vertexColor;
|
||
|
||
float ZOffsetFinalSum;
|
||
|
||
float2 normalizedScreenSpaceUV;
|
||
float2 aspectCorrectedScreenSpaceUV;
|
||
|
||
float facing;
|
||
|
||
float3 characterBoundBottomWS_positionWS;
|
||
float3 characterBoundTopWS_positionWS;
|
||
|
||
float2 characterBound2DRectUV01;
|
||
|
||
float2 matcapUV;
|
||
|
||
Light mainLight;
|
||
};
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Shared functions across .hlsl(s)
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// optimized function to get the UV0 (fastest)
|
||
float2 GetUV(Varyings varyings)
|
||
{
|
||
return varyings.uv01.xy;
|
||
}
|
||
// generic function to get any UV (fast)
|
||
float2 GetUV(Varyings varyings, uint index)
|
||
{
|
||
if(index == 0) return varyings.uv01.xy;
|
||
if(index == 1) return varyings.uv01.zw;
|
||
if(index == 2) return varyings.uv23.xy;
|
||
if(index == 3) return varyings.uv23.zw;
|
||
|
||
return 0;
|
||
/*
|
||
// same as the above code, but slower due to array assign and read
|
||
float2 uvArray[4];
|
||
uvArray[0] = varyings.uv01.xy;
|
||
uvArray[1] = varyings.uv01.zw;
|
||
uvArray[2] = varyings.uv23.xy;
|
||
uvArray[3] = varyings.uv23.zw;
|
||
|
||
|
||
return uvArray[index];
|
||
*/
|
||
}
|
||
|
||
// complex function to get a final uv (slow)
|
||
float2 CalcUV(UVData uvData, uint index, float4 scaleOffset, float2 scrollSpeed)
|
||
{
|
||
return CalcUV(uvData.GetUV(index), scaleOffset, _Time.y, scrollSpeed);
|
||
}
|
||
|
||
// Full complex function to get a final uv(slowest)
|
||
float2 CalcUV(UVData uvData, uint index, float4 scaleOffset, float2 scrollSpeed, float rotatedAngle, float rotateSpeed)
|
||
{
|
||
return CalcUV(uvData.GetUV(index), scaleOffset, _Time.y, scrollSpeed, rotatedAngle, rotateSpeed);
|
||
}
|
||
|
||
half4 GetCombinedBaseColor()
|
||
{
|
||
half4 combinedBaseColor = _BaseColor * _BaseColor2;
|
||
combinedBaseColor *= _MultiplyBRPColor ? _Color : 1;
|
||
|
||
return combinedBaseColor;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// #include .hlsl
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// all lighting equations will be inside this .hlsl,
|
||
// just by editing this .hlsl can control most of the visual result.
|
||
#include "NiloToonCharacter_LightingEquation.hlsl"
|
||
|
||
// all NiloToon supported ExternalAsset extension will go here
|
||
#include "NiloToonCharacter_ExtendDefinesForExternalAsset.hlsl"
|
||
#include "NiloToonCharacter_ExtendFunctionsForExternalAsset.hlsl"
|
||
|
||
// if you want to extend this shader without future update merge conflict, you should edit this .hlsl
|
||
// it is made for NiloToonURP's user to extend more global features by themselves in an isolated .hlsl file
|
||
#include "NiloToonCharacter_ExtendFunctionsForUserCustomLogic.hlsl"
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// vertex shared functions
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// Return: skin area
|
||
// Skin area is mainly for shadow color overriding to a skin shadow color.
|
||
half GetSkinArea(float2 uv)
|
||
{
|
||
half isSkinArea = _IsSkin;
|
||
|
||
#if _SKIN_MASK_ON
|
||
half skinMask = ExtractSingleChannel(SAMPLE_TEXTURE2D(_SkinMaskMap, sampler_BaseMap, uv), _SkinMaskMapChannelMask);// reuse sampler_BaseMap to save sampler count
|
||
skinMask = PostProcessMaskValue(skinMask, _SkinMaskMapAsIDMap, _SkinMaskMapExtractFromID, _SkinMaskMapInvertColor, _SkinMaskMapRemapStart, _SkinMaskMapRemapEnd);
|
||
isSkinArea *= skinMask;
|
||
#endif
|
||
|
||
return isSkinArea;
|
||
}
|
||
half GetFaceArea(float2 uv)
|
||
{
|
||
half isFaceArea = 0;
|
||
|
||
#if _ISFACE
|
||
#if _FACE_MASK_ON
|
||
// if enabled face mask texture, we treat only white area on _FaceMaskMap is face
|
||
isFaceArea = dot(tex2Dlod(_FaceMaskMap, float4(uv,0,0)), _FaceMaskMapChannelMask);
|
||
isFaceArea = _FaceMaskMapInvertColor? 1-isFaceArea : isFaceArea;
|
||
isFaceArea = invLerpClamp(_FaceMaskMapRemapStart,_FaceMaskMapRemapEnd,isFaceArea);
|
||
#else
|
||
// if no face mask, we assume the whole material is face when _ISFACE is on
|
||
isFaceArea = 1;
|
||
#endif
|
||
#endif
|
||
|
||
return isFaceArea;
|
||
}
|
||
// output:
|
||
// .xyz = lightingNormalWS (with face normal edit applied)
|
||
// .w = isFaceArea
|
||
half4 GetLightingNormalWS_FaceArea(half3 normalWS, float3 positionWS, float2 uv)
|
||
{
|
||
// default use original normalWS
|
||
half3 resultNormalWS = normalWS;
|
||
|
||
half isFaceArea = GetFaceArea(uv);
|
||
|
||
// disabled NiloToonDepthOnlyOrDepthNormalPass's face normal edit, so the normal in _CameraNormalTexture store the real normal
|
||
// in case any postprocess effect uses normal on face's pixels
|
||
#if _ISFACE && !NiloToonDepthOnlyOrDepthNormalPass
|
||
// [apply face normal edit]
|
||
|
||
// methodA: by forcing face normal becoming face's forward direction vector
|
||
half3 flatDirectionFixFaceNormalWS = _NiloToonGlobalPerCharFaceForwardDirWSArray[_CharacterID];
|
||
|
||
// methodB: by forcing face normal becoming sphere proxy normal, which use _HeadBonePositionWS as proxy sphere center
|
||
half3 proxySphereFixFaceNormalWS = positionWS - _NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID];
|
||
|
||
// let user select methodA or methodB
|
||
half3 fixedFaceNormalWS = lerp(flatDirectionFixFaceNormalWS,proxySphereFixFaceNormalWS,_FixFaceNormalUseFlattenOrProxySphereMethod);
|
||
|
||
//fixedFaceNormalWS = proxySphereFixFaceNormalWS; //TODO: this line better for face area additional light
|
||
|
||
resultNormalWS = normalize(lerp(resultNormalWS, fixedFaceNormalWS, _FixFaceNormalAmount * _FixFaceNormalAmountPerMaterial * isFaceArea * _ControlledByNiloToonPerCharacterRenderController)); // only normalize() once at final to improve performance
|
||
#endif
|
||
|
||
return half4(resultNormalWS,isFaceArea);
|
||
}
|
||
bool IsSmoothedNormalTSAvailableInMeshUV8(Attributes input)
|
||
{
|
||
// checks to confirm if uv8 is a correct smoothed normal:
|
||
// 1) is uv8 a unit vector?
|
||
// 2) uv8.xy should never equals to uv4.xy if uv8 is a correctly baked smoothed normal vector.
|
||
// 3) if uv8.z is 0, it is not a baked smooth normal (vrm character do not bake smoothed normal)
|
||
return (abs(dot(input.uv8,input.uv8)-1.0) < (1.0/255.0)) &&
|
||
all(input.uv8.xy != input.uv4.xy) &&
|
||
(input.uv8.z != 0)
|
||
;
|
||
}
|
||
bool ShouldOutlineUseBakedSmoothNormal(bool isSmoothedNormalAvailableInMeshUV8)
|
||
{
|
||
// apply material setting by user
|
||
return isSmoothedNormalAvailableInMeshUV8 && _OutlineUseBakedSmoothNormal;
|
||
}
|
||
float3 GetPossibleBakedSmoothedNormalWS(VertexNormalInputs vertexNormalInputs, bool isSmoothedNormalTSAvailableInMeshUV8, float3 smoothedNormalTS)
|
||
{
|
||
// by default we will use lighting normalWS(regular vertex normal) as extrude direction,
|
||
// it is usable but not perfect due to smoothing group's split normal
|
||
// which will produce discontinue outline for hard edge polygons(e.g. a cube's corner/edge, hair's end vertex)
|
||
float3 extrudeDirectionWS = vertexNormalInputs.normalWS;
|
||
|
||
// "world space smoothed normal" is a much better extrude direction for outline than simply using lighting normal(regular vertex normal),
|
||
// because smoothed normal doesn't have any split normal, so the outline will always be continuous(even on a cube), which looks much better.
|
||
// If we baked "tangent space smoothed normal" in model's uv8,
|
||
// we can convert it back to world space here and use it as a much better extrude direction for outline
|
||
if(isSmoothedNormalTSAvailableInMeshUV8)
|
||
{
|
||
extrudeDirectionWS = ConvertNormalTSToNormalTargetSpace(smoothedNormalTS, vertexNormalInputs.tangentWS,vertexNormalInputs.bitangentWS, vertexNormalInputs.normalWS);
|
||
}
|
||
|
||
return extrudeDirectionWS;
|
||
}
|
||
float3 TransformPositionWSToOutlinePositionWS(float width, VertexPositionInputs vertexPositionInputs, float3 extrudeDirectionWS)
|
||
{
|
||
width *= GetOutlineCameraFovAndDistanceFixMultiplier(vertexPositionInputs.positionVS.z, _CurrentCameraFOV, _GlobalOutlineWidthAutoAdjustToCameraDistanceAndFOV * _OutlineApplyAutoWidthAdjustment);
|
||
|
||
// [normalize length in view space]
|
||
if(_OutlineUniformLengthInViewSpace)
|
||
{
|
||
float3 extrudeDirectionVS = mul(UNITY_MATRIX_V,float4(extrudeDirectionWS,0)).xyz;
|
||
extrudeDirectionVS.z = 0;
|
||
extrudeDirectionVS = normalize(extrudeDirectionVS);
|
||
extrudeDirectionWS = mul(UNITY_MATRIX_I_V, float4(extrudeDirectionVS,0)).xyz;
|
||
}
|
||
|
||
// [this part is optional]
|
||
// you can make extrude direction normalized in screen space, which will produce screen space constant width outline
|
||
// https://www.videopoetics.com/tutorials/pixel-perfect-outline-shaders-unity/
|
||
// https://github.com/Santarh/MToon/blob/master/MToon/Resources/Shaders/MToonCore.cginc#L90
|
||
// TODO: do we need to support this option?
|
||
// ...
|
||
|
||
// [normalize in NDC]
|
||
// this part is wrong, just experimental code
|
||
/*
|
||
float3 extrudeDirectionNDC = mul(UNITY_MATRIX_VP,float4(extrudeDirectionWS,0));
|
||
extrudeDirectionNDC.z = 0;
|
||
extrudeDirectionNDC = normalize(extrudeDirectionNDC) * 12;
|
||
extrudeDirectionWS = mul(UNITY_MATRIX_I_VP, float4(extrudeDirectionNDC,0));
|
||
*/
|
||
|
||
// produce and return outline vertex position in world space
|
||
float3 outlinePositionWS = vertexPositionInputs.positionWS + extrudeDirectionWS * width;
|
||
return outlinePositionWS;
|
||
}
|
||
float ShouldRenderOutline()
|
||
{
|
||
return _RenderOutline && _PerCharacterRenderOutline;
|
||
}
|
||
|
||
bool ShouldDisableRendering()
|
||
{
|
||
// user can disable rendering:
|
||
// - per material
|
||
// - per NiloToonChar script
|
||
bool shouldDisableRendering = !(_EnableRendering && _RenderCharacter);
|
||
|
||
// when _DitherFadeoutAmount or _DissolveAmount is 100% (character is completely not visible due to NiloToonPerCharacterRenderController's dither or dissolve setting),
|
||
// discard all rendering in this vertex shader so we don't pollute URP's shadowmap and depth texture / depth normal texture
|
||
// it also helps improving performance because of skipping the fragment shader
|
||
shouldDisableRendering = shouldDisableRendering ||
|
||
(_AllowPerCharacterDitherFadeout && (_DitherFadeoutAmount >= 1.0)) ||
|
||
(_AllowPerCharacterDissolve && (_DissolveAmount >= 1.0));
|
||
|
||
// when rendering to _CameraColorTexture, this section will allow material's _RenderOutline toggle or per character script's _PerCharacterRenderOutline to control render outline or not, per material.
|
||
// Be careful not to run this section in DepthOnly/DepthNormalsOnly pass,
|
||
// in DepthOnly pass we only need to skip the outline position extrude, not invalid the vertex!
|
||
// so here only #if NiloToonSelfOutlinePass is used instead of #if NiloToonIsAnyOutlinePass.
|
||
#if NiloToonSelfOutlinePass
|
||
shouldDisableRendering = shouldDisableRendering || !ShouldRenderOutline();
|
||
#endif
|
||
|
||
// Material's "Pass On/Off" section
|
||
bool shouldRenderingExtraThickOutline = _AllowRenderExtraThickOutlinePass && _ExtraThickOutlineEnabled;
|
||
bool shouldRenderingCharacterAreaColorFill = _AllowRenderNiloToonCharacterAreaColorFillPass && _CharacterAreaColorFillEnabled;
|
||
bool shouldRenderCharacterAreaStencilBufferFill = (shouldRenderingExtraThickOutline || shouldRenderingCharacterAreaColorFill) && _AllowRenderNiloToonCharacterAreaStencilBufferFillPass;
|
||
|
||
#if NiloToonCharacterAreaStencilBufferFillPass
|
||
shouldDisableRendering = shouldDisableRendering || !shouldRenderCharacterAreaStencilBufferFill;
|
||
#endif
|
||
#if NiloToonExtraThickOutlinePass
|
||
shouldDisableRendering = shouldDisableRendering || !shouldRenderingExtraThickOutline;
|
||
#endif
|
||
#if NiloToonCharacterAreaColorFillPass
|
||
shouldDisableRendering = shouldDisableRendering || !shouldRenderingCharacterAreaColorFill;
|
||
#endif
|
||
#if NiloToonShadowCasterPass
|
||
shouldDisableRendering = shouldDisableRendering || !_AllowRenderURPShadowCasterPass;
|
||
#endif
|
||
#if NiloToonDepthOnlyOrDepthNormalPass
|
||
shouldDisableRendering = shouldDisableRendering || !_AllowRenderDepthOnlyOrDepthNormalsPass;
|
||
#endif
|
||
#if NiloToonCharSelfShadowCasterPass
|
||
shouldDisableRendering = shouldDisableRendering || !_AllowRenderNiloToonSelfShadowPass;
|
||
#endif
|
||
|
||
return shouldDisableRendering;
|
||
}
|
||
// [All pass's vertex shader will share this function]
|
||
// - if "NiloToonIsAnyOutlinePass" is not defined = do regular MVP transform
|
||
// - if "NiloToonIsAnyOutlinePass" is defined = do regular MVP transform + outline extrude vertex + all outline related task
|
||
Varyings VertexShaderAllWork(Attributes input)
|
||
{
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// init Varyings struct
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// init output struct with all 0 bits just to avoid "struct not init" warning/error
|
||
// but even passing 0 from vertex to fragment via Varying struct still has Rasterisation/interpolation cost
|
||
// so make sure Varyings struct is as small as possible by using {#if #endif} to remove unneeded things inside Varying struct
|
||
Varyings output = (Varyings)0;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// disable rendering (early exit)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// if we should not render this material,
|
||
// execute "disable rendering"
|
||
if(ShouldDisableRendering())
|
||
{
|
||
// see [a trick to "delete" any vertex] below to understand what this line does
|
||
output.positionCS.w = 0;
|
||
return output;
|
||
|
||
//---------------------------------------------------------------------------------------------------------------------------
|
||
// [a trick to "delete" any vertex]
|
||
// https://forum.unity.com/threads/ignoring-some-triangles-in-a-vertex-shader.170834/#post-5327751
|
||
|
||
// if the output vertex's positionCS.w is NaN, GPU will invalid this vertex and all directly connected vertices(Degenerate triangles),
|
||
// we can use "positionCS.w = NaN" to invalid any vertices if needed.
|
||
|
||
// 1.this section is a correct and safe implementation, 0.0/0.0 is NaN, which can invalid any target vertex, but will produce "divide by zero" warning
|
||
// {
|
||
// output.positionCS.w = 0.0/0.0; //0.0/0.0 is NaN
|
||
// return output;
|
||
// }
|
||
|
||
// 2.this section is a correct and safe implementation, _NaN is NaN from C#, which can invalid any target vertex, and will NOT produce "divide by zero" warning
|
||
// {
|
||
// // C# -> Shader.SetGlobalFloat("_NaN", System.Single.NaN);
|
||
// output.positionCS.w = _NaN;
|
||
// return output;
|
||
// }
|
||
|
||
// 3.this section is a correct and safe implementation, asfloat(0x7fc00000) is NaN, which can invalid any target vertex, and will NOT produce "divide by zero" warning
|
||
// {
|
||
// output.positionCS = asfloat(0x7fc00000);
|
||
// return output;
|
||
// }
|
||
|
||
// 4.this section will work only if you are discarding the whole mesh, but the nice thing is, it will NOT produce any warning, so we choose this for discarding the whole mesh.
|
||
// {
|
||
// output.positionCS.w = 0; // it is 0 already even without writing this line, since Varyings struct was init with all 0 bits, but we still write this line for clarity
|
||
// return output;
|
||
// }
|
||
//---------------------------------------------------------------------------------------------------------------------------
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// after invalid/discard vertex, do this part asap.
|
||
// to support GPU instancing and Single Pass Stereo rendering(VR), add the following section
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
UNITY_SETUP_INSTANCE_ID(input); // will turn into this in non OpenGL and non PSSL -> UnitySetupInstanceID(input.instanceID);
|
||
|
||
// see URP's ShadowCasterPass.hlsl for why these lines are removed for NiloToonCharSelfShadowCasterPass
|
||
#if !NiloToonCharSelfShadowCasterPass
|
||
UNITY_TRANSFER_INSTANCE_ID(input, output); // will turn into this in non OpenGL and non PSSL -> output.instanceID = input.instanceID;
|
||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); // will turn into this in non OpenGL and non PSSL -> output.stereoTargetEyeIndexAsRTArrayIdx = unity_StereoEyeIndex;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// UV
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// TRANSFORM_TEX is the same as the old shader library.
|
||
|
||
// is it correct to apply the same _BaseMap_ST uv change to all UVs? maybe not!
|
||
// we should not affect all uv0-uv3 by _BaseMap's tiling offset, it maybe is a wrong design
|
||
// but removing it is a big breaking change, so let's keep it unchanged, and add a "UV Control" group as the alternative control
|
||
output.uv01.xy = TRANSFORM_TEX(input.uv ,_BaseMap);
|
||
output.uv01.zw = TRANSFORM_TEX(input.uv2,_BaseMap);
|
||
output.uv23.xy = TRANSFORM_TEX(input.uv3,_BaseMap);
|
||
output.uv23.zw = TRANSFORM_TEX(input.uv4,_BaseMap);
|
||
|
||
if(_EnableUVEditGroup)
|
||
{
|
||
output.uv01.xy = CalcUV(output.uv01.xy, _UV0ScaleOffset, _UV0CenterPivotScalePos, _Time.y, _UV0ScrollSpeed, _UV0RotatedAngle, _UV0RotateSpeed);
|
||
output.uv01.zw = CalcUV(output.uv01.zw, _UV1ScaleOffset, _UV1CenterPivotScalePos, _Time.y, _UV1ScrollSpeed, _UV1RotatedAngle, _UV1RotateSpeed);
|
||
output.uv23.xy = CalcUV(output.uv23.xy, _UV2ScaleOffset, _UV2CenterPivotScalePos, _Time.y, _UV2ScrollSpeed, _UV2RotatedAngle, _UV2RotateSpeed);
|
||
output.uv23.zw = CalcUV(output.uv23.zw, _UV3ScaleOffset, _UV3CenterPivotScalePos, _Time.y, _UV3ScrollSpeed, _UV3RotatedAngle, _UV3RotateSpeed);
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// insert a performance debug early exit
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// exit as early as possible, to maximize performance difference, which can be used to estimate how expensive this shader is on target device.
|
||
#if _NILOTOON_FORCE_MINIMUM_SHADER
|
||
#if NiloToonIsAnyOutlinePass
|
||
output.positionCS = TransformObjectToHClip(input.positionOS + input.normalOS * 0.005); // any visible debug outline width is ok
|
||
#else
|
||
output.positionCS = TransformObjectToHClip(input.positionOS);
|
||
#endif
|
||
|
||
return output;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Edit Attributes struct by other .hlsl
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// allow NiloToon's developer to support external AssetStore/GitHub assets by editing Attributes struct,
|
||
// using NiloToonCharacter_ExtendFunctionsForExternalAsset.hlsl (used by NiloToon's developer)
|
||
ApplyExternalAssetSupportLogicToVertexAttributeAtVertexShaderStart(input);
|
||
|
||
// allow NiloToon's user to edit Attributes struct,
|
||
// using NiloToonCharacter_ExtendFunctionsForUserCustomLogic.hlsl (used by NiloToon's user)
|
||
ApplyCustomUserLogicToVertexAttributeAtVertexShaderStart(input);
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Fill in VertexPositionInputs and VertexNormalInputs utility struct
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// VertexPositionInputs struct contains the position in multiple spaces (world, view, homogeneous clip space, NDC)
|
||
// Unity compiler will strip all unused references (say you don't use view space).
|
||
// Therefore there is more flexibility at no additional cost when using VertexPositionInputs struct.
|
||
VertexPositionInputs vertexPositionInput = GetVertexPositionInputs(input.positionOS);
|
||
|
||
// Similar to VertexPositionInputs, VertexNormalInputs will contain normal, tangent and bitangent
|
||
// in world space. If not used it will be stripped.
|
||
// NormalWS and tangentWS are already normalized,
|
||
// this is required to avoid skewing the direction during interpolation
|
||
// also required for per-vertex lighting and SH evaluation if needed
|
||
VertexNormalInputs vertexNormalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
|
||
|
||
float3 positionWS = vertexPositionInput.positionWS;
|
||
float3 positionVS = vertexPositionInput.positionVS;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Smoothed normal WS cache
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
bool isSmoothedNormalTSAvailableInMeshUV8 = IsSmoothedNormalTSAvailableInMeshUV8(input);
|
||
|
||
float3 possibleSmoothedNormalWS = GetPossibleBakedSmoothedNormalWS(vertexNormalInput, isSmoothedNormalTSAvailableInMeshUV8, input.uv8.xyz); // mesh's uv8 is smoothedNormalTS if baked by NiloToon
|
||
output.smoothedNormalWS = possibleSmoothedNormalWS;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// ShouldOutlineUseBakedSmoothNormal cache
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
bool shouldOutlineUseBakedSmoothNormal = ShouldOutlineUseBakedSmoothNormal(isSmoothedNormalTSAvailableInMeshUV8);
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Fog
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#if NiloToonIsAnyLitColorPass
|
||
// we must calculate fogFactor before any positionCS.z's edit (e.g. ZOffset edit)
|
||
const half fogFactor = ComputeFogFactor(vertexPositionInput.positionCS.z);
|
||
output.SH_fogFactor.w = fogFactor;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Extrude positionWS for outline
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// extrude positionWS if this pass is any outline pass and this material wants to render outline
|
||
#if NiloToonIsAnyOutlinePass
|
||
// Inside NiloToonIsAnyOutlinePass, there are 5 passes.
|
||
|
||
// if ShouldRenderOutline() is false,
|
||
// - NiloToonSelfOutlinePass already early exit (see "discard rendering (early exit)" in the above section)
|
||
// - ExtraThickOutlinePass will ignore ShouldRenderOutline()
|
||
// so only:
|
||
// - NiloToonDepthOnlyOrDepthNormalPass || NiloToonPrepassBufferPass || NiloToonCharacterAreaStencilBufferFillPass
|
||
// will need to include the following if() section,
|
||
// these passes should be always in sync, since NiloToonPrepassBuffer pass rely on _CameraDepthTexture (DepthOnly | DepthNormalsOnly)
|
||
// else the depth comparision test in NiloToonPrepassBuffer pass will be incorrect.
|
||
// * NiloToonCharacterAreaStencilBufferFillPass should still be included here, but does not care any depth texture related checks
|
||
#if NiloToonDepthOnlyOrDepthNormalPass || NiloToonPrepassBufferPass || NiloToonCharacterAreaStencilBufferFillPass
|
||
// In this section, "Character non-color passes" means these passes:
|
||
// - DepthOnly | DepthNormalsOnly
|
||
// - NiloToonPrepassBuffer
|
||
// - NiloToonCharacterAreaStencilBufferFill
|
||
|
||
// 1)If ShouldRenderOutline() is false,
|
||
// no need to extrude in "Character non-color passes", simple to understand.
|
||
|
||
// 2)If shouldOutlineUseBakedSmoothNormal is false,
|
||
// which means we are trying to use split normal for extrude.
|
||
// Due to smoothing group's regular split normal, TransformPositionWSToOutlinePositionWS() may produce holes between polygons after extrude,
|
||
// which pollute "Character non-color passes" (e.g., pollute _CameraDepthTexture, which makes depth texture has holes, lead to 2D rim light producing bad result).
|
||
// so when the extrude normal is split normal, it is better NOT doing TransformPositionWSToOutlinePositionWS() if shouldOutlineUseBakedSmoothNormal is false for these "Character non-color passes"
|
||
|
||
// 3)If per material
|
||
// - _UnityCameraDepthTextureWriteOutlineExtrudedPosition
|
||
// is false (default is false),
|
||
// which means user want to skip this section, usually user will keep turn off _UnityCameraDepthTextureWriteOutlineExtrudedPosition in material to avoid ugly 2D rim light artifact appeared
|
||
|
||
// 4)If global uniform
|
||
// - _NiloToonGlobalAllowUnityCameraDepthTextureWriteOutlineExtrudedPosition
|
||
// is false, it means we are in deferred rendering mode, where true depth texture write is a must.
|
||
// we have no choice but to write the true depth without any extrude
|
||
if( ShouldRenderOutline()
|
||
#if !NiloToonCharacterAreaStencilBufferFillPass
|
||
&& shouldOutlineUseBakedSmoothNormal
|
||
&& false // _UnityCameraDepthTextureWriteOutlineExtrudedPosition, removed since NiloToon 0.17.10, not allow to use anymore
|
||
&& _NiloToonGlobalAllowUnityCameraDepthTextureWriteOutlineExtrudedPosition
|
||
#endif
|
||
)
|
||
#endif
|
||
{
|
||
float finalOutlineWidth = _OutlineWidth * _OutlineWidthExtraMultiplier * _PerCharacterOutlineWidthMultiply * _GlobalOutlineWidthMultiplier;
|
||
|
||
// outline width mul from texture
|
||
#if _OUTLINEWIDTHMAP
|
||
float outlineWidthTex2DlodExplicitMipLevel = 0;
|
||
float4 outlineWidthTexReadValueRGBA = tex2Dlod(_OutlineWidthTex, float4(input.uv,0,outlineWidthTex2DlodExplicitMipLevel));
|
||
float outlineWidthMultiplierByTex = dot(outlineWidthTexReadValueRGBA,_OutlineWidthTexChannelMask);
|
||
finalOutlineWidth *= outlineWidthMultiplierByTex;
|
||
#endif
|
||
|
||
// outline width mul from vertex color
|
||
finalOutlineWidth = _UseOutlineWidthMaskFromVertexColor? finalOutlineWidth * dot(input.color, _OutlineWidthMaskFromVertexColor) : finalOutlineWidth;
|
||
|
||
// ExtraThickOutlinePass will affect finalOutlineWidth at last, to ensure width control is isolated
|
||
#if NiloToonExtraThickOutlinePass || NiloToonDepthOnlyOrDepthNormalPass
|
||
|
||
#if NiloToonDepthOnlyOrDepthNormalPass
|
||
if(_ExtraThickOutlineEnabled && _ExtraThickOutlineZWrite && _ExtraThickOutlineWriteIntoDepthTexture)
|
||
#endif
|
||
{
|
||
// apply by add, not mul, to make extra thick outline's width has it's own isolated width
|
||
finalOutlineWidth += _ExtraThickOutlineWidth;
|
||
// if user used different outline width per material,
|
||
// here we expose min(x,_ExtraThickOutlineMaxFinalWidth) for user to have a uniform final ExtraThickOutline width
|
||
finalOutlineWidth = min(finalOutlineWidth, _ExtraThickOutlineMaxFinalWidth);
|
||
}
|
||
#endif
|
||
|
||
if(_GlobalShouldDisableNiloToonZOffset)
|
||
{
|
||
// in planar reflection pass, _GlobalShouldDisableNiloToonZOffset will set to true (= ZOffset is disabled),
|
||
// so we don't render face's outline, else ugly outline will appear in planar reflection pass
|
||
#if _ISFACE
|
||
// see [a trick to "delete" any vertex] about this section's logic
|
||
output.positionCS.w = 0;
|
||
return output;
|
||
#endif
|
||
|
||
// https://docs.unity3d.com/ScriptReference/Rendering.CullMode.html
|
||
// 0 is Off
|
||
// 1 is Front
|
||
// 2 is Back
|
||
// in planar reflection pass, _GlobalShouldDisableNiloToonZOffset will set to true (= ZOffset is disabled),
|
||
// so don't render Cull off material's outline
|
||
// only Cull Off has problem, so only disable outline when Cull Off
|
||
finalOutlineWidth = _Cull == 0 ? 0 : finalOutlineWidth;
|
||
}
|
||
|
||
// do positionWS extrude for outline
|
||
positionWS = TransformPositionWSToOutlinePositionWS(finalOutlineWidth, vertexPositionInput, shouldOutlineUseBakedSmoothNormal ? possibleSmoothedNormalWS : vertexNormalInput.normalWS);
|
||
|
||
// do ExtraThickOutline's view space pos offset(in world space)
|
||
#if NiloToonExtraThickOutlinePass
|
||
// transform view space position offset to world space, apply offset in world space
|
||
positionWS += mul((float3x3)UNITY_MATRIX_I_V, _ExtraThickOutlineViewSpacePosOffset).xyz;
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
#if _NILOTOON_DEBUG_SHADING
|
||
output.uv8 = input.uv8; // for showing tangent space smoothed normal as debug color, in fragment shader
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// PositionWS
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
output.positionWS_ZOffsetFinalSum.xyz = positionWS;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// TangentWS
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#if VaryingsHasTangentWS
|
||
real sign = input.tangentOS.w * GetOddNegativeScale();
|
||
output.tangentWS = half4(vertexNormalInput.tangentWS.xyz, sign);
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// PositionCS
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// we can't reuse vertexPositionInput.positionCS directly, because positionWS maybe edited in "Extrude positionWS for outline" section,
|
||
// hence we will need to recompute positionCS using TransformWorldToHClip() to ensure positionCS's correctness
|
||
|
||
#if !NiloToonCharSelfShadowCasterPass
|
||
output.positionCS = TransformWorldToHClip(positionWS);
|
||
#else
|
||
// in XR, VP matrix will be overridden by URP, you can't edit it via cmd.SetViewProjectionMatrices, so we need to use our own VP matrix
|
||
output.positionCS = mul(_NiloToonSelfShadowWorldToClip, float4(positionWS,1));
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// output normalWS
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
output.normalWS_averageShadowAttenuation.xyz = vertexNormalInput.normalWS;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// faceArea
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
half faceArea = GetFaceArea(GetUV(output));
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// viewDirTS
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// note: put this section after all require vectors by GetViewDirectionTangentSpace() are ready
|
||
#if VaryingsHasViewDirTS
|
||
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexPositionInput.positionWS);
|
||
half3 viewDirTS = GetViewDirectionTangentSpace(output.tangentWS, output.normalWS_averageShadowAttenuation.xyz, viewDirWS);
|
||
output.viewDirTS = viewDirTS;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// ZOffset sum init
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
float ZOffsetFinalSum = 0;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Outline ZOffset
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// this section will apply clip space ZOffset (view space unit input) to outline position
|
||
// doing this can hide ugly/unwanted outline, usually we will apply Outline ZOffset for face/eye
|
||
// Only apply to _CameraColorTexture's outline pass(NiloToonSelfOutlinePass), don't apply to any depth texture pass
|
||
#if NiloToonSelfOutlinePass
|
||
// we have separated settings for "face" and "not face" vertices
|
||
float outlineZOffset = lerp(_OutlineZOffset,_OutlineZOffsetForFaceArea,faceArea);
|
||
|
||
// [ZOffset mask from vertex color]
|
||
if(_UseOutlineZOffsetMaskFromVertexColor)
|
||
{
|
||
float zoffsetFromVertexColor = dot(input.color, _OutlineZOffsetMaskFromVertexColor);
|
||
zoffsetFromVertexColor = _OutlineZOffsetMaskTexFromVertexColorInvertColor ? 1 - zoffsetFromVertexColor : zoffsetFromVertexColor;
|
||
|
||
outlineZOffset *= zoffsetFromVertexColor;
|
||
}
|
||
|
||
// [ZOffset mask texture]
|
||
#if _OUTLINEZOFFSETMAP
|
||
float outlineZOffsetMask = 1;
|
||
|
||
// [Read ZOffset mask texture]
|
||
// we can't use tex2D() in vertex shader because ddx & ddy is unknown before rasterization,
|
||
// so use tex2Dlod() with an explicit mip level 0, you need to put explicit mip level 0 inside the 4th component of tex2Dlod()'s' input param
|
||
float outlineZOffsetTex2DlodExplicitMipLevel = 0;
|
||
outlineZOffsetMask = dot(tex2Dlod(_OutlineZOffsetMaskTex, float4(input.uv,0,outlineZOffsetTex2DlodExplicitMipLevel)),_OutlineZOffsetMaskTexChannelMask);
|
||
|
||
// [Remap ZOffset texture value]
|
||
// flip texture read value so default black area = apply ZOffset, because usually outline mask texture are using this format(black = hide outline, white = do nothing)
|
||
outlineZOffsetMask = 1 - outlineZOffsetMask;
|
||
// [allow user to invert again if needed]
|
||
outlineZOffsetMask = _OutlineZOffsetMaskTexInvertColor ? 1-outlineZOffsetMask : outlineZOffsetMask;
|
||
outlineZOffsetMask = invLerpClamp(_OutlineZOffsetMaskRemapStart,_OutlineZOffsetMaskRemapEnd,outlineZOffsetMask);// allow user to remap
|
||
|
||
// [Apply ZOffset, Use remapped value as ZOffset mask]
|
||
outlineZOffset *= outlineZOffsetMask;
|
||
#endif
|
||
|
||
outlineZOffset += _OutlineBaseZOffset;
|
||
|
||
// this line make ZOffset sync with camera distance corrected outline width
|
||
// If we don't do this, once camera is far away, zoffset will become not enough because outline width keep growing larger
|
||
// also stop reduce zoffset when camera is too close using max(1,x)
|
||
// TODO: we should share the GetOutlineCameraFovAndDistanceFixMultiplier() call to save some performance?
|
||
outlineZOffset *= max(1,GetOutlineCameraFovAndDistanceFixMultiplier(positionVS.z, _CurrentCameraFOV, _GlobalOutlineWidthAutoAdjustToCameraDistanceAndFOV * _OutlineApplyAutoWidthAdjustment) / 0.0025);
|
||
|
||
ZOffsetFinalSum += -outlineZOffset;
|
||
#endif
|
||
|
||
#if NiloToonExtraThickOutlinePass
|
||
ZOffsetFinalSum += -_ExtraThickOutlineZOffset;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// URP Shadow bias (matching URP16's ShadowCasterPass.hlsl)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// ShadowCaster pass needs special process to edit positionCS, else shadow artifact will appear
|
||
// see GetShadowPositionHClip() in URP/Shaders/ShadowCasterPass.hlsl
|
||
#if NiloToonShadowCasterPass
|
||
// Why not just call URP's GetShadowPositionHClip() directly?
|
||
// because it will require us to include ShadowCasterPass.hlsl
|
||
// which will include ShadowCasterPass.hlsl's struct Attributes, it is not what we want
|
||
|
||
// this part(PUNCTUAL_LIGHT_SHADOW) exists in URP12 or above's GetShadowPositionHClip()
|
||
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
|
||
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
|
||
#else
|
||
float3 lightDirectionWS = _LightDirection;
|
||
#endif
|
||
|
||
float3 normalWSForShadowCaster = possibleSmoothedNormalWS; // replace to smoothed normal if possible
|
||
|
||
// normalBias will produce "holes" in shadowmap, so we add a slider to control it globally
|
||
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWSForShadowCaster * _GlobalToonShaderNormalBiasMultiplier, lightDirectionWS));
|
||
|
||
#if UNITY_REVERSED_Z
|
||
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
|
||
#else
|
||
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
|
||
#endif
|
||
|
||
output.positionCS = positionCS;
|
||
return output; //return, since later calculations should be not related to shadow caster pass anymore
|
||
#endif
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// NiloToon Self Shadow caster pass's Shadow bias (matching URP16's ShadowCasterPass.hlsl)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// NiloToon's self ShadowCaster pass needs special process to edit positionCS, else shadow artifact will appear
|
||
#if NiloToonCharSelfShadowCasterPass
|
||
{
|
||
// TODO: improve shadow bias using: https://zhuanlan.zhihu.com/p/370951892
|
||
//------------------------------------------------------------
|
||
// settings
|
||
float depthBias = min(_NiloToonGlobalSelfShadowCasterDepthBias + -_NiloToonSelfShadowMappingDepthBias * _EnableNiloToonSelfShadowMappingDepthBias,0);
|
||
float normalBias = min(_NiloToonGlobalSelfShadowCasterNormalBias + -_NiloToonSelfShadowMappingNormalBias * _EnableNiloToonSelfShadowMappingNormalBias,0);
|
||
//------------------------------------------------------------
|
||
|
||
// referencing GetShadowPositionHClip() in URP/Shaders/ShadowCasterPass.hlsl
|
||
float3 positionWS = vertexPositionInput.positionWS;
|
||
float3 normalWS = possibleSmoothedNormalWS; // replace to smoothed normal will produce better result
|
||
|
||
float3 lightDirection = _NiloToonSelfShadowLightDirection; // pointing from vertex to light
|
||
|
||
// copy from URP/ShaderLibrary/Shadows.hlsl's ApplyShadowBias(...)
|
||
//-------------------------------------------------------------
|
||
float invNdotL = 1.0 - saturate(dot(lightDirection, normalWS));
|
||
float scale = invNdotL * normalBias;
|
||
|
||
// normal bias is negative since we want to apply an inset normal offset
|
||
positionWS = lightDirection * depthBias.xxx + positionWS;
|
||
positionWS = normalWS * scale.xxx + positionWS;
|
||
//-------------------------------------------------------------
|
||
|
||
// copy from URP/Shaders/ShadowCasterPass.hlsl's GetShadowPositionHClip(...)
|
||
//-------------------------------------------------------------
|
||
#if !NiloToonCharSelfShadowCasterPass
|
||
float4 positionCS = TransformWorldToHClip(positionWS);
|
||
#else
|
||
// in XR, VP matrix will be overridden by URP, you can't edit it via cmd.SetViewProjectionMatrices, so we need to use our own VP matrix
|
||
float4 positionCS = mul(_NiloToonSelfShadowWorldToClip, float4(positionWS,1));
|
||
#endif
|
||
|
||
#if UNITY_REVERSED_Z
|
||
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
|
||
#else
|
||
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
|
||
#endif
|
||
|
||
output.positionCS = positionCS;
|
||
return output; //return, since later calculations should be not related to shadow caster pass anymore
|
||
//-------------------------------------------------------------
|
||
}
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// ZOffset
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// per material ZOffset (originally created to push eyebrow over hair / face expression mesh (e.g >///<) over face like a alpha blend decal/sticker)
|
||
#if NiloToonIsAnyLitColorPass
|
||
if(_ZOffsetEnable)
|
||
{
|
||
float zoffset = -_ZOffset;
|
||
|
||
// let ZOffset affect outline pass also, with extra control
|
||
#if NiloToonSelfOutlinePass
|
||
zoffset *= _ZOffsetMultiplierForTraditionalOutlinePass;
|
||
#endif
|
||
|
||
#if _ZOFFSETMAP
|
||
float zOffsetTex2DlodExplicitMipLevel = 0;
|
||
float zOffsetMultiplierByTex = dot(_ZOffsetMaskMapChannelMask,tex2Dlod(_ZOffsetMaskTex, float4(input.uv,0,zOffsetTex2DlodExplicitMipLevel)));
|
||
zOffsetMultiplierByTex = _ZOffsetMaskMapInvertColor ? 1-zOffsetMultiplierByTex : zOffsetMultiplierByTex;
|
||
zoffset *= zOffsetMultiplierByTex;
|
||
#endif
|
||
|
||
ZOffsetFinalSum += zoffset;
|
||
}
|
||
#endif
|
||
|
||
// per character ZOffset
|
||
#if ApplyZOffset
|
||
ZOffsetFinalSum += -_PerCharZOffset;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Vertex color
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
output.color = input.color;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// SH (indirect from lightprobe)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// *moved from fragment shader to vertex shader for performance reason,
|
||
// it is ok due to lightprobe's low frequency result color data
|
||
|
||
// [calculate SH in vertex shader to save some cycles]
|
||
// If we call SampleSH(0), this will hide all 3D feeling by ignoring all detail SH.
|
||
// We default only use the constant term, = SampleSH(0)
|
||
// because we just want to get some average envi indirect color only.
|
||
// Hardcode 0 can enable compiler optimization to remove no-op,
|
||
// but here we don't hardcode 0, instead we use a uniform variable(_IndirectLightFlatten) to control the normal,
|
||
// which is slower but allow more flexibility for user
|
||
#if NiloToonIsAnyLitColorPass
|
||
half3 normalWSForSH = output.normalWS_averageShadowAttenuation.xyz;
|
||
normalWSForSH *= 1-_IndirectLightFlatten; // make the normal become 0 when _IndirectLightFlatten is 1
|
||
|
||
half3 SH = SampleSH(normalWSForSH) * _GlobalIndirectLightMultiplier;
|
||
output.SH_fogFactor.rgb = SH;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Average Shadow Attenuation
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// use LOAD_TEXTURE2D instead of tex2dLOD for simplify uv code and also for better performance
|
||
// _NiloToonAverageShadowMapRT is a "N x 1" texture, so LOAD index is (charID,0)
|
||
#if NiloToonIsAnyLitColorPass
|
||
if(_ControlledByNiloToonPerCharacterRenderController)
|
||
{
|
||
output.normalWS_averageShadowAttenuation.w = lerp(1,LOAD_TEXTURE2D(_NiloToonAverageShadowMapRT,float2(_CharacterID,0)).r,_PerCharReceiveAverageURPShadowMap); // floating point RT, so r channel
|
||
}
|
||
else
|
||
{
|
||
output.normalWS_averageShadowAttenuation.w = 1;
|
||
}
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Face's depth texture ZOffset
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// for face vertices, use ZOffset to push back depth write to _CameraDepthTexture (not _CameraColorTexture),
|
||
// doing this can:
|
||
// - prevent face cast 2D depth texture self shadow on face (which looks like artifact)
|
||
// - make hair/hat/sunglass... easier to cast 2D depth texture shadow on face
|
||
#if NiloToonDepthOnlyOrDepthNormalPass && _ISFACE
|
||
float cameraDepthTextureZOffsetMask = 1;
|
||
#if NeedFaceMaskArea
|
||
cameraDepthTextureZOffsetMask = faceArea;
|
||
#endif
|
||
// zoffset should be always greater or equal to depthDiffThreshold, to avoid face cast 2D depth texture self shadow
|
||
ZOffsetFinalSum += -_FaceAreaCameraDepthTextureZWriteOffset * cameraDepthTextureZOffsetMask * _NiloToonGlobalAllowUnityCameraDepthTextureWriteFaceZOffset;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Apply ZOffset
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// The reason to only apply ZOffset(NiloGetNewClipPosWithZOffsetVS()) only once per vertex shader, instead of multiple times,
|
||
// is to reduce precision loss.
|
||
// Precision loss exists if we call NiloGetNewClipPosWithZOffsetVS() multiple times in the same vertex shader.
|
||
#if ApplyZOffset
|
||
output.positionCS = NiloGetNewClipPosWithZOffsetVS(output.positionCS, ZOffsetFinalSum);
|
||
output.positionWS_ZOffsetFinalSum.w = ZOffsetFinalSum;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Remove perspective camera distortion
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#if ApplyPerspectiveRemoval
|
||
output.positionCS = NiloDoPerspectiveRemoval(output.positionCS,positionWS,_NiloToonGlobalPerCharHeadBonePosWSArray[_CharacterID],_PerspectiveRemovalRadius,_PerspectiveRemovalAmount, _PerspectiveRemovalStartHeight, _PerspectiveRemovalEndHeight);
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// User custom logic
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
ApplyCustomUserLogicToVertexShaderOutputAtVertexShaderEnd(output, input);
|
||
|
||
return output;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Above is vertex shader section, below is fragment shader section
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step1: if DEBUG, run these DEBUG functions and early exit at fragment functions's early start point)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
half4 Get_NILOTOON_FORCE_MINIMUM_FRAGMENT_SHADER_result(Varyings input)
|
||
{
|
||
half3 debugColor = SAMPLE_TEXTURE2D(_BaseMap,sampler_BaseMap, GetUV(input)).rgb;
|
||
#if NiloToonIsAnyOutlinePass
|
||
debugColor *= 0.25; // any value is valid, we just want to darken outline a bit
|
||
#endif
|
||
return half4(debugColor,1);
|
||
}
|
||
half4 Get_NILOTOON_DEBUG_SHADING_result(Varyings input, ToonSurfaceData surfaceData, ToonLightingData lightingData, UVData uvData)
|
||
{
|
||
#if _NILOTOON_DEBUG_SHADING
|
||
// Performance is not important here, since it is a debug shader function.
|
||
|
||
// if _NILOTOON_DEBUG_SHADING is active, we only want to render NiloToonForwardLitPass
|
||
#if !NiloToonForwardLitPass
|
||
{
|
||
clip(-1);
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
// Example of switch case in hlsl:
|
||
// https://github.com/SickheadGames/HL2GLSL/blob/master/tests/HL2GLSL/FlowControl.hlsl
|
||
switch(_GlobalToonShadeDebugCase)
|
||
{
|
||
case 0: return SAMPLE_TEXTURE2D(_BaseMap,sampler_BaseMap, lightingData.uv);
|
||
case 1: return half4(surfaceData.albedo, surfaceData.alpha);
|
||
case 2: return 1;
|
||
case 3: return half4(surfaceData.occlusion.xxx,1);
|
||
case 4: return half4(surfaceData.emission,1);
|
||
case 5: return half4(lightingData.normalWS * 0.5 + 0.5,1); // remap to match URP's normalWS in URP Rendering Debugger
|
||
case 6: return half4(lightingData.uv,0,1);
|
||
case 7: return half4(input.color.r.xxx,1);
|
||
case 8: return half4(input.color.g.xxx,1);
|
||
case 9: return half4(input.color.b.xxx,1);
|
||
case 10: return half4(input.color.a.xxx,1);
|
||
case 11: return half4(input.color.rgb,1);
|
||
case 12: return half4(surfaceData.specular.rgb * surfaceData.specularMask,1);
|
||
case 13: return half4(input.uv8 * 0.5 + 0.5,1);
|
||
case 14: return half4(surfaceData.albedo * dot(lightingData.normalWS,lightingData.viewDirectionWS), surfaceData.alpha);
|
||
case 15: return half4(lightingData.isFaceArea.xxx,1);
|
||
case 16: return half4(lightingData.isSkinArea.xxx,1);
|
||
case 17: return half4(surfaceData.alpha.xxx,1);
|
||
case 18: return half4(lightingData.normalVS,1);
|
||
case 19: return half4(lightingData.averageShadowAttenuation.xxx,1);
|
||
case 20: return half4(lightingData.SH,1);
|
||
case 21: return half4(uvData.GetUV(0),0,1);
|
||
case 22: return half4(uvData.GetUV(1),0,1);
|
||
case 23: return half4(uvData.GetUV(2),0,1);
|
||
case 24: return half4(uvData.GetUV(3),0,1);
|
||
case 25: return half4(input.SH_fogFactor.w.xxx,1);
|
||
case 26: return half4(lightingData.ZOffsetFinalSum.xxx,1);
|
||
case 27: return half4(lightingData.viewDirectionWS,1);
|
||
case 28: return half4(surfaceData.normalTS * 0.5 + 0.5,1); // remap to match URP's normalTS in URP Rendering Debugger
|
||
case 29: return half4(surfaceData.smoothness.xxx,1);
|
||
case 30: return half4(lightingData.facing.xxx,1);
|
||
case 31: return half4(lightingData.characterBound2DRectUV01,0,1);
|
||
case 32: return half4(lightingData.matcapUV * 0.5 + 0.5,0,1);
|
||
}
|
||
#endif
|
||
|
||
return 1;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step1.5: edit uv by parallax map)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// copy and modified using URP's LitInput.hlsl -> ApplyPerPixelDisplacement(...)
|
||
void ApplyPerPixelDisplacement(inout Varyings input)
|
||
{
|
||
// Note: this section will affect detail uv
|
||
#if _PARALLAXMAP
|
||
float2 uvoffset = ParallaxMapping(TEXTURE2D_ARGS(_ParallaxMap, sampler_ParallaxMap), input.viewDirTS, _Parallax, GetUV(input,_ParallaxSampleUVIndex));
|
||
switch(_ParallaxApplyToUVIndex)
|
||
{
|
||
case 0: input.uv01.xy += uvoffset; break;
|
||
case 1: input.uv01.zw += uvoffset; break;
|
||
case 2: input.uv23.xy += uvoffset; break;
|
||
case 3: input.uv23.zw += uvoffset; break;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step2: prepare data structs for lighting calculation)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#if AnyBaseMapStackingLayerEnabled
|
||
half4 GetStackingLayerRGBAResult(
|
||
float2 layerTexUV,
|
||
float4 layerTexUVTilingOffset,
|
||
float4 layerTexUVCenterPivotScalePos,
|
||
float2 layerTexUVScrollSpeed,
|
||
float layerTexRotatedAngle,
|
||
float layerTexRotateSpeed,
|
||
half4 layerTexTintColor,
|
||
half4 layerMaskTexChannel,
|
||
float2 layerMaskUV,
|
||
Texture2D layerTex,
|
||
Texture2D layerMaskTex,
|
||
SamplerState layerTexSamplerState,
|
||
float facing,
|
||
bool layerTexIgnoreAlpha,
|
||
float applyToFaces,
|
||
bool invertMask,
|
||
bool layerMaskAsID,
|
||
half layerMaskExtractFromID,
|
||
half layerMaskRemapStart,
|
||
half layerMaskRemapEnd
|
||
)
|
||
{
|
||
// UV's old method (in NiloToon 0.15.x or below)
|
||
//float2 stackingLayerUV = (uv + layerTexUVScaleOffset.zw + frac(_Time.yy * layerTexUVScrollSpeed)) * layerTexUVScaleOffset.xy;
|
||
|
||
// UV's new method (in NiloToon 0.16.0 or above, match lilToon's logic)
|
||
// (the result is different to the old method, it is an intended breaking change in order to match lilToon)
|
||
float2 stackingLayerUV = CalcUV(
|
||
layerTexUV,
|
||
layerTexUVTilingOffset,
|
||
layerTexUVCenterPivotScalePos,
|
||
_Time.y,
|
||
layerTexUVScrollSpeed,
|
||
layerTexRotatedAngle,
|
||
layerTexRotateSpeed);
|
||
|
||
// Note:
|
||
// "The Team Color Problem" by Ben Golus
|
||
// It is possible to improve the result by using "Pre-multiplied Color",
|
||
// but it will require the user to prepare special textures for it, which means it is not practical for NiloToon
|
||
// https://bgolus.medium.com/the-team-color-problem-b70ec69d109f
|
||
|
||
// Sample Texture, sharing the sampler to avoid sampler max count = 16 limit
|
||
half4 stackingLayerRGBAResult = SAMPLE_TEXTURE2D(layerTex, layerTexSamplerState, stackingLayerUV);
|
||
if(layerTexIgnoreAlpha)
|
||
{
|
||
stackingLayerRGBAResult.a = 1;
|
||
}
|
||
stackingLayerRGBAResult *= layerTexTintColor;
|
||
|
||
// for mask texture, we reuse _BaseMap's sampler to reduce sampler count since in most cases the setting should be the same
|
||
half stackingLayerMask = ExtractSingleChannel(SAMPLE_TEXTURE2D(layerMaskTex, sampler_BaseMap, layerMaskUV), layerMaskTexChannel);
|
||
stackingLayerMask = PostProcessMaskValue(stackingLayerMask,layerMaskAsID,layerMaskExtractFromID,invertMask,layerMaskRemapStart,layerMaskRemapEnd);
|
||
stackingLayerRGBAResult.a *= stackingLayerMask;
|
||
|
||
// render face mask
|
||
if(applyToFaces != 0)
|
||
{
|
||
if(applyToFaces == 1) stackingLayerRGBAResult.a *= saturate(-facing);
|
||
if(applyToFaces == 2) stackingLayerRGBAResult.a *= saturate(facing);
|
||
}
|
||
|
||
return stackingLayerRGBAResult;
|
||
}
|
||
|
||
void ApplyStackingLayer(inout half4 baseLayerRGBA, half4 topLayerRGBA, uint colorBlendMode, half applyStrength, bool isOpaque)
|
||
{
|
||
topLayerRGBA.a *= applyStrength;
|
||
|
||
// to prevent any opaque material using basemap with 0 alpha causing bug in NormalRGBA blending
|
||
if(isOpaque && colorBlendMode == 0)
|
||
{
|
||
// convert NormalRGBA -> NormalRGB
|
||
colorBlendMode = 1;
|
||
}
|
||
|
||
baseLayerRGBA = BlendColor(baseLayerRGBA, topLayerRGBA, colorBlendMode);
|
||
}
|
||
#endif
|
||
|
||
half4 GetFinalBaseColor(Varyings input, UVData uvData, float facing)
|
||
{
|
||
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uvData.GetUV(_BaseMapUVIndex));
|
||
|
||
#if _ALPHAOVERRIDEMAP
|
||
half alphaOverrideStrength = _AlphaOverrideStrength;
|
||
if(_ApplyAlphaOverrideOnlyWhenFaceForwardIsPointingToCamera > 0)
|
||
{
|
||
// copied from https://docs.unity3d.com/Packages/com.unity.shadergraph@12.1/manual/Camera-Node.html?q=camera%20node
|
||
half3 cameraBackwardDir = mul(UNITY_MATRIX_M, float4(transpose(mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V)) [2].xyz,0)).xyz;
|
||
|
||
half faceForwardFadeoutControl = invLerpClamp(_ApplyAlphaOverrideOnlyWhenFaceForwardIsPointingToCameraRemapStart, _ApplyAlphaOverrideOnlyWhenFaceForwardIsPointingToCameraRemapEnd,dot(_NiloToonGlobalPerCharFaceForwardDirWSArray[_CharacterID], cameraBackwardDir)); // find the angle between faceForward vector and cameraBackwardDir
|
||
faceForwardFadeoutControl = pow(faceForwardFadeoutControl,4); // fadeout faster
|
||
faceForwardFadeoutControl = lerp(1,faceForwardFadeoutControl, _ApplyAlphaOverrideOnlyWhenFaceForwardIsPointingToCamera); // allow user to control it
|
||
alphaOverrideStrength *= faceForwardFadeoutControl;
|
||
}
|
||
half newAlpha = dot(tex2D(_AlphaOverrideTex, uvData.GetUV(_AlphaOverrideTexUVIndex)),_AlphaOverrideTexChannelMask);
|
||
newAlpha = _AlphaOverrideTexInvertColor ? 1-newAlpha : newAlpha;
|
||
newAlpha = saturate(newAlpha * _AlphaOverrideTexValueScale + _AlphaOverrideTexValueOffset); // alpha MAD of liltoon
|
||
|
||
if(_AlphaOverrideMode == 0)
|
||
{
|
||
// Mode: Replace
|
||
color.a = lerp(color.a,newAlpha,alphaOverrideStrength);
|
||
}
|
||
else if(_AlphaOverrideMode == 1)
|
||
{
|
||
// Mode: Multiply
|
||
color.a = color.a * lerp(1,newAlpha,alphaOverrideStrength);
|
||
}
|
||
else if(_AlphaOverrideMode == 2)
|
||
{
|
||
// Mode: Add
|
||
color.a = saturate(color.a + newAlpha * alphaOverrideStrength);
|
||
}
|
||
else
|
||
{
|
||
// Mode: Subtract
|
||
color.a = saturate(color.a - newAlpha * alphaOverrideStrength);
|
||
}
|
||
#endif
|
||
|
||
color *= GetCombinedBaseColor(); // edit color's rgba(including alpha also, not just rgb), since _BaseColor/_BaseColor2/_Color is per material, using _BaseColor/_BaseColor2/_Color's alpha to control rendering alpha should be intentional by user
|
||
color.rgb *= _BaseMapBrightness;
|
||
|
||
// to prevent opaque material with 0 alpha to use RGBA normal blending
|
||
bool isOpaque = (_SrcBlend == 1) && (_DstBlend == 0);
|
||
#if _ALPHATEST_ON
|
||
isOpaque = isOpaque && (_Cutoff <= 0);
|
||
#endif
|
||
|
||
// add 10 photoshop layer, after _BaseMap's edit finished.
|
||
// This is designed for face makeup, decal, logo, tattoo etc
|
||
#if _BASEMAP_STACKING_LAYER1
|
||
{
|
||
const half4 layer1Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer1TexUVIndex),
|
||
_BaseMapStackingLayer1TexUVScaleOffset,
|
||
_BaseMapStackingLayer1TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer1TexUVAnimSpeed,
|
||
_BaseMapStackingLayer1TexUVRotatedAngle,
|
||
_BaseMapStackingLayer1TexUVRotateSpeed,
|
||
_BaseMapStackingLayer1TintColor,
|
||
_BaseMapStackingLayer1MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer1MaskUVIndex),
|
||
_BaseMapStackingLayer1Tex,
|
||
_BaseMapStackingLayer1MaskTex,
|
||
sampler_BaseMapStackingLayer1Tex, // only the first 2 layers use it's own sampler, to allow more control for user
|
||
facing,
|
||
_BaseMapStackingLayer1TexIgnoreAlpha,
|
||
_BaseMapStackingLayer1ApplytoFaces,
|
||
_BaseMapStackingLayer1MaskInvertColor,
|
||
_BaseMapStackingLayer1MaskTexAsIDMap,
|
||
_BaseMapStackingLayer1MaskTexExtractFromID,
|
||
_BaseMapStackingLayer1MaskRemapStart,
|
||
_BaseMapStackingLayer1MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer1Content, _BaseMapStackingLayer1ColorBlendMode, _BaseMapStackingLayer1MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
|
||
#if _BASEMAP_STACKING_LAYER2
|
||
{
|
||
const half4 layer2Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer2TexUVIndex),
|
||
_BaseMapStackingLayer2TexUVScaleOffset,
|
||
_BaseMapStackingLayer2TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer2TexUVAnimSpeed,
|
||
_BaseMapStackingLayer2TexUVRotatedAngle,
|
||
_BaseMapStackingLayer2TexUVRotateSpeed,
|
||
_BaseMapStackingLayer2TintColor,
|
||
_BaseMapStackingLayer2MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer2MaskUVIndex),
|
||
_BaseMapStackingLayer2Tex,
|
||
_BaseMapStackingLayer2MaskTex,
|
||
sampler_BaseMapStackingLayer2Tex, // only the first 2 layers use it's own sampler, to allow more control for user
|
||
facing,
|
||
_BaseMapStackingLayer2TexIgnoreAlpha,
|
||
_BaseMapStackingLayer2ApplytoFaces,
|
||
_BaseMapStackingLayer2MaskInvertColor,
|
||
_BaseMapStackingLayer2MaskTexAsIDMap,
|
||
_BaseMapStackingLayer2MaskTexExtractFromID,
|
||
_BaseMapStackingLayer2MaskRemapStart,
|
||
_BaseMapStackingLayer2MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer2Content, _BaseMapStackingLayer2ColorBlendMode, _BaseMapStackingLayer2MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER3
|
||
{
|
||
const half4 layer3Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer3TexUVIndex),
|
||
_BaseMapStackingLayer3TexUVScaleOffset,
|
||
_BaseMapStackingLayer3TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer3TexUVAnimSpeed,
|
||
_BaseMapStackingLayer3TexUVRotatedAngle,
|
||
_BaseMapStackingLayer3TexUVRotateSpeed,
|
||
_BaseMapStackingLayer3TintColor,
|
||
_BaseMapStackingLayer3MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer3MaskUVIndex),
|
||
_BaseMapStackingLayer3Tex,
|
||
_BaseMapStackingLayer3MaskTex,
|
||
sampler_linear_clamp, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer3TexIgnoreAlpha,
|
||
_BaseMapStackingLayer3ApplytoFaces,
|
||
_BaseMapStackingLayer3MaskInvertColor,
|
||
_BaseMapStackingLayer3MaskTexAsIDMap,
|
||
_BaseMapStackingLayer3MaskTexExtractFromID,
|
||
_BaseMapStackingLayer3MaskRemapStart,
|
||
_BaseMapStackingLayer3MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer3Content, _BaseMapStackingLayer3ColorBlendMode, _BaseMapStackingLayer3MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER4
|
||
{
|
||
const half4 layer4Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer4TexUVIndex),
|
||
_BaseMapStackingLayer4TexUVScaleOffset,
|
||
_BaseMapStackingLayer4TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer4TexUVAnimSpeed,
|
||
_BaseMapStackingLayer4TexUVRotatedAngle,
|
||
_BaseMapStackingLayer4TexUVRotateSpeed,
|
||
_BaseMapStackingLayer4TintColor,
|
||
_BaseMapStackingLayer4MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer4MaskUVIndex),
|
||
_BaseMapStackingLayer4Tex,
|
||
_BaseMapStackingLayer4MaskTex,
|
||
sampler_linear_repeat, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer4TexIgnoreAlpha,
|
||
_BaseMapStackingLayer4ApplytoFaces,
|
||
_BaseMapStackingLayer4MaskInvertColor,
|
||
_BaseMapStackingLayer4MaskTexAsIDMap,
|
||
_BaseMapStackingLayer4MaskTexExtractFromID,
|
||
_BaseMapStackingLayer4MaskRemapStart,
|
||
_BaseMapStackingLayer4MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer4Content, _BaseMapStackingLayer4ColorBlendMode, _BaseMapStackingLayer4MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER5
|
||
{
|
||
const half4 layer5Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer5TexUVIndex),
|
||
_BaseMapStackingLayer5TexUVScaleOffset,
|
||
_BaseMapStackingLayer5TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer5TexUVAnimSpeed,
|
||
_BaseMapStackingLayer5TexUVRotatedAngle,
|
||
_BaseMapStackingLayer5TexUVRotateSpeed,
|
||
_BaseMapStackingLayer5TintColor,
|
||
_BaseMapStackingLayer5MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer5MaskUVIndex),
|
||
_BaseMapStackingLayer5Tex,
|
||
_BaseMapStackingLayer5MaskTex,
|
||
sampler_linear_clamp, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer5TexIgnoreAlpha,
|
||
_BaseMapStackingLayer5ApplytoFaces,
|
||
_BaseMapStackingLayer5MaskInvertColor,
|
||
_BaseMapStackingLayer5MaskTexAsIDMap,
|
||
_BaseMapStackingLayer5MaskTexExtractFromID,
|
||
_BaseMapStackingLayer5MaskRemapStart,
|
||
_BaseMapStackingLayer5MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer5Content, _BaseMapStackingLayer5ColorBlendMode, _BaseMapStackingLayer5MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER6
|
||
{
|
||
const half4 layer6Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer6TexUVIndex),
|
||
_BaseMapStackingLayer6TexUVScaleOffset,
|
||
_BaseMapStackingLayer6TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer6TexUVAnimSpeed,
|
||
_BaseMapStackingLayer6TexUVRotatedAngle,
|
||
_BaseMapStackingLayer6TexUVRotateSpeed,
|
||
_BaseMapStackingLayer6TintColor,
|
||
_BaseMapStackingLayer6MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer6MaskUVIndex),
|
||
_BaseMapStackingLayer6Tex,
|
||
_BaseMapStackingLayer6MaskTex,
|
||
sampler_linear_repeat, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer6TexIgnoreAlpha,
|
||
_BaseMapStackingLayer6ApplytoFaces,
|
||
_BaseMapStackingLayer6MaskInvertColor,
|
||
_BaseMapStackingLayer6MaskTexAsIDMap,
|
||
_BaseMapStackingLayer6MaskTexExtractFromID,
|
||
_BaseMapStackingLayer6MaskRemapStart,
|
||
_BaseMapStackingLayer6MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer6Content, _BaseMapStackingLayer6ColorBlendMode, _BaseMapStackingLayer6MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER7
|
||
{
|
||
const half4 layer7Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer7TexUVIndex),
|
||
_BaseMapStackingLayer7TexUVScaleOffset,
|
||
_BaseMapStackingLayer7TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer7TexUVAnimSpeed,
|
||
_BaseMapStackingLayer7TexUVRotatedAngle,
|
||
_BaseMapStackingLayer7TexUVRotateSpeed,
|
||
_BaseMapStackingLayer7TintColor,
|
||
_BaseMapStackingLayer7MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer7MaskUVIndex),
|
||
_BaseMapStackingLayer7Tex,
|
||
_BaseMapStackingLayer7MaskTex,
|
||
sampler_linear_clamp, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer7TexIgnoreAlpha,
|
||
_BaseMapStackingLayer7ApplytoFaces,
|
||
_BaseMapStackingLayer7MaskInvertColor,
|
||
_BaseMapStackingLayer7MaskTexAsIDMap,
|
||
_BaseMapStackingLayer7MaskTexExtractFromID,
|
||
_BaseMapStackingLayer7MaskRemapStart,
|
||
_BaseMapStackingLayer7MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer7Content, _BaseMapStackingLayer7ColorBlendMode, _BaseMapStackingLayer7MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER8
|
||
{
|
||
const half4 layer8Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer8TexUVIndex),
|
||
_BaseMapStackingLayer8TexUVScaleOffset,
|
||
_BaseMapStackingLayer8TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer8TexUVAnimSpeed,
|
||
_BaseMapStackingLayer8TexUVRotatedAngle,
|
||
_BaseMapStackingLayer8TexUVRotateSpeed,
|
||
_BaseMapStackingLayer8TintColor,
|
||
_BaseMapStackingLayer8MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer8MaskUVIndex),
|
||
_BaseMapStackingLayer8Tex,
|
||
_BaseMapStackingLayer8MaskTex,
|
||
sampler_linear_repeat, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer8TexIgnoreAlpha,
|
||
_BaseMapStackingLayer8ApplytoFaces,
|
||
_BaseMapStackingLayer8MaskInvertColor,
|
||
_BaseMapStackingLayer8MaskTexAsIDMap,
|
||
_BaseMapStackingLayer8MaskTexExtractFromID,
|
||
_BaseMapStackingLayer8MaskRemapStart,
|
||
_BaseMapStackingLayer8MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer8Content, _BaseMapStackingLayer8ColorBlendMode, _BaseMapStackingLayer8MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER9
|
||
{
|
||
const half4 layer9Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer9TexUVIndex),
|
||
_BaseMapStackingLayer9TexUVScaleOffset,
|
||
_BaseMapStackingLayer9TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer9TexUVAnimSpeed,
|
||
_BaseMapStackingLayer9TexUVRotatedAngle,
|
||
_BaseMapStackingLayer9TexUVRotateSpeed,
|
||
_BaseMapStackingLayer9TintColor,
|
||
_BaseMapStackingLayer9MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer9MaskUVIndex),
|
||
_BaseMapStackingLayer9Tex,
|
||
_BaseMapStackingLayer9MaskTex,
|
||
sampler_linear_clamp, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer9TexIgnoreAlpha,
|
||
_BaseMapStackingLayer9ApplytoFaces,
|
||
_BaseMapStackingLayer9MaskInvertColor,
|
||
_BaseMapStackingLayer9MaskTexAsIDMap,
|
||
_BaseMapStackingLayer9MaskTexExtractFromID,
|
||
_BaseMapStackingLayer9MaskRemapStart,
|
||
_BaseMapStackingLayer9MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer9Content, _BaseMapStackingLayer9ColorBlendMode, _BaseMapStackingLayer9MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
#if _BASEMAP_STACKING_LAYER10
|
||
{
|
||
const half4 layer10Content = GetStackingLayerRGBAResult(
|
||
uvData.GetUV(_BaseMapStackingLayer10TexUVIndex),
|
||
_BaseMapStackingLayer10TexUVScaleOffset,
|
||
_BaseMapStackingLayer10TexUVCenterPivotScalePos,
|
||
_BaseMapStackingLayer10TexUVAnimSpeed,
|
||
_BaseMapStackingLayer10TexUVRotatedAngle,
|
||
_BaseMapStackingLayer10TexUVRotateSpeed,
|
||
_BaseMapStackingLayer10TintColor,
|
||
_BaseMapStackingLayer10MaskTexChannel,
|
||
uvData.GetUV(_BaseMapStackingLayer10MaskUVIndex),
|
||
_BaseMapStackingLayer10Tex,
|
||
_BaseMapStackingLayer10MaskTex,
|
||
sampler_linear_repeat, // for layer 3-10, we hardcode sampler_linear_clamp or sampler_linear_repeat to avoid adding more sampler count
|
||
facing,
|
||
_BaseMapStackingLayer10TexIgnoreAlpha,
|
||
_BaseMapStackingLayer10ApplytoFaces,
|
||
_BaseMapStackingLayer10MaskInvertColor,
|
||
_BaseMapStackingLayer10MaskTexAsIDMap,
|
||
_BaseMapStackingLayer10MaskTexExtractFromID,
|
||
_BaseMapStackingLayer10MaskRemapStart,
|
||
_BaseMapStackingLayer10MaskRemapEnd);
|
||
ApplyStackingLayer(color, layer10Content, _BaseMapStackingLayer10ColorBlendMode, _BaseMapStackingLayer10MasterStrength, isOpaque);
|
||
}
|
||
#endif
|
||
|
||
// final per character + global edit
|
||
color.rgb *= _PerCharacterBaseColorTint * _GlobalVolumeBaseColorTintColor; // edit rgb only, since they are not per material, edit alpha per character/globally should be not intentional by user
|
||
|
||
return color;
|
||
}
|
||
|
||
half3 GetFinalEmissionColor(Varyings input, half3 baseColor)
|
||
{
|
||
#if _EMISSION
|
||
float2 uv = GetUV(input) * _EmissionMapTilingXyOffsetZw.xy + _EmissionMapTilingXyOffsetZw.zw + frac(_Time.yy * _EmissionMapUVScrollSpeed);
|
||
|
||
half4 emissionMapSampleValue = tex2D(_EmissionMap, uv);
|
||
half3 emissionResult = _EmissionMapUseSingleChannelOnly ? dot(emissionMapSampleValue,_EmissionMapSingleChannelMask) : emissionMapSampleValue.rgb; // alpha is ignored if using rgb mode
|
||
|
||
emissionResult *= _EmissionColor.rgb * _EmissionIntensity;
|
||
emissionResult *= lerp(1,baseColor,_MultiplyBaseColorToEmissionColor); // let user optionally mix base color to emission color
|
||
|
||
// anim tint ramp map
|
||
#if _EMISSION_ANIM_TINT_RAMPMAP
|
||
half3 rampSampleColor = SAMPLE_TEXTURE2D_LOD(_EmissionAnimTintRampMap, sampler_linear_clamp, half2(frac(_Time.y * _EmissionAnimTintRampMapSpeed), 0.5),0).rgb;
|
||
emissionResult *= rampSampleColor;
|
||
#endif
|
||
|
||
// mask by an optional mask texture
|
||
// (decided to not use shader_feature for this mask section, else too much shader_feature is used)
|
||
float2 maskMapUV = GetUV(input);
|
||
half emissionMask = dot(SAMPLE_TEXTURE2D(_EmissionMaskMap, sampler_BaseMap, maskMapUV),_EmissionMaskMapChannelMask); // reuse sampler_BaseMap to save sampler count
|
||
emissionMask = _EmissionMaskMapInvertColor? 1-emissionMask : emissionMask;
|
||
emissionMask = invLerpClamp(_EmissionMaskMapRemapStart, _EmissionMaskMapRemapEnd, emissionMask);
|
||
emissionResult *= emissionMask;
|
||
|
||
return emissionResult;
|
||
#else
|
||
return 0; // default emission value is black when turn off
|
||
#endif
|
||
}
|
||
half GetFinalOcculsion(UVData uvData, float facing)
|
||
{
|
||
#if _OCCLUSIONMAP
|
||
half4 texValue = tex2D(_OcclusionMap, uvData.GetUV(_OcclusionMapUVIndex));
|
||
half occlusionValue = dot(texValue, _OcclusionMapChannelMask);
|
||
occlusionValue = _OcclusionMapInvertColor? 1-occlusionValue : occlusionValue;
|
||
occlusionValue = invLerpClamp(_OcclusionRemapStart, _OcclusionRemapEnd, occlusionValue); // should remap first,
|
||
|
||
// render face mask (Both,0 | Front,2 | Back,1)
|
||
half shouldApplyToTargetRenderFace = !((_OcclusionMapApplytoFaces == 1 && facing == 1.0) || (_OcclusionMapApplytoFaces == 2 && facing == -1.0));
|
||
|
||
occlusionValue = lerp(1, occlusionValue, _OcclusionStrength * _GlobalOcclusionStrength * shouldApplyToTargetRenderFace); // then apply per material and per volume fadeout.
|
||
|
||
return occlusionValue;
|
||
#else
|
||
return 1; // default occulusion value is 1 when turn off
|
||
#endif
|
||
}
|
||
half GetFinalSmoothness(Varyings input)
|
||
{
|
||
half smoothnessResult = _Smoothness;
|
||
#if _SMOOTHNESSMAP
|
||
half4 texValue = tex2D(_SmoothnessMap, GetUV(input));
|
||
half smoothnessMultiplierByTexture = dot(texValue, _SmoothnessMapChannelMask);
|
||
smoothnessMultiplierByTexture = _SmoothnessMapInputIsRoughnessMap ? 1-smoothnessMultiplierByTexture : smoothnessMultiplierByTexture;
|
||
smoothnessMultiplierByTexture = invLerpClamp(_SmoothnessMapRemapStart, _SmoothnessMapRemapEnd, smoothnessMultiplierByTexture); // remap
|
||
smoothnessResult *= smoothnessMultiplierByTexture; // apply
|
||
#endif
|
||
return smoothnessResult;
|
||
}
|
||
// return .rgb is specular RGB, return .a is specular mask
|
||
half4 GetFinalSpecularRGBA(UVData uvData, half3 baseColor, float facing)
|
||
{
|
||
#if _SPECULARHIGHLIGHTS
|
||
// mask
|
||
half4 texValue = tex2D(_SpecularMap, uvData.GetUV(_SpecularMapUVIndex));
|
||
half specularMask = dot(texValue, _SpecularMapChannelMask);
|
||
|
||
specularMask = _SpecularMapAsIDMap ? abs(specularMask * 255 - _SpecularMapExtractFromID) < 2 ? 1 : 0 : specularMask;
|
||
specularMask = _SpecularMapInvertColor ? 1 - specularMask : specularMask;
|
||
specularMask = invLerpClamp(_SpecularMapRemapStart, _SpecularMapRemapEnd, specularMask); // should remap first,
|
||
|
||
// * render face mask
|
||
if(_SpecularApplytoFaces != 0)
|
||
{
|
||
if(_SpecularApplytoFaces == 1) specularMask *= saturate(-facing);
|
||
if(_SpecularApplytoFaces == 2) specularMask *= saturate(facing);
|
||
}
|
||
|
||
half3 specularRGBResult = _SpecularColor * _SpecularIntensity;// then apply intensity / color
|
||
specularRGBResult *= lerp(1,baseColor,_MultiplyBaseColorToSpecularColor); // let user optionally mix base color to specular color
|
||
#if _SPECULARHIGHLIGHTS_TEX_TINT
|
||
float2 specularColorTintMapUV = uvData.GetUV(_SpecularColorTintMapUseSecondUv ? 1 : 0);
|
||
specularColorTintMapUV = specularColorTintMapUV * _SpecularColorTintMapTilingXyOffsetZw.xy + _SpecularColorTintMapTilingXyOffsetZw.zw;
|
||
specularRGBResult *= lerp(1,tex2D(_SpecularColorTintMap, specularColorTintMapUV).rgb,_SpecularColorTintMapUsage);
|
||
#endif
|
||
return half4(specularRGBResult,specularMask);
|
||
#else
|
||
return 0; // default specular value is 0 when turn off
|
||
#endif
|
||
}
|
||
half3 GetFinalNormalTS(UVData uvData, float facing)
|
||
{
|
||
#if _NORMALMAP
|
||
// render face mask (Both,0 | Front,2 | Back,1)
|
||
if((_BumpMapApplytoFaces == 1 && facing == 1.0) || (_BumpMapApplytoFaces == 2 && facing == -1.0))
|
||
{
|
||
return half3(0,0,1);
|
||
}
|
||
|
||
const float2 uv = CalcUV(uvData, _BumpMapUVIndex, _BumpMapUVScaleOffset, _BumpMapUVScrollSpeed);
|
||
return UnpackNormalScale(tex2D(_BumpMap, uv), _BumpScale);
|
||
#else
|
||
return half3(0,0,1); //default value of normal map when turn off, pointing out in tangent space, if converted back to world space it will be the same as raw vertex normal
|
||
#endif
|
||
}
|
||
void DoClipTestToTargetAlphaValue(half alpha)
|
||
{
|
||
#if _ALPHATEST_ON
|
||
clip(alpha - _Cutoff);
|
||
#endif
|
||
}
|
||
|
||
uint NiloGetMeshRenderingLayer()
|
||
{
|
||
// SHADER_LIBRARY_VERSION_MAJOR is deprecated for Unity2022.2 or later, so we will use UNITY_VERSION instead
|
||
// see -> https://github.com/Cyanilux/URP_ShaderCodeTemplates/blob/main/URP_SimpleLitTemplate.shader#L145
|
||
#if UNITY_VERSION >= 202220 // (for URP 14 or above)
|
||
uint meshRenderingLayers = GetMeshRenderingLayer();
|
||
#else
|
||
uint meshRenderingLayers = GetMeshRenderingLightLayer();
|
||
#endif
|
||
|
||
return meshRenderingLayers;
|
||
}
|
||
|
||
InputData NiloGetLightLoopInputData(ToonLightingData lightingData)
|
||
{
|
||
// LIGHT_LOOP_BEGIN needs:
|
||
// - inputData.normalizedScreenSpaceUV
|
||
// - inputData.positionWS
|
||
// so we create a temp InputData
|
||
InputData inputData = (InputData)0;
|
||
inputData.normalizedScreenSpaceUV = lightingData.normalizedScreenSpaceUV;
|
||
inputData.positionWS = lightingData.positionWS;
|
||
|
||
return inputData;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Copy and simplify from URP10.2.2's LitInput.hlsl's detail map logic - START
|
||
// this section only exist #if _DETAIL
|
||
#if _DETAIL
|
||
// Used for scaling detail albedo. Main features:
|
||
// - Depending if detailAlbedo brightens or darkens, scale magnifies effect.
|
||
// - No effect is applied if detailAlbedo is 0.5.
|
||
half3 ScaleDetailAlbedo(half3 detailAlbedo, half scale)
|
||
{
|
||
// detailAlbedo = detailAlbedo * 2.0h - 1.0h;
|
||
// detailAlbedo *= _DetailAlbedoMapScale;
|
||
// detailAlbedo = detailAlbedo * 0.5h + 0.5h;
|
||
// return detailAlbedo * 2.0f;
|
||
|
||
// A bit more optimized
|
||
return 2.0h * detailAlbedo * scale - scale + 1.0h;
|
||
}
|
||
half3 ApplyDetailAlbedo(float2 detailUv, half3 albedo, half detailMask)
|
||
{
|
||
half3 detailAlbedo = SAMPLE_TEXTURE2D(_DetailAlbedoMap, sampler_DetailAlbedoMap, detailUv).rgb;
|
||
detailAlbedo = ScaleDetailAlbedo(detailAlbedo, _DetailAlbedoMapScale);
|
||
detailAlbedo *= 0.5 / _DetailAlbedoWhitePoint;
|
||
return albedo * LerpWhiteTo(detailAlbedo, detailMask); // apply detail albedo's method is just 1 simple multiply
|
||
}
|
||
half3 ApplyDetailNormal(float2 detailUv, half3 normalTS, half detailMask)
|
||
{
|
||
// Not using UnpackNormal(...) for mobile & switch now
|
||
// because we want to unify mobile and non mobile detail normalmap rendering, since performance differernce is small
|
||
// honestly no one will care 1 more MUL in 2021, even for mobile
|
||
half3 detailNormalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_DetailNormalMap, sampler_DetailNormalMap, detailUv), _DetailNormalMapScale);
|
||
|
||
// With UNITY_NO_DXT5nm unpacked vector is not normalized for BlendNormalRNM
|
||
// For visual consistancy we should do normalize() in all cases,
|
||
// but here we only normalize #if UNITY_NO_DXT5nm, for performance reason
|
||
#if UNITY_NO_DXT5nm
|
||
detailNormalTS = normalize(detailNormalTS);
|
||
#endif
|
||
|
||
// TODO: detailMask should lerp the angle of the quaternion rotation, not the normals
|
||
return lerp(normalTS, BlendNormalRNM(normalTS, detailNormalTS), detailMask);
|
||
}
|
||
#endif
|
||
// Copy and simplify from URP10.2.2's LitInput.hlsl's detail map logic - END
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// similar to URP's LitForwardPass.hlsl -> InitializeStandardLitSurfaceData()
|
||
ToonSurfaceData InitializeSurfaceData(Varyings input, UVData uvData, float facing, half3 normalTS, half detailMask, float2 detailUV)
|
||
{
|
||
ToonSurfaceData output;
|
||
|
||
// albedo(Base Color) & alpha
|
||
half4 baseColorFinal = GetFinalBaseColor(input, uvData, facing);
|
||
ApplyExternalAssetSupportLogicToBaseColor(baseColorFinal, input, uvData, facing);
|
||
ApplyCustomUserLogicToBaseColor(baseColorFinal, input, uvData, facing);
|
||
output.albedo = baseColorFinal.rgb;
|
||
output.alpha = baseColorFinal.a;
|
||
|
||
// alpha clip and dither fadeout
|
||
DoClipTestToTargetAlphaValue(output.alpha);// let clip() early exit asap once alpha value is known
|
||
#if _NILOTOON_DITHER_FADEOUT
|
||
NiloDoDitherFadeoutClip(input.positionCS.xy, 1-_DitherFadeoutAmount*_AllowPerCharacterDitherFadeout);
|
||
#endif
|
||
|
||
// occlusion
|
||
output.occlusion = GetFinalOcculsion(uvData, facing);
|
||
|
||
// smoothness
|
||
output.smoothness = GetFinalSmoothness(input);
|
||
|
||
// normalTS
|
||
output.normalTS = normalTS;
|
||
|
||
// Detail albedo and normal (only enable in non-DEBUG)
|
||
#if _DETAIL && !_NILOTOON_DEBUG_SHADING
|
||
output.albedo = ApplyDetailAlbedo(detailUV, output.albedo, detailMask);
|
||
#endif
|
||
|
||
///////////////////////////////////////////////////////////
|
||
// after Detail albedo and normal,
|
||
// do all functions that require detail albedo and normal
|
||
///////////////////////////////////////////////////////////
|
||
// emission
|
||
output.emission = GetFinalEmissionColor(input, output.albedo);
|
||
|
||
// specular & specular mask (not roughness)
|
||
half4 specularResult = GetFinalSpecularRGBA(uvData, output.albedo, facing);
|
||
output.specular = specularResult.rgb;
|
||
output.specularMask = specularResult.a;
|
||
|
||
return output;
|
||
}
|
||
|
||
UVData InitializeUV0ToUV3(Varyings input)
|
||
{
|
||
UVData uvData = (UVData)0; // init to 0 to make compiler happy
|
||
|
||
uvData.allUVs[0] = input.uv01.xy;
|
||
uvData.allUVs[1] = input.uv01.zw;
|
||
uvData.allUVs[2] = input.uv23.xy;
|
||
uvData.allUVs[3] = input.uv23.zw;
|
||
|
||
return uvData;
|
||
}
|
||
|
||
half3 CalculateLightInjectionWeightedDirection(Light light, half Avg3AdditionalLightPixelColor)
|
||
{
|
||
return light.direction * Avg3AdditionalLightPixelColor;
|
||
}
|
||
|
||
#if NeedCalculateAdditionalLight
|
||
struct NiloToonPerAdditionalLightData
|
||
{
|
||
half injectIntoMainLightColor;
|
||
half injectIntoMainLightDirection;
|
||
half injectIntoMainLightColorApplyDesaturate;
|
||
half additiveLightIntensity;
|
||
|
||
half injectIntoMainLightBackLightOcclusion2D;
|
||
half injectIntoMainLightBackLightOcclusion3D;
|
||
|
||
bool ignoreRenderingLayer;
|
||
};
|
||
|
||
NiloToonPerAdditionalLightData GetNiloToonPerAdditionalLightData(int lightIndex)
|
||
{
|
||
// copy the code of GetAdditionalLight(...) for find the final GPU array index
|
||
#if NILO_USE_CLUSTER_LIGHT_LOOP
|
||
int realGPULightArrayIndex = lightIndex;
|
||
#else
|
||
int realGPULightArrayIndex = GetPerObjectLightIndex(lightIndex);
|
||
#endif
|
||
|
||
half4 niloToonPerUnityLightData = _NiloToonGlobalPerUnityLightDataArray[realGPULightArrayIndex];
|
||
half4 niloToonPerUnityLightData2 = _NiloToonGlobalPerUnityLightDataArray2[realGPULightArrayIndex];
|
||
|
||
NiloToonPerAdditionalLightData data;
|
||
data.injectIntoMainLightColor = niloToonPerUnityLightData.x;
|
||
data.injectIntoMainLightDirection = niloToonPerUnityLightData.y;
|
||
data.injectIntoMainLightColorApplyDesaturate = niloToonPerUnityLightData.z;
|
||
data.additiveLightIntensity = niloToonPerUnityLightData.w;
|
||
|
||
data.injectIntoMainLightBackLightOcclusion2D = niloToonPerUnityLightData2.x;
|
||
data.injectIntoMainLightBackLightOcclusion3D = niloToonPerUnityLightData2.y;
|
||
|
||
data.ignoreRenderingLayer = niloToonPerUnityLightData2.z > 0.5;
|
||
|
||
return data;
|
||
}
|
||
#endif
|
||
|
||
// similar to URP's LitForwardPass.hlsl -> InitializeInputData()
|
||
ToonLightingData InitializeLightingData(Varyings input, half3 normalTS, float facing, half faceArea)
|
||
{
|
||
ToonLightingData lightingData;
|
||
|
||
lightingData.uv = GetUV(input);
|
||
lightingData.positionWS = input.positionWS_ZOffsetFinalSum.xyz;
|
||
|
||
lightingData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(lightingData.positionWS);
|
||
|
||
half3 normalWS = input.normalWS_averageShadowAttenuation.xyz;
|
||
|
||
// We should re-normalize all direction unit vector after interpolation.
|
||
// Here even _NORMALMAP is false, we still normalize() to ensure correctness (unit vector in vertex shader, after interpolation, is NOT always unit vector in fragment shader).
|
||
// Not doing NormalizeNormalPerPixel(normalWS) will affect GGX specular result greatly since specular depends on normal quality heavily!
|
||
normalWS = NormalizeNormalPerPixel(normalWS);
|
||
|
||
lightingData.normalWS = normalWS;
|
||
lightingData.normalWS_NoNormalMap = normalWS; // extra: save the normalized interpolated vertex normal into lightingData, don't let normal map affect this vector
|
||
|
||
#if VaryingsHasTangentWS
|
||
// [you can reference this section from URP's LitForwardPass.hlsl -> InitializeInputData(...)]
|
||
float sgn = input.tangentWS.w; // should be either +1 or -1. No need to apply unity_WorldTransformParams here because it was applied in vertex shader already
|
||
|
||
// no need to normalize bitangentWS if you only use normalWS in later calculations,
|
||
// since we normalize normalWS at the end anyway when we assign normalWS's result to lightingData.
|
||
float3 bitangentWS = sgn * cross(input.normalWS_averageShadowAttenuation.xyz, input.tangentWS.xyz);
|
||
|
||
lightingData.TBN_WS = half3x3(input.tangentWS.xyz, bitangentWS, input.normalWS_averageShadowAttenuation.xyz);
|
||
|
||
#if defined(VaryingsHasViewDirTS)
|
||
half3 viewDirTS = input.viewDirTS;
|
||
#else
|
||
half3 viewDirTS = GetViewDirectionTangentSpace(input.tangentWS, input.normalWS_averageShadowAttenuation.xyz, lightingData.viewDirectionWS);
|
||
#endif
|
||
lightingData.viewDirectionTS = viewDirTS;
|
||
#endif
|
||
|
||
// if any normalmap is enabled, convert normalTS in normalmap to normalWS
|
||
#if _NORMALMAP || _DETAIL
|
||
normalWS = TransformTangentToWorld(normalTS, lightingData.TBN_WS); // apply normal-mapped result normalWS (rotation-only matrix's transpose equals inverse)
|
||
normalWS = NormalizeNormalPerPixel(normalWS); // since T & B are not normalized, we need to normalize result normalWS after TBN matrix mul
|
||
lightingData.normalWS = normalWS;
|
||
#endif
|
||
|
||
//---------------------------------------------------------------------------------------
|
||
// all normal map related data should init after normalmap is applied to lightingData.normalWS
|
||
//---------------------------------------------------------------------------------------
|
||
|
||
// if no one use lightingData.normalVS, unity's shader compiler will remove this line's calculation,
|
||
// same as URP's VertexPositionInputs and VertexNormalInputs struct
|
||
lightingData.normalVS = mul((half3x3)UNITY_MATRIX_V, lightingData.normalWS).xyz;
|
||
|
||
// see URP's GlobalIllumination.hlsl -> half3 GlobalIllumination(...)'s reflectVector
|
||
lightingData.reflectionVectorWS = reflect(-lightingData.viewDirectionWS,lightingData.normalWS);
|
||
|
||
lightingData.NdotV = saturate(dot(lightingData.normalWS,lightingData.viewDirectionWS));
|
||
|
||
// Note: NiloToon don't have "shadowCoord" from vertex shader, "shadowCoord" is always calculated in fragment shader
|
||
// ...
|
||
|
||
// [Why not passing isFaceArea from vertex shader, but recalculate in frag shader again instead?]
|
||
// because the gap between vertices will produce ugly isFaceArea in fragment shader due to interpolation, so we need to calculate in frag shader again to ensure correctness
|
||
|
||
// toggle "_ISFACE" will affect face lighting and shadowing
|
||
// if is face: override normalWS by user defined face forward direction & face area mask, in vertex shader
|
||
// if is not face: don't edit normal, use mesh's normal directly
|
||
// normalWS face area edit already done in vertex shader
|
||
// so here we don't need to edit normalWS
|
||
lightingData.isFaceArea = faceArea;
|
||
|
||
lightingData.isSkinArea = GetSkinArea(lightingData.uv);
|
||
|
||
lightingData.SV_POSITIONxy = input.positionCS.xy;
|
||
|
||
// note for future XR development:
|
||
// ref code that may help fixing VR problem: https://docs.unity3d.com/Manual/SinglePassStereoRendering.html
|
||
//lightingData.screenUV = input.screenPos.xy / input.screenPos.w;
|
||
//lightingData.screenUV = UnityStereoTransformScreenSpaceTex(lightingData.screenUV);
|
||
|
||
// [why not just pass abs(positionVS.z) from vertex shader? it seems it is also linearEyeDepth]
|
||
// because we want lightingData.selfLinearEyeDepth having the same format as Convert_SV_PositionZ_ToLinearViewSpaceDepth(tex2D(_CameraDepthTexture)),
|
||
// recalculate selfLinearEyeDepth from positionCS.z provided much better precision when doing depth texture shadow depth comparison logic.
|
||
// A "Samsung A70" mobile precision test @ 2021-3-23 confirmed depth texture shadow precision improved a lot using this line instead of abs(positionVS.z) where positionVS is from vertex shader
|
||
lightingData.selfLinearEyeDepth = Convert_SV_PositionZ_ToLinearViewSpaceDepth(input.positionCS.z);
|
||
|
||
lightingData.ZOffsetFinalSum = input.positionWS_ZOffsetFinalSum.w;
|
||
|
||
lightingData.SH = input.SH_fogFactor.xyz;
|
||
|
||
lightingData.averageShadowAttenuation = input.normalWS_averageShadowAttenuation.w;
|
||
|
||
lightingData.NdotV_NoNormalMap = saturate(dot(lightingData.normalWS_NoNormalMap, lightingData.viewDirectionWS));
|
||
|
||
lightingData.vertexColor = input.color;
|
||
|
||
lightingData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
|
||
lightingData.aspectCorrectedScreenSpaceUV = GetAspectRatioCorrectedNormalizedScreenSpaceUV(lightingData.normalizedScreenSpaceUV);
|
||
|
||
lightingData.facing = facing;
|
||
|
||
float3 characterBoundCenterPosWS = _NiloToonGlobalPerCharBoundCenterPosWSArray[_CharacterID];
|
||
lightingData.characterBoundBottomWS_positionWS = characterBoundCenterPosWS - float3(0,_CharacterBoundRadius,0);
|
||
lightingData.characterBoundTopWS_positionWS = characterBoundCenterPosWS + float3(0,_CharacterBoundRadius,0);
|
||
|
||
// Note:
|
||
// for (NiloToonShadowCasterPass || NiloToonCharSelfShadowCasterPass), UNITY_MATRIX_V is converting the vertex to shadow camera's View space,
|
||
// but for a correct dissolve, it should be converting the vertex to main camera's View space, it is a contradiction since there can be many cameras.
|
||
// This will result the screen space dissolve not having a stable direction for (NiloToonShadowCasterPass || NiloToonCharSelfShadowCasterPass)
|
||
// there is no 100% correct solution to this problem, the best we can do is to match the dissolve to main camera's view space, and ignore other cameras.
|
||
lightingData.characterBound2DRectUV01 = Calc3DSphereTo2DUV(lightingData.positionWS, characterBoundCenterPosWS, _CharacterBoundRadius);
|
||
|
||
// make characterBound2DRectUV01 uv matching in all pass
|
||
// TODO: this line is not needed, it only flip the uv.y, without fixing the problem
|
||
#if NiloToonShadowCasterPass || NiloToonCharSelfShadowCasterPass
|
||
lightingData.characterBound2DRectUV01.y = 1-lightingData.characterBound2DRectUV01.y;
|
||
#endif
|
||
|
||
// [require normalmap finish first]
|
||
// a shared matcap uv for all matcap features
|
||
lightingData.matcapUV = CalcMatCapUV(lightingData.viewDirectionWS,lightingData.normalWS);
|
||
lightingData.matcapUV = _EnableUVEditGroup ? lightingData.matcapUV * _MatCapUVTiling : lightingData.matcapUV;
|
||
|
||
//--------------------------------------------------------------------
|
||
// MainLight Note
|
||
//--------------------------------------------------------------------
|
||
// [Which directional light is main light?]
|
||
// For main light index, see URP's UniversalRenderPipeline.cs -> GetMainLightIndex(...)
|
||
// - If Sun exists, it is main light (RenderSettings.sun)
|
||
// - If Sun not exists, the brightest directional light is main light
|
||
// - If multiple brightest directional light exist, the first one found is main light, so it is random
|
||
//
|
||
// It is shaded outside the light loop and it has a specific set of variables and shading path
|
||
// so we can be as fast as possible in the case when there's only a single directional light in scene
|
||
// There are 3 overloads of GetMainLight(...) in URP16:
|
||
// - Light GetMainLight()
|
||
// - Light GetMainLight(float4 shadowCoord)
|
||
// - Light GetMainLight(float4 shadowCoord, float3 positionWS, half4 shadowMask)
|
||
//
|
||
// we will call the most basic GetMainLight() first, then complete
|
||
// - shadowAttenuation
|
||
// - color (light cookie color multiply)
|
||
// in later steps. So we can skip shadowAttenuation calculation if not used
|
||
|
||
//--------------------------------------------------------------------
|
||
// Inject all additional lights into mainLight
|
||
//--------------------------------------------------------------------
|
||
uint meshRenderingLayers = NiloGetMeshRenderingLayer();
|
||
Light mainLight = GetMainLight();
|
||
|
||
// cache original value
|
||
half3 originalMainLightDirection = mainLight.direction;
|
||
half3 originalMainLightColor = mainLight.color;
|
||
|
||
// init main light color as 0, fill in main light only if mainLight's IsMatchingLightLayer is true
|
||
mainLight.color = 0;
|
||
|
||
//----------------------------------------------------------------
|
||
// 0.init Main light's color and direction
|
||
#ifdef _LIGHT_LAYERS
|
||
if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
|
||
#endif
|
||
{
|
||
// user can override main light's direction + color by
|
||
// - NiloToonCharRenderingControlVolume
|
||
// - NiloToonCharacterMainLightOverrider
|
||
// if user didn't override, the value assigned will be the same as original mainLight.color/direction
|
||
mainLight.direction = _GlobalUserOverriddenMainLightDirWS;
|
||
mainLight.color = _GlobalUserOverriddenMainLightColor;
|
||
|
||
// support main light's light cookie
|
||
// since we are calling a basic version GetMainLight(), we have to fill in required data by ourselves
|
||
#if defined(_LIGHT_COOKIES)
|
||
real3 cookieColor = SampleMainLightCookie(lightingData.positionWS);
|
||
mainLight.color *= cookieColor;
|
||
#endif
|
||
|
||
// although main directional light doesn't have a "DistanceAttenuation" like point or spot light,
|
||
// multiply distanceAttenuation with a directional light color will make Light's Culling mask works(the game object's layer, not the new URP rendering layer),
|
||
// this line equals to: *= unity_LightData.z; // unity_LightData.z is 1 when not culled by the culling mask, otherwise 0.
|
||
// (see URP's Lighting.hlsl's GetMainLight())
|
||
mainLight.color *= mainLight.distanceAttenuation;
|
||
|
||
mainLight.color *= lightingData.averageShadowAttenuation;
|
||
|
||
mainLight.color *= _GlobalMainDirectionalLightMultiplier;
|
||
}
|
||
|
||
// apply per char main light control (from NiloToonCharacterLightController scripts)
|
||
// *don't put this in the above if(){...}, since this feature don't care mainLight exist or not, it should always execute
|
||
mainLight.color = mainLight.color * _NiloToonGlobalPerCharMainDirectionalLightTintColorArray[_CharacterID] + _NiloToonGlobalPerCharMainDirectionalLightAddColorArray[_CharacterID];
|
||
|
||
//----------------------------------------------------------------
|
||
// 1. inject indirect light into main light
|
||
|
||
// [sample APV and replace SH, if project enabled APV]
|
||
#if (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2))
|
||
lightingData.SH = SAMPLE_GI(lightingData.SH,
|
||
GetAbsolutePositionWS(lightingData.positionWS),
|
||
lightingData.normalWS * (1-_IndirectLightFlatten), // do normal flatten for indirect light
|
||
lightingData.viewDirectionWS,
|
||
lightingData.SV_POSITIONxy,
|
||
1, // probe occlusion, since this shader is for dynamic character, we use a constant 1
|
||
1); // shadowmask, since this shader is for dynamic character, we use a constant 1
|
||
#endif
|
||
|
||
// [pick the highest between indirect and main directional light = max(direct light,indirect light)]
|
||
// anime character artist usually don't want the concept of indirect + direct light
|
||
// because it will ruin the final color easily,
|
||
// what anime character artist want is:
|
||
// - if direct lighting is bright enough -> keep character's result same as albedo texture that they draw
|
||
// - if in a dark environment -> switch to use light probe/APV to make character blend into the environment color
|
||
// *max() can prevent result completely black, if light probe was not baked and no direct light is active
|
||
const half3 indirectLight = min(max(lightingData.SH,_GlobalIndirectLightMinColor),_GlobalIndirectLightMaxColor);
|
||
mainLight.color = max(mainLight.color, indirectLight);
|
||
|
||
//----------------------------------------------------------------
|
||
// [the following light loop will apply "additional light inject into main light" (from NiloToonAdditionalLightStyle volume)]
|
||
//----------------------------------------------------------------
|
||
#if NeedCalculateAdditionalLight
|
||
InputData inputData = NiloGetLightLoopInputData(lightingData);
|
||
|
||
uint pixelLightCount = GetAdditionalLightsCount();
|
||
|
||
half3 additionalLightsDirectionWeightedSum = 0;
|
||
half3 additionalLightsColorSum = 0;
|
||
|
||
//----------------------------------------------------------------
|
||
// 2.inject Forward+'s directional additional light into main light [Forward+ only]
|
||
|
||
// this is an extra for-loop that only exists in Forward+,
|
||
// and is only looping directional lights as additional light, not including main directional light, or any point/spot light
|
||
// Note: as Directional lights would be in all clusters since they don't have a 3D volume, so they don't go into the cluster structure.
|
||
// Instead, they are stored first in the light buffer(array), so here we can loop them easily starting from index 0.
|
||
#if NILO_USE_CLUSTER_LIGHT_LOOP
|
||
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
|
||
{
|
||
#if UNITY_VERSION >= 60010000
|
||
CLUSTER_LIGHT_LOOP_SUBTRACTIVE_LIGHT_CHECK
|
||
#else
|
||
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
|
||
#endif
|
||
|
||
Light light = GetAdditionalLight(lightIndex, input.positionWS_ZOffsetFinalSum.xyz); // since URP10, you must provide shadowMask in order to receive shadowmap
|
||
NiloToonPerAdditionalLightData niloToonPerAdditionalLightData = GetNiloToonPerAdditionalLightData(lightIndex);
|
||
#ifdef _LIGHT_LAYERS
|
||
if (niloToonPerAdditionalLightData.ignoreRenderingLayer || IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
||
#endif
|
||
{
|
||
// support additional directional light's light cookie
|
||
// since we are calling a basic version GetMainLight(), we have to fill in required data by ourselves
|
||
#if defined(_LIGHT_COOKIES)
|
||
real3 cookieColor = SampleAdditionalLightCookie(lightIndex, inputData.positionWS);
|
||
light.color *= cookieColor;
|
||
#endif
|
||
|
||
// - don't * light.shadowAttenuation, shadow map looks ugly (shadow map doesn't exist for additional directional light in URP)
|
||
half3 additionalLightPixelColor = light.color * light.distanceAttenuation;
|
||
|
||
half Avg3AdditionalLightPixelColor = NiloAvg3(additionalLightPixelColor);
|
||
// simple sum, don't need to multiply any dot(N,L) mask, keep the color sum clean and blurry!
|
||
additionalLightsColorSum += lerp(additionalLightPixelColor,Avg3AdditionalLightPixelColor,niloToonPerAdditionalLightData.injectIntoMainLightColorApplyDesaturate) * niloToonPerAdditionalLightData.injectIntoMainLightColor;
|
||
|
||
// [1]
|
||
// It is possible to additionally multiply "saturate(dot(light.direction,lightingData.normalWS) * k + (1-k))" to weighted sum,
|
||
// it will produce double side rim light when multiple lights having different direction,
|
||
// but double side rim light is not the style that NiloToon want to produce
|
||
// [2]
|
||
// simply use Avg3() as weight for weighted sum, not Luminance(), since red/blue light should have the same directional weight as green light
|
||
additionalLightsDirectionWeightedSum += CalculateLightInjectionWeightedDirection(light,Avg3AdditionalLightPixelColor) * niloToonPerAdditionalLightData.injectIntoMainLightDirection;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//----------------------------------------------------------------
|
||
// 3.inject only point/spot additional light into main light [if Forward+]
|
||
// 3.inject all additional light into main light [if Forward/Deferred]
|
||
|
||
LIGHT_LOOP_BEGIN(pixelLightCount)
|
||
|
||
// TODO: try characterBoundCenterPosWS for GetAdditionalLight(...)
|
||
Light light = GetAdditionalLight(lightIndex, lightingData.positionWS); // not provide shadowMask in order to not receive additional light's shadowmap
|
||
NiloToonPerAdditionalLightData niloToonPerAdditionalLightData = GetNiloToonPerAdditionalLightData(lightIndex);
|
||
|
||
#ifdef _LIGHT_LAYERS
|
||
if (niloToonPerAdditionalLightData.ignoreRenderingLayer || IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
||
#endif
|
||
{
|
||
// support additional directional light's light cookie
|
||
// since we are calling a basic version GetMainLight(), we have to fill in required data by ourselves
|
||
#if defined(_LIGHT_COOKIES)
|
||
real3 cookieColor = SampleAdditionalLightCookie(lightIndex, inputData.positionWS);
|
||
light.color *= cookieColor;
|
||
#endif
|
||
|
||
// - don't * light.shadowAttenuation, shadow map looks ugly
|
||
// - optionally apply saturate() to light.distanceAttenuation to cap it in 0~1, since it is 1/(d^2), it can go very high when light is close to character where d < 1
|
||
half3 additionalLightPixelColor = light.color * lerp(saturate(light.distanceAttenuation),light.distanceAttenuation,_GlobalAdditionalLightInjectIntoMainLightColor_AllowCloseLightOverBright);
|
||
|
||
|
||
|
||
half Avg3AdditionalLightPixelColor = NiloAvg3(additionalLightPixelColor);
|
||
|
||
half backLightOcclusion2DMask = saturate(dot(lightingData.viewDirectionWS, light.direction));
|
||
half backLightOcclusion3DMask = smoothstep(0,0.1,dot(lightingData.normalWS, light.direction));
|
||
//maskTerm = dot(V, L) * 0.5 + 0.5; // possible
|
||
//maskTerm = smoothstep(0.4,0.6,dot(N, L)); // face may look ugly, not te best one.
|
||
|
||
// simple sum, don't need to multiply any dot(N,L) mask, keep the color sum clean and blurry!
|
||
additionalLightsColorSum += lerp(additionalLightPixelColor,Avg3AdditionalLightPixelColor,niloToonPerAdditionalLightData.injectIntoMainLightColorApplyDesaturate)
|
||
* niloToonPerAdditionalLightData.injectIntoMainLightColor
|
||
* lerp(1, backLightOcclusion2DMask,niloToonPerAdditionalLightData.injectIntoMainLightBackLightOcclusion2D)
|
||
* lerp(1, backLightOcclusion3DMask,niloToonPerAdditionalLightData.injectIntoMainLightBackLightOcclusion3D)
|
||
;
|
||
|
||
// [1]
|
||
// It is possible to additionally multiply "saturate(dot(light.direction,lightingData.normalWS) * k + (1-k))" to weighted sum,
|
||
// it will produce double side rim light when multiple lights having different direction,
|
||
// but double side rim light is not the style that NiloToon want to produce
|
||
// [2]
|
||
// simply use Avg3() as weight for weighted sum, not Luminance(), since red/blue light should have the same directional weight as green light
|
||
additionalLightsDirectionWeightedSum += CalculateLightInjectionWeightedDirection(light,Avg3AdditionalLightPixelColor) * niloToonPerAdditionalLightData.injectIntoMainLightDirection;
|
||
}
|
||
LIGHT_LOOP_END
|
||
|
||
// Note: NiloToon doesn't have any additional vertex light
|
||
|
||
additionalLightsColorSum *= _GlobalAdditionalLightInjectIntoMainLightColor_Strength;
|
||
additionalLightsDirectionWeightedSum *= _GlobalAdditionalLightInjectIntoMainLightDirection_Strength;
|
||
|
||
// *Avg3 as weight for weighted sum, not Luminance, since red/blue light should have the same weight as green light
|
||
// (you must make it sync with the weight method of other additionalLightsDirectionWeightedSum)
|
||
half Avg3DirectionalLightPixelColor = NiloAvg3(mainLight.color);
|
||
half3 mainDirectionalLightDirectionWeightedSum = CalculateLightInjectionWeightedDirection(mainLight,Avg3DirectionalLightPixelColor);
|
||
|
||
// normalize the weighted sum of all light's direction, to get the final merged main light's direction
|
||
// brighter light will affect the final merged main light's direction more due to using Avg3 as weight
|
||
// *Add a constant to avoid lightDirectionsWeightedSum is 0, where normal(0) will produce error
|
||
mainLight.direction = normalize(mainDirectionalLightDirectionWeightedSum + additionalLightsDirectionWeightedSum + half3(0,0.001,0));
|
||
|
||
// apply mainLight.color injection at last, don't let it affect mainLight.direction
|
||
// *Also apply an optional Desaturate to the inject color
|
||
mainLight.color += lerp(additionalLightsColorSum,Luminance(additionalLightsColorSum),_GlobalAdditionalLightInjectIntoMainLightColor_Desaturate);
|
||
#endif
|
||
|
||
// [final override mainLight]
|
||
mainLight.direction = _GlobalUserOverriddenFinalMainLightDirWSParam.w ? _GlobalUserOverriddenFinalMainLightDirWSParam.xyz : mainLight.direction;
|
||
mainLight.color = _GlobalUserOverriddenFinalMainLightColorParam.w ? _GlobalUserOverriddenFinalMainLightColorParam.rgb : mainLight.color;
|
||
|
||
/*
|
||
// TODO: possible mainLight.color tonemapping?
|
||
half uselightColorTonemapping = sin(_Time.y * 3.1415 * 2) > 0;
|
||
if(uselightColorTonemapping > 0)
|
||
{
|
||
mainLight.color = lerp(mainLight.color, mainLight.color / (mainLight.color+1.0), uselightColorTonemapping);
|
||
}
|
||
*/
|
||
|
||
// [_ReceiveSelfShadowMappingPosOffset]
|
||
// this uniform will control the extra depth bias of URP shadowmap,
|
||
// doing this extra depth bias is usually for hiding ugly self shadow for shadow sensitive area like face
|
||
#if ShouldReceiveURPShadow
|
||
float materialDepthBias = lerp(_ReceiveSelfShadowMappingPosOffset,_ReceiveSelfShadowMappingPosOffsetForFaceArea,lightingData.isFaceArea);
|
||
float3 URPShadowTestPosWS = lightingData.positionWS + originalMainLightDirection * (materialDepthBias+_GlobalReceiveSelfShadowMappingPosOffset);
|
||
|
||
// always compute any type of URP's ShadowCoord in the fragment shader instead of vertex shader now, due to URP having shadow-cascades
|
||
// https://forum.unity.com/threads/shadow-cascades-weird-since-7-2-0.828453/#post-5516425
|
||
float4 URPShadowCoord = TransformWorldToShadowCoord(URPShadowTestPosWS);
|
||
mainLight.shadowAttenuation = MainLightRealtimeShadow(URPShadowCoord);
|
||
#endif
|
||
|
||
// copy before edit
|
||
lightingData.rawURPShadowAttenuation = mainLight.shadowAttenuation;
|
||
|
||
// apply URP shadowmap intensity (material, per character, renderer feature)
|
||
#if ShouldReceiveURPShadow
|
||
half materialReceiveShadowMappingAmount = lerp(_ReceiveURPShadowMappingAmountForNonFace,_ReceiveURPShadowMappingAmountForFace, lightingData.isFaceArea) * _ReceiveURPShadowMappingAmount;
|
||
mainLight.shadowAttenuation = lerp(1,mainLight.shadowAttenuation, materialReceiveShadowMappingAmount * _GlobalReceiveShadowMappingAmount * _PerCharReceiveStandardURPShadowMap);
|
||
#endif
|
||
|
||
lightingData.mainLight = mainLight;
|
||
|
||
return lightingData;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step3: override surfaceData.albedo)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// [Dynamic Eye]
|
||
// currently using a simple method only
|
||
// (not HDRP's method: https://github.com/Unity-Technologies/Graphics/blob/c6ae7599b33ca48852eddd592b3e40f33f2dd982/com.unity.render-pipelines.high-definition/Runtime/Material/Eye/EyeUtils.hlsl)
|
||
#if _DYNAMIC_EYE
|
||
half3 CalculateDynamicEyeColor(float2 inputUV, float3 inputViewDirTS)
|
||
{
|
||
// scale uv using eye center as pivot
|
||
float2 uv = _DynamicEyeSize * (inputUV - 0.5) + 0.5;
|
||
|
||
// eye Pupil Alpha
|
||
float eyePupilAlpha = dot(tex2D(_DynamicEyePupilMaskTex, uv),_DynamicEyePupilMaskTexChannelMask);
|
||
|
||
// inner eye color (eye pupil)
|
||
float2 offset = -0.73 * inputViewDirTS * _DynamicEyePupilDepthScale * eyePupilAlpha + uv;
|
||
float2 normalizeResult = normalize((offset - 0.5) * 0.5);
|
||
float2 innerEyeUV = lerp(offset, 0.5 + normalizeResult, ((0.8 / _DynamicEyeSize * _DynamicEyePupilSize ) * (1 - 2 * _DynamicEyeSize * length(inputUV - 0.5))));
|
||
half3 innerEyeColor = tex2D(_DynamicEyePupilMap, innerEyeUV) *_DynamicEyePupilColor;
|
||
|
||
// outer eye color (eye white)
|
||
float2 outerEyeUV = inputUV * _DynamicEyeWhiteMap_ST.xy + _DynamicEyeWhiteMap_ST.zw;
|
||
half3 outerEyeColor = tex2D(_DynamicEyeWhiteMap, outerEyeUV);
|
||
|
||
// combine eye color (inner or outer by mask)
|
||
half finalPupilMask = saturate(eyePupilAlpha / _DynamicEyePupilMaskSoftness);
|
||
half3 combinedEyeColor = lerp(outerEyeColor, innerEyeColor, finalPupilMask);
|
||
|
||
return combinedEyeColor * _DynamicEyeFinalTintColor * _DynamicEyeFinalBrightness;
|
||
}
|
||
#endif
|
||
void ApplyDynamicEye(inout ToonSurfaceData surfaceData, Varyings varyings, ToonLightingData lightingData)
|
||
{
|
||
#if _DYNAMIC_EYE
|
||
surfaceData.albedo = CalculateDynamicEyeColor(GetUV(varyings), lightingData.viewDirectionTS);
|
||
#endif
|
||
}
|
||
void ApplyPostLightingPerCharacterBaseMapOverride(inout half3 originalSurfaceColor, UVData uvData)
|
||
{
|
||
#if _NILOTOON_PERCHARACTER_BASEMAP_OVERRIDE
|
||
float2 uv = uvData.GetUV(_PerCharacterBaseMapOverrideUVIndex);
|
||
uv -= 0.5;
|
||
uv = CalcUV(uv,_PerCharacterBaseMapOverrideTilingOffset, _Time.y, _PerCharacterBaseMapOverrideUVScrollSpeed);
|
||
uv += 0.5;
|
||
half4 texValue = SAMPLE_TEXTURE2D(_PerCharacterBaseMapOverrideMap, sampler_BaseMap, uv);
|
||
texValue.rgb *= _PerCharacterBaseMapOverrideTintColor;
|
||
texValue.a *= _PerCharacterBaseMapOverrideAmount;
|
||
|
||
half4 originalColorRGBA = half4(originalSurfaceColor.r,originalSurfaceColor.g,originalSurfaceColor.b,1);
|
||
half4 resultColorRGBA = BlendColor(originalColorRGBA,texValue, _PerCharacterBaseMapOverrideBlendMode);
|
||
|
||
originalSurfaceColor = resultColorRGBA.rgb;
|
||
//we don't want to edit alpha, so just edit rgb
|
||
#endif
|
||
}
|
||
|
||
// [MatCap(Color Replace)]
|
||
void ApplyMatCapColorReplace(inout ToonSurfaceData surfaceData, Varyings varyings, ToonLightingData lightingData)
|
||
{
|
||
#if _MATCAP_BLEND
|
||
half matCapAlphaBlendFinalUsage = _MatCapAlphaBlendUsage;
|
||
|
||
// mask by an optional mask texture
|
||
// (decided to not use shader_feature for this mask section, else too much shader_feature is used)
|
||
half matCapAlphaBlendMask = dot(SAMPLE_TEXTURE2D(_MatCapAlphaBlendMaskMap, sampler_BaseMap, GetUV(varyings)),_MatCapAlphaBlendMaskMapChannelMask); // reuse sampler_BaseMap to save sampler count
|
||
matCapAlphaBlendMask = _MatCapAlphaBlendMaskMapInvertColor? 1-matCapAlphaBlendMask : matCapAlphaBlendMask;
|
||
matCapAlphaBlendMask = invLerpClamp(_MatCapAlphaBlendMaskMapRemapStart, _MatCapAlphaBlendMaskMapRemapEnd, matCapAlphaBlendMask);
|
||
matCapAlphaBlendFinalUsage *= matCapAlphaBlendMask;
|
||
|
||
float2 matCapBlendUV = lightingData.matcapUV.xy * 0.5 * _MatCapAlphaBlendUvScale + 0.5;
|
||
half4 matCapTextureReadRGBA = tex2D(_MatCapAlphaBlendMap, matCapBlendUV) *_MatCapAlphaBlendTintColor;
|
||
|
||
// do [alpha as mask]
|
||
matCapAlphaBlendFinalUsage *= lerp(1,matCapTextureReadRGBA.a,_MatCapAlphaBlendMapAlphaAsMask); // allow MatCap texture's alpha channel as mask also
|
||
surfaceData.albedo = lerp(surfaceData.albedo,matCapTextureReadRGBA.rgb,matCapAlphaBlendFinalUsage);
|
||
#endif
|
||
}
|
||
|
||
// [Mat Cap(additive)]
|
||
void ApplyMatCapAdditive(inout ToonSurfaceData surfaceData, Varyings varyings, ToonLightingData lightingData)
|
||
{
|
||
#if _MATCAP_ADD
|
||
// read matcap texture
|
||
float2 matCapAddUV = lightingData.matcapUV.xy * (0.5 * _MatCapAdditiveUvScale) + 0.5;
|
||
half4 matCapTextureReadRGBA = tex2D(_MatCapAdditiveMap, matCapAddUV);
|
||
matCapTextureReadRGBA = pow(abs(matCapTextureReadRGBA),abs(_MatCapAdditiveExtractBrightArea+1));
|
||
|
||
half4 matCapAdditiveRGBA = _MatCapAdditiveColor; // contains alpha also
|
||
|
||
// intensity slider
|
||
matCapAdditiveRGBA.rgb *= _MatCapAdditiveIntensity; // only affect rgb
|
||
|
||
// let user select mix with basecolor or not
|
||
matCapAdditiveRGBA.rgb *= lerp(1,surfaceData.albedo,_MatCapAdditiveMixWithBaseMapColor);
|
||
|
||
// mask by an optional mask texture
|
||
// (decided to not use shader_feature for this mask section, else too much shader_feature is used)
|
||
half matCapAdditiveMask = dot(SAMPLE_TEXTURE2D(_MatCapAdditiveMaskMap, sampler_BaseMap, GetUV(varyings)),_MatCapAdditiveMaskMapChannelMask); // reuse sampler_BaseMap to save sampler count
|
||
matCapAdditiveMask = _MatCapAdditiveMaskMapInvertColor ? 1-matCapAdditiveMask : matCapAdditiveMask;
|
||
matCapAdditiveMask = invLerpClamp(_MatCapAdditiveMaskMapRemapStart, _MatCapAdditiveMaskMapRemapEnd, matCapAdditiveMask);
|
||
matCapAdditiveRGBA.rgb *= matCapAdditiveMask; // only affect rgb
|
||
|
||
// mix RGBA with matcap texture
|
||
half4 result = matCapTextureReadRGBA * matCapAdditiveRGBA;
|
||
|
||
// do [alpha as mask]
|
||
half alphaAsMask = lerp(1,result.a,_MatCapAdditiveMapAlphaAsMask); // allow MatCap texture's alpha channel as mask also
|
||
result.rgb *= alphaAsMask;
|
||
|
||
// render face mask
|
||
if(_MatCapAdditiveApplytoFaces != 0)
|
||
{
|
||
if(_MatCapAdditiveApplytoFaces == 1) result.rgb *= saturate(-lightingData.facing);
|
||
if(_MatCapAdditiveApplytoFaces == 2) result.rgb *= saturate(lightingData.facing);
|
||
}
|
||
|
||
half3 finalAdd = result.rgb;
|
||
surfaceData.albedo += finalAdd;
|
||
surfaceData.alpha = saturate(surfaceData.alpha + Luminance(finalAdd)); // semi-transparent's reflection should NOT be affected by alpha, so we add alpha on specular area to cancel it
|
||
#endif
|
||
}
|
||
// [Mat Cap(occlusion)]
|
||
void ApplyMatCapOcclusion(inout ToonSurfaceData surfaceData, Varyings varyings, ToonLightingData lightingData)
|
||
{
|
||
#if _MATCAP_OCCLUSION
|
||
// read matcap texture
|
||
float2 matCapOcclusionUV = lightingData.matcapUV.xy * (0.5 * _MatCapOcclusionUvScale) + 0.5;
|
||
half4 matCapTextureReadRGBA = tex2D(_MatCapOcclusionMap, matCapOcclusionUV);
|
||
half matCapTextureReadOcclusion = dot(matCapTextureReadRGBA, _MatCapOcclusionMapChannelMask);
|
||
matCapTextureReadOcclusion = invLerpClamp(_MatCapOcclusionMapRemapStart, _MatCapOcclusionMapRemapEnd, matCapTextureReadOcclusion); // remap
|
||
|
||
// mask by an optional mask texture
|
||
// (decided to not use shader_feature for this mask section, else too much shader_feature is used)
|
||
half matCapOcclusionMask = dot(SAMPLE_TEXTURE2D(_MatCapOcclusionMaskMap, sampler_BaseMap, GetUV(varyings)),_MatCapOcclusionMaskMapChannelMask); // reuse sampler_BaseMap to save sampler count
|
||
matCapOcclusionMask = _MatCapOcclusionMaskMapInvert ? 1-matCapOcclusionMask : matCapOcclusionMask;
|
||
matCapOcclusionMask = invLerpClamp(_MatCapOcclusionMaskMapRemapStart, _MatCapOcclusionMaskMapRemapEnd, matCapOcclusionMask);
|
||
|
||
// occlsion *= matcap texture occlsion & [alpha as mask]
|
||
half resultOcclusion = lerp(1, matCapTextureReadOcclusion, saturate(matCapOcclusionMask * lerp(1,matCapTextureReadRGBA.a,_MatCapOcclusionMapAlphaAsMask) * _MatCapOcclusionIntensity));
|
||
|
||
// apply to occlusion
|
||
surfaceData.occlusion *= resultOcclusion;
|
||
#endif
|
||
}
|
||
|
||
// [Environment Reflections]
|
||
void ApplyEnvironmentReflections(inout ToonSurfaceData surfaceData, Varyings varyings, ToonLightingData lightingData)
|
||
{
|
||
// [How to sample reflection probe correctly?]
|
||
// see URP's GlobalIllumination.hlsl ->
|
||
// half3 GlobalIllumination(BRDFData brdfData, BRDFData brdfDataClearCoat, float clearCoatMask,
|
||
// half3 bakedGI, half occlusion, float3 positionWS,
|
||
// half3 normalWS, half3 viewDirectionWS)
|
||
#if _ENVIRONMENTREFLECTIONS
|
||
const half smoothness = saturate(_EnvironmentReflectionSmoothnessMultiplier * surfaceData.smoothness);
|
||
|
||
// PerceptualRoughness is equal to the (1-raw smoothness texture) linear value
|
||
half roughness = PerceptualSmoothnessToPerceptualRoughness(smoothness);
|
||
|
||
#if UNITY_VERSION > 202220
|
||
half3 GlossyEnvironmentReflectionResult = GlossyEnvironmentReflection(lightingData.reflectionVectorWS, lightingData.positionWS, roughness, surfaceData.occlusion, lightingData.normalizedScreenSpaceUV);
|
||
#else
|
||
half3 GlossyEnvironmentReflectionResult = GlossyEnvironmentReflection(lightingData.reflectionVectorWS, lightingData.positionWS, roughness, surfaceData.occlusion);
|
||
#endif
|
||
|
||
half3 environmentReflection = GlossyEnvironmentReflectionResult * _EnvironmentReflectionColor * _EnvironmentReflectionBrightness;
|
||
environmentReflection *= lerp(1,surfaceData.albedo,_EnvironmentReflectionTintAlbedo);
|
||
|
||
half applyIntensity = _EnvironmentReflectionUsage;
|
||
|
||
// mask by an optional mask texture
|
||
// (decided to not use shader_feature for this mask section, else too much shader_feature is used)
|
||
half mask = dot(SAMPLE_TEXTURE2D(_EnvironmentReflectionMaskMap, sampler_BaseMap, GetUV(varyings)),_EnvironmentReflectionMaskMapChannelMask); // reuse sampler_BaseMap to save sampler count
|
||
mask = _EnvironmentReflectionMaskMapInvertColor? 1-mask : mask;
|
||
mask = invLerpClamp(_EnvironmentReflectionMaskMapRemapStart, _EnvironmentReflectionMaskMapRemapEnd, mask);
|
||
applyIntensity *= mask; // only affect rgb
|
||
|
||
// NdotV as mask (fresnel effect)
|
||
applyIntensity *= lerp(1,invLerpClamp(_EnvironmentReflectionFresnelRemapStart,_EnvironmentReflectionFresnelRemapEnd,pow(1-lightingData.NdotV,abs(_EnvironmentReflectionFresnelPower))),_EnvironmentReflectionFresnelEffect);
|
||
|
||
// render face mask (Both,0 | Front,2 | Back,1)
|
||
applyIntensity *= !((_EnvironmentReflectionApplytoFaces == 1 && lightingData.facing == 1.0) || (_EnvironmentReflectionApplytoFaces == 2 && lightingData.facing == -1.0));
|
||
|
||
// remove face area apply
|
||
#if _ISFACE
|
||
applyIntensity *= (1-lightingData.isFaceArea * (1-_EnvironmentReflectionShouldApplyToFaceArea));
|
||
#endif
|
||
|
||
// apply to albedo
|
||
surfaceData.albedo = lerp(surfaceData.albedo,environmentReflection,applyIntensity * _EnvironmentReflectionApplyReplaceBlending);
|
||
surfaceData.albedo += environmentReflection * applyIntensity * _EnvironmentReflectionApplyAddBlending;
|
||
//surfaceData.alpha = lerp(surfaceData.alpha,1, applyIntensity); // TODO: find out a good way to do environment reflection, similar to matcap(additive)
|
||
#endif
|
||
}
|
||
|
||
// [back face color tint]
|
||
void ApplyBackFaceColorTintIfForwardLitPass(inout half3 originalSurfaceColor, Varyings varyings, ToonLightingData lightingData, half facing)
|
||
{
|
||
#if NiloToonForwardLitPass
|
||
originalSurfaceColor *= facing < 0 ? _BackFaceTintColor : 1;
|
||
#endif
|
||
}
|
||
void ApplyBackFaceForceShadowIfForwardLitPass(inout ToonSurfaceData surfaceData, ToonLightingData lightingData)
|
||
{
|
||
#if NiloToonForwardLitPass
|
||
surfaceData.occlusion *= lightingData.facing < 0 ? 1 - _BackFaceForceShadow : 1;
|
||
#endif
|
||
}
|
||
void ApplyBackFaceReplaceBaseMapColorIfForwardLitPass(inout ToonSurfaceData surfaceData, ToonLightingData lightingData)
|
||
{
|
||
#if NiloToonForwardLitPass
|
||
surfaceData.albedo = lightingData.facing < 0 ? lerp(surfaceData.albedo, _BackFaceBaseMapReplaceColor.rgb, _BackFaceBaseMapReplaceColor.a) : surfaceData.albedo;
|
||
#endif
|
||
}
|
||
|
||
void ApplyAlbedoPreLightingEditIfOutlinePass(inout ToonSurfaceData surfaceData)
|
||
{
|
||
#if NiloToonIsAnyOutlinePass
|
||
surfaceData.albedo = lerp(surfaceData.albedo, _OutlinePreLightingReplaceColor, _OutlineUsePreLightingReplaceColor);
|
||
|
||
// override by texture
|
||
//ApplyOutlineColorOverrideByTexture(originalSurfaceColor, lightingData);
|
||
#endif
|
||
}
|
||
|
||
void ApplyURPDecal(inout ToonSurfaceData surfaceData, Varyings varyings, inout ToonLightingData lightingData)
|
||
{
|
||
#ifdef _DBUFFER
|
||
// cache
|
||
half3 originalAlbedo = surfaceData.albedo;
|
||
half3 originalNormalWS = lightingData.normalWS;
|
||
half originalOcclusion = surfaceData.occlusion;
|
||
half originalSmoothness = surfaceData.smoothness;
|
||
half3 originalSpecular = surfaceData.specular;
|
||
|
||
// apply decal
|
||
half tempMetallic = 0; // we don't use metallic for now
|
||
ApplyDecal(
|
||
varyings.positionCS,
|
||
surfaceData.albedo,
|
||
surfaceData.specular,
|
||
lightingData.normalWS,
|
||
tempMetallic,
|
||
surfaceData.occlusion,
|
||
surfaceData.smoothness
|
||
);
|
||
|
||
// control apply %
|
||
surfaceData.albedo = lerp(originalAlbedo, surfaceData.albedo, _DecalAlbedoApplyStrength);
|
||
lightingData.normalWS = lerp(originalNormalWS, lightingData.normalWS, _DecalNormalApplyStrength);
|
||
surfaceData.occlusion = lerp(originalOcclusion, surfaceData.occlusion, _DecalOcclusionApplyStrength);
|
||
surfaceData.smoothness = lerp(originalSmoothness, surfaceData.smoothness, _DecalSmoothnessApplyStrength);
|
||
surfaceData.specular = lerp(originalSpecular, surfaceData.specular, _DecalSpecularApplyStrength);
|
||
#endif
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step4: calculate lighting & final color)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// Similar to URP's Lighting.hlsl -> UniversalFragmentPBR(...),
|
||
// This function contains no lighting logic, it just pass lighting results data around and sum them.
|
||
// The job done in this function is "do shadow mapping depth test positionWS offset"
|
||
half3 ShadeAllLights(inout ToonSurfaceData surfaceData, ToonLightingData lightingData, Varyings input, UVData uvData)
|
||
{
|
||
///////////////////////////////////////////////////////////////////
|
||
// Main Directional Light
|
||
///////////////////////////////////////////////////////////////////
|
||
//----------------------------------------------------------------------------
|
||
// Light struct is provided by URP to abstract light shader variables.
|
||
//
|
||
// struct Light
|
||
// {
|
||
// half3 direction;
|
||
// half3 color;
|
||
// float distanceAttenuation; // full-float precision required on some platforms
|
||
// half shadowAttenuation;
|
||
// uint layerMask;
|
||
// };
|
||
//
|
||
// URP takes different shading approaches depending on light and platform.
|
||
// You should never reference light shader variables in your shader, instead use the
|
||
// - GetMainLight(...)
|
||
// - GetAdditionalLight(...)
|
||
// funcitons to fill this Light struct.
|
||
//
|
||
// (see struct and function calls, see URP's RealtimeLights.hlsl)
|
||
//----------------------------------------------------------------------------
|
||
|
||
uint meshRenderingLayers = NiloGetMeshRenderingLayer();
|
||
|
||
half3 mainLightResult = 0;
|
||
half depthTexRimArea = 0;
|
||
|
||
#ifdef _LIGHT_LAYERS
|
||
// Note:
|
||
// Since NiloToon 0.16.0, all additional lights will be merged into lightingData.mainLight's color & direction,
|
||
// so it doesn't make sense to use mainLight.layerMask to cull ShadeMainLight(...) anymore,
|
||
// the chance of 0 lights in scene is so low, so we skip the if check completely now.
|
||
//if (IsMatchingLightLayer(lightingData.mainLight.layerMask, meshRenderingLayers)) // Disabled since NiloToon 0.16.0
|
||
#endif
|
||
{
|
||
half4 result = ShadeMainLight(surfaceData, input, lightingData, lightingData.mainLight, uvData);
|
||
mainLightResult = result.rgb;
|
||
depthTexRimArea = result.a;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////
|
||
// additional light (as additive rim light in any style)
|
||
///////////////////////////////////////////////////////////////////
|
||
half3 additionalLightResult = 0;
|
||
|
||
#if NeedCalculateAdditionalLight
|
||
// NOTE: for additional light's sample code, you can copy from URP's Lighting.hlsl's UniversalFragmentPBR(...)
|
||
|
||
// LIGHT_LOOP_BEGIN needs:
|
||
// - inputData.normalizedScreenSpaceUV
|
||
// - inputData.positionWS
|
||
// so we create a temp InputData
|
||
InputData inputData = (InputData)0;
|
||
inputData.normalizedScreenSpaceUV = lightingData.normalizedScreenSpaceUV;
|
||
inputData.positionWS = lightingData.positionWS;
|
||
|
||
half3 additionalLightSum = 0;
|
||
|
||
// shadowMask is related to lightmap's baked shadow mask, which we can ignore it by setting default value (1,1,1,1),
|
||
// we still need this to call the correct GetAdditionalLight(...) overload method
|
||
half4 shadowMask = half4(1, 1, 1, 1);
|
||
|
||
uint pixelLightCount = GetAdditionalLightsCount();
|
||
|
||
// this is an extra for-loop that only exists in Forward+,
|
||
// and is only looping directional lights as additional light, not including main directional light, or any point/spot light
|
||
// Note: as Directional lights would be in all clusters since they don't have a 3D volume, so they don't go into the cluster structure.
|
||
// Instead, they are stored first in the light buffer array, so here we can loop them easily starting from index 0.
|
||
#if NILO_USE_CLUSTER_LIGHT_LOOP
|
||
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
|
||
{
|
||
#if UNITY_VERSION >= 60010000
|
||
CLUSTER_LIGHT_LOOP_SUBTRACTIVE_LIGHT_CHECK
|
||
#else
|
||
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
|
||
#endif
|
||
|
||
Light light = GetAdditionalLight(lightIndex, input.positionWS_ZOffsetFinalSum.xyz, shadowMask); // since URP10, you must provide shadowMask in order to receive shadowmap
|
||
NiloToonPerAdditionalLightData niloToonPerAdditionalLightData = GetNiloToonPerAdditionalLightData(lightIndex);
|
||
#ifdef _LIGHT_LAYERS
|
||
if (niloToonPerAdditionalLightData.ignoreRenderingLayer || IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
||
#endif
|
||
{
|
||
// Different function(simpler and faster function) used to shade additional lights.
|
||
additionalLightSum += CalculateAdditiveSingleAdditionalLight(light, lightingData) * niloToonPerAdditionalLightData.additiveLightIntensity;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// [If Forward+]
|
||
// this is another for-loop, is for looping all effective point and spot additional lights, without any directional lights.
|
||
// since point/spot lights have their own 3D volume inside camera frustum, URP cull them by building Forward+'s light list per 3D cell using CPU jobs for intersection test + binning.
|
||
// [If non Forward+]
|
||
// this is the only for-loop, and will loop all additional lights(maximum 8) in a simple Forward way,
|
||
// loop can include any additional directional/point/spot lights
|
||
LIGHT_LOOP_BEGIN(pixelLightCount)
|
||
Light light = GetAdditionalLight(lightIndex, input.positionWS_ZOffsetFinalSum.xyz, shadowMask); // you must provide shadowMask in order to receive additional light's shadowmap
|
||
NiloToonPerAdditionalLightData niloToonPerAdditionalLightData = GetNiloToonPerAdditionalLightData(lightIndex);
|
||
#ifdef _LIGHT_LAYERS
|
||
if (niloToonPerAdditionalLightData.ignoreRenderingLayer || IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
||
#endif
|
||
{
|
||
// A different function(simpler and faster function) is used to shade additional lights.
|
||
// which do not look as good as ShadeMainLight, but it is faster and support NiloToonCinematicRimLightVolume
|
||
additionalLightSum += CalculateAdditiveSingleAdditionalLight(light, lightingData) * niloToonPerAdditionalLightData.additiveLightIntensity;
|
||
}
|
||
LIGHT_LOOP_END
|
||
|
||
// [NiloToon's Volume] max contribution clamp (will not clamp rim area)
|
||
additionalLightSum = lerp(min(additionalLightSum,_GlobalAdditionalLightMaxContribution),additionalLightSum,depthTexRimArea);
|
||
|
||
// [NiloToon's Volume] rim mask
|
||
half additionalLightRimMask = pow(1-lightingData.NdotV_NoNormalMap,_GlobalAdditionalLightRimMaskPower);
|
||
additionalLightRimMask = smoothstep(0.5-_GlobalAdditionalLightRimMaskSoftness,0.5+_GlobalAdditionalLightRimMaskSoftness,additionalLightRimMask);
|
||
additionalLightSum *= lerp(1, additionalLightRimMask, _GlobalAdditionalLightApplyRimMask);
|
||
|
||
// [NiloToon's Volume] cinematic 2D rim mask
|
||
additionalLightSum *= lerp(1, depthTexRimArea, _GlobalCinematic2DRimMaskStrength);
|
||
|
||
// [NiloToon's Volume] since light can become over bright after modification by Cinematic rim, so we remove light intensity for outline area, which looks better
|
||
#if NiloToonSelfOutlinePass
|
||
additionalLightSum *= 1.0 - _GlobalCinematic3DRimMaskEnabled;
|
||
#endif
|
||
|
||
// [NiloToon's Volume] intensity multiplier for different area
|
||
additionalLightSum *= _GlobalAdditionalLightMultiplier;
|
||
additionalLightSum *= lerp(1,_GlobalAdditionalLightMultiplierForFaceArea,lightingData.isFaceArea);
|
||
#if NiloToonSelfOutlinePass
|
||
additionalLightSum *= _GlobalAdditionalLightMultiplierForOutlineArea;
|
||
#endif
|
||
|
||
additionalLightResult = additionalLightSum * lerp(surfaceData.occlusion,1,_AdditionalLightIgnoreOcclusion);
|
||
additionalLightResult *= lerp(surfaceData.albedo, 1, _GlobalCinematic3DRimMaskEnabled * (1-_GlobalCinematicRimTintBaseMap)); // when cinematic is on, additional light is rim light, so it should not * albedo 100%
|
||
|
||
// when cinematic rim light is on, it is better to remove the additional light on face area,
|
||
// since NiloToon's face normal is usually flatten to face forward direction, which can producing weird rim light.
|
||
// TODO: make 3D rim light use raw normal, and provide option in cinematic rim light volume to allow showing face rim light
|
||
additionalLightResult *= lerp(1,1-_GlobalCinematic3DRimMaskEnabled,lightingData.isFaceArea);
|
||
|
||
#endif
|
||
|
||
///////////////////////////////////////////////////////////////////
|
||
// Emission
|
||
///////////////////////////////////////////////////////////////////
|
||
half3 emissionResult = 0;
|
||
#if _EMISSION
|
||
emissionResult = ShadeEmission(surfaceData, lightingData, lightingData.mainLight);
|
||
#endif
|
||
|
||
///////////////////////////////////////////////////////////////////
|
||
// Composite all lighting result
|
||
///////////////////////////////////////////////////////////////////
|
||
return CompositeAllLightResults(mainLightResult, additionalLightResult, emissionResult);
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step5: outline color edit)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
void ApplyOutlineColorOverrideByTexture(inout half3 originalSurfaceColor, ToonLightingData lightingData)
|
||
{
|
||
#if _OVERRIDE_OUTLINECOLOR_BY_TEXTURE
|
||
half4 outlineColorOverrideTexSampleValue = tex2D(_OverrideOutlineColorTex, lightingData.uv) * _OverrideOutlineColorTexTintColor;
|
||
outlineColorOverrideTexSampleValue.a = lerp(outlineColorOverrideTexSampleValue.a,1,_OverrideOutlineColorTexIgnoreAlphaChannel);
|
||
originalSurfaceColor = lerp(originalSurfaceColor, outlineColorOverrideTexSampleValue.rgb, outlineColorOverrideTexSampleValue.a * _OverrideOutlineColorByTexIntensity);
|
||
#endif
|
||
}
|
||
|
||
void ApplySurfaceToClassicOutlineColorEdit(inout half3 originalSurfaceColor, ToonSurfaceData surfaceData, ToonLightingData lightingData)
|
||
{
|
||
half3 outlineTintColor = lerp(_OutlineTintColor, _OutlineTintColorSkinAreaOverride.rgb, _OutlineTintColorSkinAreaOverride.a * lightingData.isSkinArea);
|
||
|
||
#if _OUTLINETINTCOLORMAP
|
||
outlineTintColor *= tex2D(_OutlineTintColorMap, lightingData.uv);
|
||
#endif
|
||
|
||
originalSurfaceColor *= outlineTintColor * _PerCharacterOutlineColorTint * _GlobalOutlineTintColor;
|
||
originalSurfaceColor *= lerp(_OutlineOcclusionAreaTintColor, 1, surfaceData.occlusion);
|
||
|
||
// _OutlineUseReplaceColor will not account for lighting. To account for lighting, see _OutlineUsePreLightingReplaceColor
|
||
originalSurfaceColor = lerp(originalSurfaceColor, _OutlineReplaceColor, _OutlineUseReplaceColor);
|
||
|
||
// override by texture
|
||
ApplyOutlineColorOverrideByTexture(originalSurfaceColor, lightingData);
|
||
|
||
// override by per char script gameplay effect
|
||
originalSurfaceColor = lerp(originalSurfaceColor, _PerCharacterOutlineColorLerp.rgb, _PerCharacterOutlineColorLerp.a);
|
||
}
|
||
// [Second Pass Extrude Cull front Outline]
|
||
void ApplySurfaceToClassicOutlineColorEditIfOutlinePass(inout half3 originalSurfaceColor, ToonSurfaceData surfaceData, ToonLightingData lightingData)
|
||
{
|
||
#if NiloToonIsAnyOutlinePass
|
||
ApplySurfaceToClassicOutlineColorEdit(originalSurfaceColor,surfaceData,lightingData);
|
||
#endif
|
||
}
|
||
|
||
void ApplySurfaceToScreenSpaceOutlineColorEditIfSurfacePass(inout half3 originalSurfaceColor, ToonSurfaceData surfaceData, ToonLightingData lightingData)
|
||
{
|
||
// this function will only affect ForwardLit pass's surface pixels!
|
||
// while outline pass will only use ApplySurfaceToOutlineColorEditIfOutlinePass(...), but not this function
|
||
#if NiloToonForwardLitPass && _SCREENSPACE_OUTLINE && _NILOTOON_GLOBAL_ENABLE_SCREENSPACE_OUTLINE
|
||
|
||
// if _ZWrite is off (= 0), screen space outline is disabled.
|
||
// because if _ZWrite is off (= 0), this material should be a transparent material,
|
||
// and transparent material won't write to _CameraDepthTexture,
|
||
// which makes screen space outline not correct (screen space outline rely on _CameraDepthTexture and _CameraNormalsTexture),
|
||
// so disable screen space outline automatically if _Zwrite is off
|
||
if(_ZWrite)
|
||
{
|
||
// outline width
|
||
float finalOutlineWidth = lerp(_ScreenSpaceOutlineWidth, _ScreenSpaceOutlineWidthIfFace, lightingData.isFaceArea) * _GlobalScreenSpaceOutlineWidthMultiplierForChar;
|
||
|
||
// depth sensitivity
|
||
float finalDepthSensitivity = max(0,lerp(_ScreenSpaceOutlineDepthSensitivity, _ScreenSpaceOutlineDepthSensitivityIfFace, lightingData.isFaceArea) + _GlobalScreenSpaceOutlineDepthSensitivityOffsetForChar);
|
||
// depth sensitivity texture apply
|
||
half4 depthSensitivityTexValue = tex2D(_ScreenSpaceOutlineDepthSensitivityTex, lightingData.uv);
|
||
half depthSensitivityMultiplierByTex = dot(depthSensitivityTexValue, _ScreenSpaceOutlineDepthSensitivityTexChannelMask);
|
||
depthSensitivityMultiplierByTex = invLerpClamp(_ScreenSpaceOutlineDepthSensitivityTexRemapStart, _ScreenSpaceOutlineDepthSensitivityTexRemapEnd, depthSensitivityMultiplierByTex); // should remap first,
|
||
finalDepthSensitivity *= depthSensitivityMultiplierByTex;
|
||
|
||
// normals sensitivity
|
||
float finalNormalsSensitivity = max(0,lerp(_ScreenSpaceOutlineNormalsSensitivity, _ScreenSpaceOutlineNormalsSensitivityIfFace, lightingData.isFaceArea) + _GlobalScreenSpaceOutlineNormalsSensitivityOffsetForChar);
|
||
// normals sensitivity texture apply
|
||
half4 normalsSensitivityTexValue = tex2D(_ScreenSpaceOutlineNormalsSensitivityTex, lightingData.uv);
|
||
half normalsSensitivityMultiplierByTex = dot(normalsSensitivityTexValue, _ScreenSpaceOutlineNormalsSensitivityTexChannelMask);
|
||
normalsSensitivityMultiplierByTex = invLerpClamp(_ScreenSpaceOutlineNormalsSensitivityTexRemapStart, _ScreenSpaceOutlineNormalsSensitivityTexRemapEnd, normalsSensitivityMultiplierByTex); // should remap first,
|
||
finalNormalsSensitivity *= normalsSensitivityMultiplierByTex;
|
||
//----------------------------------------------------------------------------------------------------------------------------------------
|
||
float isScreenSpaceOutlineArea = IsScreenSpaceOutline(lightingData.SV_POSITIONxy,
|
||
finalOutlineWidth,
|
||
finalDepthSensitivity,
|
||
finalNormalsSensitivity,
|
||
lightingData.selfLinearEyeDepth,
|
||
_GlobalScreenSpaceOutlineDepthSensitivityDistanceFadeoutStrengthForChar,
|
||
_CurrentCameraFOV,
|
||
lightingData.viewDirectionWS);
|
||
|
||
isScreenSpaceOutlineArea *= _GlobalScreenSpaceOutlineIntensityForChar; // intensity control
|
||
originalSurfaceColor *= lerp(1,_ScreenSpaceOutlineTintColor * _GlobalScreenSpaceOutlineTintColorForChar,isScreenSpaceOutlineArea);
|
||
originalSurfaceColor *= lerp(_ScreenSpaceOutlineOcclusionAreaTintColor, 1, surfaceData.occlusion);
|
||
|
||
originalSurfaceColor = lerp(originalSurfaceColor, _ScreenSpaceOutlineReplaceColor, _ScreenSpaceOutlineUseReplaceColor * isScreenSpaceOutlineArea);
|
||
|
||
// override by texture
|
||
half3 originalSurfaceColorBeforeOverrideByTexture = originalSurfaceColor;
|
||
ApplyOutlineColorOverrideByTexture(originalSurfaceColor, lightingData);
|
||
originalSurfaceColor = lerp(originalSurfaceColorBeforeOverrideByTexture, originalSurfaceColor, isScreenSpaceOutlineArea);
|
||
}
|
||
#endif
|
||
}
|
||
void ApplySurfaceToScreenSpaceOutlineV2ColorEditIfSurfacePass(inout half3 originalSurfaceColor, ToonSurfaceData surfaceData, ToonLightingData lightingData)
|
||
{
|
||
// this function will only affect ForwardLit pass's surface pixels!
|
||
// while outline pass will only use ApplySurfaceToOutlineColorEditIfOutlinePass(...), but not this function
|
||
#if NiloToonForwardLitPass && _SCREENSPACE_OUTLINE_V2 && _NILOTOON_GLOBAL_ENABLE_SCREENSPACE_OUTLINE_V2
|
||
// if _ZWrite is off (= 0), screen space outline is disabled.
|
||
// because if _ZWrite is off (= 0), this material should be a transparent material,
|
||
// and transparent material won't write to _CameraDepthTexture,
|
||
// which makes screen space outline not correct (screen space outline rely on _CameraDepthTexture and _CameraNormalsTexture),
|
||
// so disable screen space outline automatically if _Zwrite is off
|
||
if(_ZWrite)
|
||
{
|
||
// TODO: receive from renderer feature V2
|
||
float width = _GlobalScreenSpaceOutlineV2WidthMultiplierForChar;
|
||
float GeometryEdgeThreshold = _GlobalScreenSpaceOutlineV2GeometryEdgeThresholdForChar;
|
||
float NormalAngleCosThetaThresholdMin = _GlobalScreenSpaceOutlineV2NormalAngleCosThetaThresholdForChar;
|
||
float NormalAngleCosThetaThresholdMax = cos(DegToRad(180));
|
||
bool DrawGeometryEdge = _GlobalScreenSpaceOutlineV2EnableGeometryEdgeForChar;
|
||
bool DrawNormalAngleEdge = _GlobalScreenSpaceOutlineV2EnableNormalAngleEdgeForChar;
|
||
bool DrawMaterialIDBoundary = true;
|
||
bool DrawCustomIDBoundary = true;
|
||
bool DrawWireframe = false;
|
||
|
||
if(lightingData.isFaceArea)
|
||
{
|
||
DrawGeometryEdge = false;
|
||
DrawNormalAngleEdge = false;
|
||
}
|
||
if(lightingData.isSkinArea)
|
||
{
|
||
DrawNormalAngleEdge = false;
|
||
}
|
||
|
||
float isScreenSpaceOutlineArea = IsScreenSpaceOutlineV2(
|
||
lightingData.SV_POSITIONxy,
|
||
width,
|
||
GeometryEdgeThreshold,
|
||
NormalAngleCosThetaThresholdMin,
|
||
NormalAngleCosThetaThresholdMax,
|
||
DrawGeometryEdge,
|
||
DrawNormalAngleEdge,
|
||
DrawMaterialIDBoundary,
|
||
DrawCustomIDBoundary,
|
||
DrawWireframe,
|
||
lightingData.positionWS);
|
||
|
||
half3 outlineSurfaceColor = originalSurfaceColor;
|
||
ApplySurfaceToClassicOutlineColorEdit(outlineSurfaceColor,surfaceData,lightingData);
|
||
originalSurfaceColor = lerp(originalSurfaceColor,outlineSurfaceColor,isScreenSpaceOutlineArea * _GlobalScreenSpaceOutlineV2IntensityForChar);
|
||
}
|
||
#endif
|
||
}
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step6: per volume and per character color edit)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// [Volume]
|
||
void ApplyVolumeEffectColorEdit(inout half3 color)
|
||
{
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
// (not lighting, usually for cinematic needs like cut scene)
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
color *= _GlobalVolumeMulColor;
|
||
color = lerp(color,_GlobalVolumeLerpColor.rgb,_GlobalVolumeLerpColor.a);
|
||
}
|
||
// [Per Character script]
|
||
void ApplyPerCharacterEffectColorEdit(inout half3 color, ToonLightingData lightingData)
|
||
{
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
// (not lighting, usually for gameplay needs like character selection)
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
// desaturate
|
||
color = lerp(color,Luminance(color), _PerCharEffectDesaturatePercentage);
|
||
|
||
// mul add
|
||
color.rgb = color.rgb * _PerCharEffectTintColor + _PerCharEffectAddColor;
|
||
|
||
// lerp
|
||
color.rgb = lerp(color.rgb,_PerCharEffectLerpColor.rgb,_PerCharEffectLerpColor.a);
|
||
|
||
// rim light (outline pass don't need this)
|
||
#if NiloToonForwardLitPass
|
||
// Use PolygonNdotV instead of NdotV,
|
||
// because ToonLightingData's NdotV is normalmap applied & normalized, we DONT want normalmap affecting rim light here.
|
||
half NdotV = lightingData.NdotV_NoNormalMap;
|
||
half rim = 1-NdotV;
|
||
|
||
rim = pow(rim,_PerCharEffectRimSharpnessPower);
|
||
|
||
// apply(additive)
|
||
color.rgb += (rim * (1-lightingData.isFaceArea)) * _PerCharEffectRimColor; // rim light not show on face because it looks ugly
|
||
#endif
|
||
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step7: dissolve)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
void DoPerMatDissolve(inout half3 color, Varyings input, ToonLightingData lightingData, UVData uvData)
|
||
{
|
||
#if _PER_MAT_DISSOLVE_ON
|
||
half dissolveSampleValue = dot(tex2D(_PerMaterialDissolveThresholdMap, uvData.GetUV(_PerMaterialDissolveThresholdMapUVIndex) * _PerMaterialDissolveThresholdMapUVTiling), _PerMaterialDissolveThresholdMapChannelMask);
|
||
dissolveSampleValue = _PerMaterialDissolveThresholdMapInvertColor ? 1-dissolveSampleValue : dissolveSampleValue;
|
||
half dissolvePatternSampleValue = dot(tex2D(_PerMaterialDissolvePatternMap, uvData.GetUV(_PerMaterialDissolvePatternMapUVIndex) * _PerMaterialDissolvePatternMapUVTiling), _PerMaterialDissolvePatternMapChannelMask);
|
||
dissolvePatternSampleValue = _PerMaterialDissolvePatternMapInvertColor ? 1-dissolvePatternSampleValue : dissolvePatternSampleValue;
|
||
half patternOffset = (dissolvePatternSampleValue * 2.0 - 1.0) * _PerMaterialDissolvePatternMapStrength;
|
||
half finalClipValue = dissolveSampleValue - _PerMaterialDissolveCutoff + patternOffset;
|
||
clip(finalClipValue);
|
||
|
||
// color at edge
|
||
color.rgb = lerp(color.rgb, _PerMaterialDissolveEdgeColor, _PerMaterialDissolveCutoff > 0 ? invLerpClamp(_PerMaterialDissolveEdgeWidth,0, finalClipValue) : 0);
|
||
#endif
|
||
}
|
||
void ApplyDissolve(inout half3 color, Varyings input, ToonLightingData lightingData, UVData uvData)
|
||
{
|
||
// do per material dissolve first
|
||
DoPerMatDissolve(color, input, lightingData, uvData);
|
||
//------------------------------------------------------------
|
||
// then do per character dissolve
|
||
#if _NILOTOON_DISSOLVE
|
||
// matching to NiloToonPerCharacterRenderController.cs's enum DissolveMode
|
||
#define DISSOLVEMODE_UV1 1
|
||
#define DISSOLVEMODE_UV2 2
|
||
#define DISSOLVEMODE_WorldSpaceNoise 3
|
||
#define DISSOLVEMODE_WorldSpaceVerticalUpward 4
|
||
#define DISSOLVEMODE_WorldSpaceVerticalDownward 5
|
||
#define DISSOLVEMODE_ScreenSpaceNoise 6
|
||
#define DISSOLVEMODE_ScreenSpaceVerticalUpward 7
|
||
#define DISSOLVEMODE_ScreenSpaceVerticalDownward 8
|
||
|
||
half finalDissolveAmount = _DissolveAmount * _AllowPerCharacterDissolve;
|
||
if(finalDissolveAmount <= 0) return;
|
||
|
||
float noiseStrength = _DissolveNoiseStrength * 0.1;
|
||
float2 uvTiling = float2(_DissolveThresholdMapTilingX,_DissolveThresholdMapTilingY);
|
||
|
||
half dissolveMapThresholdMapValue = 0;
|
||
|
||
// UV1
|
||
if(_DissolveMode == DISSOLVEMODE_UV1)
|
||
{
|
||
float2 uv = GetUV(input) * uvTiling;
|
||
dissolveMapThresholdMapValue = tex2D(_DissolveThresholdMap, uv).g;
|
||
}
|
||
else
|
||
// UV2
|
||
if(_DissolveMode == DISSOLVEMODE_UV2)
|
||
{
|
||
float2 uv = uvData.GetUV(1) * uvTiling;
|
||
dissolveMapThresholdMapValue = tex2D(_DissolveThresholdMap, uv).g;
|
||
}
|
||
else
|
||
// WorldSpaceNoise
|
||
if(_DissolveMode == DISSOLVEMODE_WorldSpaceNoise)
|
||
{
|
||
float2 uv;
|
||
|
||
uv = lightingData.positionWS.xz * uvTiling;
|
||
dissolveMapThresholdMapValue = tex2D(_DissolveThresholdMap, uv).g;
|
||
|
||
uv = lightingData.positionWS.xy * uvTiling;
|
||
dissolveMapThresholdMapValue *= tex2D(_DissolveThresholdMap, uv).g;
|
||
|
||
uv = lightingData.positionWS.yz * uvTiling;
|
||
dissolveMapThresholdMapValue *= tex2D(_DissolveThresholdMap, uv).g;
|
||
}
|
||
else
|
||
// WorldSpaceVerticalUpward
|
||
if(_DissolveMode == DISSOLVEMODE_WorldSpaceVerticalUpward)
|
||
{
|
||
float2 uv = lightingData.positionWS.xz * uvTiling;
|
||
float noise = tex2D(_DissolveThresholdMap, uv).g * noiseStrength;
|
||
dissolveMapThresholdMapValue = invLerpClamp(lightingData.characterBoundBottomWS_positionWS.y,lightingData.characterBoundTopWS_positionWS.y,lightingData.positionWS.y + noise);
|
||
}
|
||
else
|
||
// WorldSpaceVerticalDownward
|
||
if(_DissolveMode == DISSOLVEMODE_WorldSpaceVerticalDownward)
|
||
{
|
||
float2 uv = lightingData.positionWS.xz * uvTiling;
|
||
float noise = tex2D(_DissolveThresholdMap, uv).g * noiseStrength;
|
||
dissolveMapThresholdMapValue = invLerpClamp(lightingData.characterBoundTopWS_positionWS.y,lightingData.characterBoundBottomWS_positionWS.y,lightingData.positionWS.y + noise);
|
||
}
|
||
else
|
||
// ScreenSpaceNoise
|
||
if(_DissolveMode == DISSOLVEMODE_ScreenSpaceNoise)
|
||
{
|
||
// since the default bounding sphere is 2.5m, when compared to world space, it should have 2.5x Tiling
|
||
float2 uv = lightingData.characterBound2DRectUV01 * uvTiling * 2.5;
|
||
dissolveMapThresholdMapValue = tex2D(_DissolveThresholdMap, uv).g;
|
||
}
|
||
else
|
||
// ScreenSpaceVerticalUpward
|
||
if(_DissolveMode == DISSOLVEMODE_ScreenSpaceVerticalUpward)
|
||
{
|
||
// since the default bounding sphere is 2.5m, when compared to world space, it should have 2.5x Tiling
|
||
float2 uv = lightingData.characterBound2DRectUV01 * uvTiling * 2.5;
|
||
float noise = tex2D(_DissolveThresholdMap, uv).g * noiseStrength;
|
||
dissolveMapThresholdMapValue = saturate(lightingData.characterBound2DRectUV01.y + noise);
|
||
}
|
||
else
|
||
// ScreenSpaceVerticalDownward
|
||
if(_DissolveMode == DISSOLVEMODE_ScreenSpaceVerticalDownward)
|
||
{
|
||
// since the default bounding sphere is 2.5m, when compared to world space, it should have 2.5x Tiling
|
||
float2 uv = lightingData.characterBound2DRectUV01 * uvTiling * 2.5;
|
||
float noise = tex2D(_DissolveThresholdMap, uv).g * noiseStrength;
|
||
dissolveMapThresholdMapValue = saturate((1-lightingData.characterBound2DRectUV01.y) + noise);
|
||
}
|
||
|
||
half dissolve = dissolveMapThresholdMapValue - finalDissolveAmount;
|
||
|
||
// clip threshold
|
||
clip(dissolve-0.0001);
|
||
|
||
// HDR color tint to "near threshold area"
|
||
color = lerp(color, _DissolveBorderTintColor, smoothstep(finalDissolveAmount + _DissolveBorderRange, finalDissolveAmount, dissolveMapThresholdMapValue));
|
||
#endif
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step8: fog)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
void ApplyFog(inout half3 color, Varyings input)
|
||
{
|
||
// Mix the pixel color with fog color.
|
||
// You can optionally use MixFogColor to override the fog color with a custom one.
|
||
float fogCoord = InitializeInputDataFog(float4(input.positionWS_ZOffsetFinalSum.xyz, 1.0), input.SH_fogFactor.w);
|
||
color = MixFog(color, fogCoord);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions (Step9: override final output alpha)
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
void ApplyOverrideOutputAlpha(inout half outputAlpha)
|
||
{
|
||
// not worth an if() here, so use a?b:c
|
||
outputAlpha = _EditFinalOutputAlphaEnable ? (_ForceFinalOutputAlphaEqualsOne ? 1.0 : outputAlpha) : outputAlpha;
|
||
|
||
// auto output "alpha == 1" for default opaque material (_SrcBlend = One, _DstBlend = Zero)
|
||
// since when rendering opaque material, most of the cases the alpha value on RT is expected to be one, but not alpha value of _BaseMap
|
||
// forcing 1 will make rendering character mask(RT's alpha) always correct.
|
||
// https://docs.unity3d.com/ScriptReference/Rendering.BlendMode.html
|
||
if((_SrcBlend == 1) && (_DstBlend == 0))
|
||
outputAlpha = 1;
|
||
|
||
#if NiloToonSelfOutlinePass
|
||
outputAlpha = 1; // outline pass always render as opaque colors, semi-transparent outline is not allowed, so for outline we force output alpha = 1 to make alpha correct when user use RenderTexture's alpha as mask
|
||
#endif
|
||
}
|
||
|
||
void InitAllData(inout Varyings input, float facing, half faceArea, out UVData uvData, out ToonLightingData lightingData, out ToonSurfaceData surfaceData)
|
||
{
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// Apply Parallax, edit uv before any uv copy or texture sampling
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
ApplyPerPixelDisplacement(input);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// UV0~3 copy to struct
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
uvData = InitializeUV0ToUV3(input);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// prepare all data struct for lighting function
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
half3 normalTS = GetFinalNormalTS(uvData,facing);
|
||
|
||
// normalTS apply detail normal
|
||
half detailMask = 1;
|
||
float2 detailUV = 0;
|
||
#if _DETAIL && !_NILOTOON_DEBUG_SHADING
|
||
detailMask = dot(_DetailMaskChannelMask, SAMPLE_TEXTURE2D(_DetailMask, sampler_DetailMask, GetUV(input)));
|
||
detailMask = _DetailMaskInvertColor? 1-detailMask : detailMask;
|
||
detailUV = uvData.GetUV(_DetailUseSecondUv ? 1 : 0) * _DetailMapsScaleTiling.xy + _DetailMapsScaleTiling.zw; // TODO: we want to allow UV0~3, but it will be a breaking change of _DetailUseSecondUv
|
||
normalTS = ApplyDetailNormal(detailUV, normalTS, detailMask);
|
||
#endif
|
||
|
||
lightingData = InitializeLightingData(input,normalTS, facing, faceArea);
|
||
|
||
// fill-in remaining UVData slots
|
||
uvData.allUVs[4] = lightingData.matcapUV * 0.5 + 0.5;
|
||
uvData.allUVs[5] = lightingData.characterBound2DRectUV01;
|
||
uvData.allUVs[6] = lightingData.aspectCorrectedScreenSpaceUV;
|
||
|
||
surfaceData = InitializeSurfaceData(input, uvData, facing, normalTS, detailMask, detailUV);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// only NiloToonCharacter.shader will be calling this function by using
|
||
// #pragma fragment FragmentShaderAllWork
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// [Note: VFACE vs SV_IsFrontFace]
|
||
// Don't use VFACE anymore(it is a DX9 legacy semantics), use SV_IsFrontFace instead,
|
||
// because VFACE can't compile correctly(no compile error log, but rendering is wrong in build) on DX12(XBox One GameCore)
|
||
// - https://forum.unity.com/threads/using-vface-in-surface-shader.460941/#post-3787066
|
||
// - https://github.com/microsoft/DirectXShaderCompiler/issues/3494
|
||
// - https://forum.unity.com/threads/unity-is-adding-a-new-dxc-hlsl-compiler-backend-option.1086272/
|
||
// - https://docs.google.com/document/d/1yHARKE5NwOGmWKZY2z3EPwSz5V_ZxTDT8RnRl521iyE/edit#
|
||
// We now use these highlevel define and macro provided by Unity:
|
||
// - FRONT_FACE_TYPE (usually defined as -> bool)
|
||
// - FRONT_FACE_SEMANTIC (usually defined as -> SV_IsFrontFace)
|
||
// - IS_FRONT_VFACE(IsFrontFace, 1.0, -1.0) (usually defined as -> IsFrontFace ? 1.0 : -1.0)
|
||
void FragmentShaderAllWork(Varyings input, FRONT_FACE_TYPE IsFrontFace : FRONT_FACE_SEMANTIC
|
||
, out half4 outColor : SV_Target0
|
||
#ifdef _WRITE_RENDERING_LAYERS
|
||
// Unity6.2 changed float4 -> uint
|
||
#if UNITY_VERSION >= 60020000
|
||
, out uint outRenderingLayers : SV_Target1
|
||
#else
|
||
, out float4 outRenderingLayers : SV_Target1
|
||
#endif
|
||
#endif
|
||
)
|
||
{
|
||
// [Note: SV_POSITION (input.positionCS)'s value in fragment shader]
|
||
// In the vertex shader, you output SV_POSITION, which contains the clip space position (positionCS) after the model-view-projection (MVP) transformation.
|
||
// In the fragment shader, if you read SV_POSITION's value, it's not the same as the clip space position from the vertex shader, because it has been transformed to screen space coordinates.
|
||
// When you access input.positionCS.xyzw (SV_POSITION) in the fragment shader, you will get:
|
||
// - x: for DX10 or later = the pixel center position: integer index x on RenderTarget + 0.5 (e.g. 0.5~1919.5 in a 1920x1080 render target), you can use input.positionCS.xy for LOAD texture. You can also get a screen space 0~1 uv by dividing it by the render target width
|
||
// - y: for DX10 or later = the pixel center position: integer index y on RenderTarget + 0.5 (e.g. 0.5~1079.5 in a 1920x1080 render target), you can use input.positionCS.xy for LOAD texture. You can also get a screen space 0~1 uv by dividing it by the render target height
|
||
// - z: SV_DEPTH, it is the same as NDC's z, the depth value that is written to the depth buffer, it is 0~1 for all graphics API
|
||
// - w: positive value of view space depth or view space z, which is the same as the w component in clip space (positionCS)
|
||
// or
|
||
// fragment SV_POSITION.x = (CLIP_POSITION.x / CLIP_POSITION.w + 1) / 2 * w
|
||
// fragment SV_POSITION.y = (CLIP_POSITION.y / CLIP_POSITION.w + 1) / 2 * h
|
||
// fragment SV_POSITION.z = CLIP_POSITION.z / CLIP_POSITION.w
|
||
// fragment SV_POSITION.w = CLIP_POSITION.w
|
||
// * the NDC range of DirectX is x:[-1, 1],y:[-1, 1],z:[0, 1]
|
||
// * For more information about SV_POSITION's value in the fragment shader, see:
|
||
// https://zhuanlan.zhihu.com/p/597918725
|
||
// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics
|
||
// https://zhuanlan.zhihu.com/p/455189480
|
||
|
||
// to support GPU instancing and Single Pass Stereo rendering(VR), add the following section
|
||
//------------------------------------------------------------------------------------------------------------------------------
|
||
// see ShadowCasterPass.hlsl for why these lines are removed for NiloToonCharSelfShadowCasterPass
|
||
#if !NiloToonCharSelfShadowCasterPass
|
||
UNITY_SETUP_INSTANCE_ID(input); // in non OpenGL and non PSSL, MACRO will turn into -> UnitySetupInstanceID(input.instanceID);
|
||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); // in non OpenGL and non PSSL, MACRO will turn into -> unity_StereoEyeIndex = input.stereoTargetEyeIndexAsRTArrayIdx;
|
||
#endif
|
||
//------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// flip normalWS by IS_FRONT_VFACE()
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
float facing = IS_FRONT_VFACE(IsFrontFace, 1.0, -1.0);
|
||
|
||
// not apply "flip normal" to "NiloToonOutline" lit color pass, because we need to original normal for light shading and shadowmap normal bias
|
||
#if !NiloToonIsAnyOutlinePass
|
||
input.normalWS_averageShadowAttenuation.xyz *= facing;
|
||
input.smoothedNormalWS *= facing;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// Find out face area, and edit normalWS if pixel is within face area
|
||
// (must do this in fragment shader, else if we interpolate normalWS result based on vertex level "IsFace", it looks very bad)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
half4 lightingNormal_faceArea = GetLightingNormalWS_FaceArea(input.normalWS_averageShadowAttenuation.xyz,input.positionWS_ZOffsetFinalSum.xyz,input.uv01.xy);
|
||
input.normalWS_averageShadowAttenuation.xyz = lightingNormal_faceArea.xyz;
|
||
|
||
half faceArea = lightingNormal_faceArea.w;
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// insert a performance debug minimum shader early exit section here
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// exit asap, to maximize performance difference
|
||
// doing this can create a net GPU time difference between "Unlit/Texture" vs "full fragment shader"
|
||
// which reflect the cost of this fragment shader quite correctly on target device
|
||
#if _NILOTOON_FORCE_MINIMUM_SHADER
|
||
outColor = Get_NILOTOON_FORCE_MINIMUM_FRAGMENT_SHADER_result(input);
|
||
return;
|
||
#endif
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// Init all data structs
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
UVData uvData;
|
||
ToonLightingData lightingData;
|
||
ToonSurfaceData surfaceData;
|
||
InitAllData(input, facing, faceArea, uvData, lightingData, surfaceData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// insert debug shading early exit section here
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
#if _NILOTOON_DEBUG_SHADING
|
||
outColor = Get_NILOTOON_DEBUG_SHADING_result(input, surfaceData, lightingData, uvData);
|
||
return;
|
||
#endif
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply struct ToonSurfaceData's edit (using lightingData's data also)
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// (apply first) edit albedo
|
||
ApplyDynamicEye(surfaceData, input, lightingData);
|
||
ApplyMatCapColorReplace(surfaceData, input, lightingData);
|
||
// (apply later) also edit albedo, by reflection features
|
||
ApplyEnvironmentReflections(surfaceData, input, lightingData);
|
||
ApplyMatCapAdditive(surfaceData, input, lightingData);
|
||
// (apply last) replace albedo for back face
|
||
ApplyBackFaceReplaceBaseMapColorIfForwardLitPass(surfaceData, lightingData);
|
||
|
||
// edit occlusion
|
||
ApplyMatCapOcclusion(surfaceData, input, lightingData);
|
||
ApplyBackFaceForceShadowIfForwardLitPass(surfaceData, lightingData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply URP decal
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
ApplyURPDecal(surfaceData, input, lightingData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply pre-lighting outline color calculation
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
ApplyAlbedoPreLightingEditIfOutlinePass(surfaceData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply all lighting calculation
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
half3 color = ShadeAllLights(surfaceData, lightingData, input, uvData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply outline color calculation
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
ApplySurfaceToClassicOutlineColorEditIfOutlinePass(color, surfaceData, lightingData);
|
||
ApplySurfaceToScreenSpaceOutlineColorEditIfSurfacePass(color, surfaceData, lightingData);
|
||
ApplySurfaceToScreenSpaceOutlineV2ColorEditIfSurfacePass(color, surfaceData, lightingData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply per "material>character>volume" color edit
|
||
// (Apply this section after outline, due to outline color user settings are usually for the original color)
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply order = from local to global (per material > per character > per volume(global))
|
||
ApplyBackFaceColorTintIfForwardLitPass(color, input, lightingData, facing);
|
||
ApplyPostLightingPerCharacterBaseMapOverride(color, uvData);
|
||
ApplyPerCharacterEffectColorEdit(color, lightingData);
|
||
ApplyVolumeEffectColorEdit(color);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply dissolve
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
ApplyDissolve(color, input, lightingData, uvData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// apply fog (with extend logic functions before and after fog)
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
ApplyExternalAssetSupportLogicBeforeFog(color, surfaceData, lightingData, input, uvData);
|
||
ApplyCustomUserLogicBeforeFog(color, surfaceData, lightingData, input, uvData);
|
||
ApplyFog(color, input);
|
||
ApplyExternalAssetSupportLogicAfterFog(color, surfaceData, lightingData, input, uvData);
|
||
ApplyCustomUserLogicAfterFog(color, surfaceData, lightingData, input, uvData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// override output alpha
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
ApplyOverrideOutputAlpha(surfaceData.alpha);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// Inverse Tonemapping
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// we can apply inverse tonemap to color inorder to use URP's tonemapping, but it will make bloom extremely bright, so likely not a good idea
|
||
// see https://zhuanlan.zhihu.com/p/14603997646?utm_psn=1857106055890354177&fbclid=IwZXh0bgNhZW0CMTAAAR26OV4Aooye9-I3cRDhDAeokolYIMjF6jxR1G-qNzGE6UDxRJwUjdIWuRQ_aem_tTvZmD9FNdQYSqUf7pE2Kw
|
||
/*
|
||
// Inverse for Unity's official Neutral
|
||
float3 InvTonemap_Neutral4(float3 RGB)
|
||
{
|
||
float3 t = RGB*RGB;
|
||
return 5.2f*t*t - 3.0f*t*RGB + 0.7f*t + RGB;
|
||
}
|
||
// Inverse for Unity's official ACES
|
||
float3 InvTonemap_ACES(float3 RGB)
|
||
{
|
||
float3 t = RGB * RGB;
|
||
return (0.5f*RGB*t + 0.3f*t + 0.7f*RGB + 0.45f) * sqrt(RGB);
|
||
}
|
||
*/
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// output
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
outColor = half4(color, surfaceData.alpha);
|
||
|
||
// pre-multiply alpha option
|
||
outColor.rgb *= _PreMultiplyAlphaIntoRGBOutput ? outColor.a : 1;
|
||
|
||
#ifdef _WRITE_RENDERING_LAYERS
|
||
// Unity6.2 changed API
|
||
#if UNITY_VERSION >= 60020000
|
||
outRenderingLayers = EncodeMeshRenderingLayer();
|
||
#else
|
||
uint renderingLayers = GetMeshRenderingLayer();
|
||
outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0);
|
||
#endif
|
||
#endif
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions
|
||
// (only for ShadowCaster pass, DepthOnly and NiloToonSelfShadowCaster pass to use only)
|
||
// (not used in other pass)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
half BaseColorAlphaClipTest(Varyings input, FRONT_FACE_TYPE IsFrontFace : FRONT_FACE_SEMANTIC) : SV_TARGET
|
||
{
|
||
#if !NiloToonCharSelfShadowCasterPass
|
||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||
#endif
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// flip normalWS by IS_FRONT_VFACE()
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
float facing = IS_FRONT_VFACE(IsFrontFace, 1.0, -1.0);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// Init all data structs
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
UVData uvData;
|
||
ToonLightingData lightingData;
|
||
ToonSurfaceData surfaceData;
|
||
InitAllData(input, facing, 0, uvData, lightingData, surfaceData);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
// if _AlphaClip is off, there is no reason to call GetFinalBaseColor()
|
||
// so here we use #if _ALPHATEST_ON to avoid calling GetFinalBaseColor() as an optimization
|
||
#if _ALPHATEST_ON
|
||
DoClipTestToTargetAlphaValue(GetFinalBaseColor(input,uvData, facing).a);
|
||
#endif
|
||
|
||
// dither fade out should affect URP's shadowmap and depth texture / depth normal texture also
|
||
#if _NILOTOON_DITHER_FADEOUT
|
||
NiloDoDitherFadeoutClip(input.positionCS.xy, 1-_DitherFadeoutAmount*_AllowPerCharacterDitherFadeout);
|
||
#endif
|
||
|
||
// dissolve should affect URP's shadowmap and depth texture / depth normal texture also
|
||
half3 dummy = half3(0,0,0);
|
||
ApplyDissolve(dummy,input,lightingData, uvData);
|
||
|
||
return input.positionCS.z;
|
||
}
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions
|
||
// (only for DepthNormal pass to use only)
|
||
// (not used in other pass)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
void BaseColorAlphaClipTest_AndDepthNormalColorOutput(Varyings input, FRONT_FACE_TYPE IsFrontFace : FRONT_FACE_SEMANTIC
|
||
, out half4 outNormalWS : SV_Target0
|
||
#ifdef _WRITE_RENDERING_LAYERS
|
||
// Unity6.2 changed float4 -> uint
|
||
#if UNITY_VERSION >= 60020000
|
||
, out uint outRenderingLayers : SV_Target1
|
||
#else
|
||
, out float4 outRenderingLayers : SV_Target1
|
||
#endif
|
||
#endif
|
||
)
|
||
{
|
||
#if !NiloToonCharSelfShadowCasterPass
|
||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||
#endif
|
||
BaseColorAlphaClipTest(input, IsFrontFace);
|
||
|
||
#if defined(_GBUFFER_NORMALS_OCT)
|
||
float3 normalWS = normalize(input.normalWS_averageShadowAttenuation.xyz);
|
||
float2 octNormalWS = PackNormalOctQuadEncode(normalWS); // values between [-1, +1], must use fp32 on some platforms.
|
||
float2 remappedOctNormalWS = saturate(octNormalWS * 0.5 + 0.5); // values between [ 0, 1]
|
||
half3 packedNormalWS = PackFloat2To888(remappedOctNormalWS); // values between [ 0, 1]
|
||
outNormalWS = half4(packedNormalWS, 0.0);
|
||
#else
|
||
float3 normalWS = NormalizeNormalPerPixel(input.normalWS_averageShadowAttenuation.xyz);
|
||
outNormalWS = half4(normalWS, 0.0);
|
||
#endif
|
||
|
||
// TODO: should we apply?
|
||
//outNormalWS *= saturate(1 - _DitherFadeoutAmount * _AllowPerCharacterDitherFadeout * _DitherFadeoutNormalScaleFix);
|
||
|
||
#ifdef _WRITE_RENDERING_LAYERS
|
||
// Unity6.2 changed API
|
||
#if UNITY_VERSION >= 60020000
|
||
outRenderingLayers = EncodeMeshRenderingLayer();
|
||
#else
|
||
uint renderingLayers = GetMeshRenderingLayer();
|
||
outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0);
|
||
#endif
|
||
#endif
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// fragment shared functions
|
||
// (only for NiloToonPrepassBuffer pass to use only)
|
||
// (not used in other pass)
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
float4 BaseColorAlphaClipTest_AndNiloToonPrepassBufferColorOutput(Varyings input, FRONT_FACE_TYPE IsFrontFace : FRONT_FACE_SEMANTIC) : SV_TARGET
|
||
{
|
||
BaseColorAlphaClipTest(input, IsFrontFace);
|
||
|
||
// use the same method to calculate linear depth from self(SV_POSITION.z) & scene(Load(_CameraDepthTexture))
|
||
float sceneLinearDepth = Convert_SV_PositionZ_ToLinearViewSpaceDepth(LoadSceneDepth(input.positionCS.xy));
|
||
float selfLinearDepth = Convert_SV_PositionZ_ToLinearViewSpaceDepth(input.positionCS.z);
|
||
|
||
// When the RT depth and MSAA format of _CameraDepthTexture match _NiloToonPrepassBufferRT,
|
||
// we can perform precise depth comparisons to reject "scene blocked char pixels."
|
||
// but still, it is possible to have problems similar Shadow acne, so a very small depth bias may help
|
||
// Bias should not be affected by platform, since platform difference (Z reverse) is handled inside Convert_SV_PositionZ_ToLinearViewSpaceDepth()'s LinearEyeDepth()
|
||
// DX11 = - correct , + wrong
|
||
// Vulkan = - correct , + wrong
|
||
// OPENGLES = - correct , + wrong
|
||
// -0.00003 is the minimum number to work for a 24bit depth texture
|
||
// no reasonable value works for 16bit depth texture (should we allow user to increase it? expose in renderer feature?)
|
||
float depthBias = - 0.0001; // we use a "~3.3x larger than -0.00003" bias just in case
|
||
|
||
// Add gradient bias for edges to fix pixel holes
|
||
float2 depthGradient = float2(ddx(selfLinearDepth), ddy(selfLinearDepth));
|
||
float gradientBias = length(depthGradient) * 0.5;
|
||
depthBias = depthBias - gradientBias;
|
||
|
||
if(sceneLinearDepth < selfLinearDepth + depthBias)
|
||
return 0;
|
||
|
||
float isFace = GetFaceArea(input.uv01.xy);
|
||
|
||
// if character pixel is still visible(not blocked by scene), draw to _NiloToonPrepassBufferTex
|
||
return float4(isFace,_AllowNiloToonBloomCharacterAreaOverride * _AllowedNiloToonBloomOverrideStrength,0,1);
|
||
} |