URP 简单卡渲

 使用unity版本为2022.2.21f1

shader文件

Shader "Unlit/SRUniversal"
{
    Properties
    {
        [KeywordEnum(None,Face,Hair,UpperBody,LowerBody)] _Area("Material area", Float) = 0
        [HideInInspector] _HeadForward("", Vector) = (0,0,1)
        [HideInInspector] _HeadRight("", Vector) = (1,0,0)

        [Header(Base Color)]
        [HideInInspector] _BaseMap("", 2D) = "white"{}
        [NoScaleOffset] _FaceColorMap("Face color map (Default white)", 2D) = "white"{}
        [NoScaleOffset] _HairColorMap("Hair color map (Default white)", 2D) = "white"{}
        [NoScaleOffset] _UpperBodyColorMap("Upper body color map (Default white)", 2D) = "white"{}
        [NoScaleOffset] _LowerBodyColorMap("Lower body color map (Default white)", 2D) = "white"{}
        _FrontFaceTintColor("Front face tint color (Default white)", Color) = (1,1,1)
        _BackFaceTintColor("Back face tint color (Default white)", Color) = (1,1,1)
        _Alpha("Alpha (Default 1)", Range(0, 1)) = 1
        _AlphaClip("Alpha clip (Default 0.333)", Range(0, 1)) = 0.333

        [Header(Light Map)]
        [NoScaleOffset] _HairLightMap("Hair light map (Default black)", 2D) = "black"{}
        [NoScaleOffset] _UpperBodyLightMap("Upper body light map (Default black)", 2D) = "black"{}
        [NoScaleOffset] _LowerBodyLightMap("Lower body light map (Default black)", 2D) = "black"{}

        [Header(Ramp Map)]
        [NoScaleOffset] _HairCoolRamp("Hair cool ramp (Default white)", 2D) = "white"{}
        [NoScaleOffset] _HairWarmRamp("Hair warm ramp (Default white)", 2D) = "white"{}
        [NoScaleOffset] _BodyCoolRamp("Body cool ramp (Default white)", 2D) = "white"{}
        [NoScaleOffset] _BodyWarmRamp("Body warm ramp (Default white)", 2D) = "white"{}

        [Header(Indirect Lighting)]
        _IndirectLightFlattenNormal("Indirect light flatten normal (Default 0)", Range(0, 1)) = 0
        _IndirectLightUsage("Indirect light usage (Default 0.5)", Range(0, 1)) = 0.5
        _IndirectLightOcclusionUsage("Indirect light occlusion usage (Default 0.5)", Range(0, 1)) = 0.5
        _IndirectLightMixBaseColor("Indirect light base color (Default 1)", Range(0, 1)) = 1

        [Header(Main Lighting)]
        _MainLightColorUsage("Main light color usage (Default 1)", Range(0, 1)) = 1
        _ShadowThresholdCenter("Shadow threshold center (Default 0)", Range(-1, 1)) = 0
        _ShadowThresholdSoftness("Shadow threshold softness (Default 0.1)", Range(0, 1)) = 0.1
        _ShadowRampOffset("Shadow ramp offset (Default 0.75)", Range(0, 1)) = 0.75

        [Header(Face)]
        [NoScaleOffset] _FaceMap("Face map (Default black)", 2D) = "black"{}
        _FaceShadowOffset("Face shadow offset (Default -0.01)", Range(-1, 1)) = -0.01
        _FaceShadowTransitionSoftness("Face shadow transition softness (Default 0.05)", Range(0, 1)) = 0.05

        [Header(Specular)]
        _SpecularExpon("Specular exponent (Default 50)", Range(1, 128)) = 50
        _SpecularKsNonMetal("Specular Ks non-metal (Default 0.04)", Range(0, 1)) = 0.04
        _SpecularKsMetal("Specular Ks metal (Default 1)", Range(0, 1)) = 1
        _SpecularBrightness("Specular brightness (Default 1)", Range(0, 10)) = 1

        [Header(Sockings)]
        [NoScaleOffset] _UpperBodyStockings("Upper body stockings (Default black)", 2D) = "black"{}
        [NoScaleOffset] _LowerBodyStockings("Lower body stockings (Default black)", 2D) = "black"{}
        _StockingsDarkColor("Stocking dark color (Default black)", Color) = (0,0,0)
        [HDR] _StockingsLightColor("Stocking light color (Default 1.8 1.48299 0.856821)", Color) = (1.8, 1.48299, 0.856821)
        [HDR] _StockingsTransitionColor("Stocking Transition color (Default 0.360381 0.242986 0.358131)", Color) = (0.360381, 0.242986, 0.358131)
        _StockingsTransitionThreshold("Stocking transition threshold (Default 0.58)", Range(0, 1)) = 0.58
        _StockingsTransitionPower("Stocking transition Power (Default 1)", Range(0.1, 50)) = 1
        _StockingsTransitionHardness("Stocking transition hardness (Default 0.4)", Range(0, 1)) = 0.4
        _StockingsTextureUsage("Stocking texture usage (Default 0.1)", Range(0, 1)) = 0.1

        [Header(Rim Lighting)]
        _RimLightWidth("Rim light width (Default 1)", Range(0, 10)) = 1
        _RimLightThreshold("Rim light threshold (Default 0.05)", Range(-1, 1)) = 0.05
        _RimLightFadeout("Rim light fadeout (Default 1)", Range(0.01, 1)) = 1
        [HDR] _RimLightTintColor("Rim light tint color (Default white)", Color) = (1,1,1)
        _RimLightBrightness("Rim light brightness (Default 1)", Range(0, 10)) = 1
        _RimLightMixAlbedo("Rim light mix albedo (Default 0.9)", Range(0, 1)) = 0.9

        [Header(Emission)]
        [Toggle(_EMISSION_ON)] _UseEmission("Use emission (Default False)", Float) = 0
        _EmissionMixBaseColor("Emission mix base color (Default 1)", Range(0, 1)) = 1
        _EmissionTintColor("Emission tint color (Default white)", Color) = (1,1,1)
        _EmissionIntensity("Emission intensity (Default 1)", Range(0, 100)) = 1

        [Header(Outline)]
        [Toggle(_OUTLINE_ON)] _UseOutline("Use outline (Default True)", Float) = 1
        [Toggle(_OUTLINE_VERTEX_COLOR_SMOOTH_NORMAL)] _OutlineUseVertexColorSmoothNormal("Use vertex color smooth normal (Default False)", Float) = 0
        _OutlineWidth("Outline width (Default 1)", Range(0, 10)) = 1
        _OutlineGamma("Outline gamma (Default 16)", Range(1, 255)) = 16

        [Header(Surface Options)]
        [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull (Default back)", Float) = 2
        [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlendMode("Src blend mode (Default One)", Float) = 1
        [Enum(UnityEngine.Rendering.BlendMode)] _DstBlendMode("Dst blend mode (Default Zero)", Float) = 0
        [Enum(UnityEngine.Rendering.BlendOp)] _BlendOp("Blend operation (Default Add)", Float) = 0
        [Enum(Off, 0, On, 1)] _ZWrite("ZWrite (Default On)", Float) = 1
        _StencilRef("Stencil reference (Default 0)", Range(0, 255)) = 0
        [Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil comparison (Default disabled)", Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilPassOp("Stencil pass operation (Default keep)", Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilFailOp("Stencil fail operation (Default keep)", Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilZFailOp("Stencil Z fail operation (Default keep)", Int) = 0

        [Header(Draw Overlay)]
        [Toggle(_DRAW_OVERLAY_ON)] _UseDrawOverlay("Use draw overlay (Default False)", Float) = 0
        [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlendModeOverlay("Overlay pass src blend mode (Default One)", Float) = 1
        [Enum(UnityEngine.Rendering.BlendMode)] _DstBlendModeOverlay("Overlay pass src blend mode (Default Zero)", Float) = 0
        [Enum(UnityEngine.Rendering.BlendOp)] _BlendOpOverlay("Overlay pass blend operation (Default Add)", Float) = 0
        _StencilRefOverlay("Overlay pass stencil reference (Default 0)", Range(0, 255)) = 0
        [Enum(UnityEngine.Rendering.CompareFunction)] _StencilCompOverlay ("Overlay pass stencil comparison (Default disabled)", Int) = 0
    }
    SubShader
    {
        LOD 100

        HLSLINCLUDE
         HLSL code that you want to share goes here
        #pragma shader_feature_local _AREA_FACE
        #pragma shader_feature_local _AREA_HAIR
        #pragma shader_feature_local _AREA_UPPERBODY
        #pragma shader_feature_local _AREA_LOWERBODY
        #pragma shader_feature_local _OUTLINE_ON
        #pragma shader_feature_local _OUTLINE_VERTEX_COLOR_SMOOTH_NORMAL
        #pragma shader_feature_local _DRAW_OVERLAY_ON
        #pragma shader_feature_local _EMISSION_ON
        ENDHLSL


        Pass
        {
            Name "ShadowCaster"
            Tags{"LightMode" = "ShadowCaster"}  //  阴影贴图

            ZWrite [_ZWrite]
            ZTest LEqual
            ColorMask RGBA
            Cull [_Cull]

            HLSLPROGRAM
            //#pragma exclude_renders gles gles3 glcore
            #pragma targe 4.5

            // -------------------------------------------------------------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            // -------------------------------------------------------------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma multi_compile _ DOTS_INSTANCING_ON

            // -------------------------------------------------------------------------------------------
            // Universial Pipeline keywords

            // this is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"

            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags{"LightMode" = "DepthOnly" "Queue" = "Transparent" }  //  深度贴图

            ZWrite [_ZWrite]
            ColorMask RGBA
            Cull [_Cull]

            HLSLPROGRAM
            //#pragma exclude_renders gles gles3 glcore
            #pragma targe 4.5

            // -------------------------------------------------------------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            // -------------------------------------------------------------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma multi_compile _ DOTS_INSTANCING_ON

            #pragma Vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"

            ENDHLSL
        }

        Pass
        {
            Name "DepthNormals"
            Tags{"LightMode" = "DepthNormals"}  //  向SSAO传输值

            ZWrite [_ZWrite]
            Cull [_Cull]

            HLSLPROGRAM
            //#pragma exclude_renders gles gles3 glcore
            #pragma targe 4.5

            // -------------------------------------------------------------------------------------------
            // Material Keywords
            #pragma shader_feature_local _NORMALMAP
            #pragma shader_feature_local _PARALLAXMAP
            #pragma shader_feature_local _ _DETAIL_MULTX2 _DETAIL_SCALED
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            // -------------------------------------------------------------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma multi_compile _ DOTS_INSTANCING_ON

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitDepthNormalsPass.hlsl"

            ENDHLSL
        }

        Pass
        {
            Name "DrawCore"
            Tags{ "RenderPipeline" = "UniversalRenderPipeline" "RenderType" = "Opaque" "LightMode" = "SRPDefaultUnlit"}

            Cull [_Cull]
            Stencil{
                Ref [_StencilRef]           //这是传进去的int值(默认0【0,255】注意这是二进制11111111),如果在测试就用这个值和模板值(如果没有写入过默认0)作比较,受readMask影响、如果在写入就是写入这个值,受writeMask影响
                Comp [_StencilComp]         //何时通过模板测试,这个和写入没有任何关系,还有CompBack和CompFront专门针对背面和正面,如果存在Comp则会覆盖他们,值为:Never、Less、Equal、LEqual、Greater、NotEqual、GEqual、Always
                Pass [_StencilPassOp]       //设置对通过模板测试的像素怎么处理,还有PassBack和PassFront专门针对背面和正面,如果存在Pass则会覆盖他们,值为:Keep(保持原值)、Zero(写入模板值0)、Replace(写入Ref值)、IncrSat(加上去但不超过255)、DecrSat(相减但不小于0)、Invert(所有位取反)、IncrWrap(加上去但超过255会从0重新开始)、DecrWrap(相减但小于0会从255重新开始)
                Fail [_StencilFailOp]       //设置对未通过模板测试的像素怎么处理,还有FailBack和FailFront专门针对背面和正面,如果存在Fail则会覆盖他们,值和Pass的相同
                ZFail [_StencilZFailOp]     //当像素通过模板测试但未通过深度测试时怎么处理,还有ZFailBack和ZFailFront专门针对背面和正面,如果存在ZFail则会覆盖他们,值和Pass的相同
            }
            Blend [_SrcBlendMode] [_DstBlendMode]
            BlendOp [_BlendOp]
            ZWrite [_ZWrite]

            HLSLPROGRAM
            //#pragma exclude_renders gles gles3 glcore
            #pragma targe 4.5
            #pragma multi_compile _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _SHADOW_SOFT

            #pragma vertex vert
            #pragma fragment frag

            #pragma multi_compile_fog

            #include "SRUniversalInput.hlsl"
            #include "SRUniversalDrawCorePass.hlsl"

            ENDHLSL
        }

        Pass
        {
            Name "DrawOutline"
            Tags{ "RenderPipeline" = "UniversalRenderPipeline" "RenderType" = "Opaque" "LightMode" = "UniversalForwardOnly" }
            Cull Front
            ZWrite[_ZWrite]

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #pragma multi_compile_fog

            #if _OUTLINE_ON
            #include "SRUniversalInput.hlsl"
            #include "SRUniversalDrawOutlinePass.hlsl"
            #else

            struct Attribute {};

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
            };

            Varyings vert(Attribute input)
            {
                return (Varyings)0;
            }

            float4 frag(Varyings input) : SV_TARGET
            {
                return 0;
            }

            #endif
            ENDHLSL
            
        }
    }
        Fallback Off
}

hlsl文件

SRUniversalInput

#ifndef _SR_UNIVERSAL_INPUT_INCLUDED
#define _SR_UNIVERSAL_INPUT_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
/*
包含VertexPositionInputs、VertexNormalInputs结构以及各种基础定义  #define TEXTURE2D_X(textureName)  TEXTURE2D_ARRAY(textureName)
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
约定:
unity在世界空间是左手,但是在视图空间为右手。世界到视图空间的矩阵行列式是负的
对于cubmap捕获(反射探针),视图空间仍是左手,行列式为正
	//WS:世界空间
	//RWS:相机相对世界空间。为了提高精度,已经减去了相机平移的空间
	//VS:视图空间
	//OS:对象空间
	//CS:同构剪辑空间
	//TS:切线空间
	//TXS:纹理空间
大写字母矢量都指向像素位置且归一View vector、Light vector、Light vector、Half vector
out和inout过滤器,当声明函数的“out”参数时,它们总是最后一个
不要使用SetGlobalFloatArray或SetComputeFloatParams。//数组可以是hlsl中的别名。示例:uniform float4-packedArray[3];static float unpackedArray[12]=(float[12])packedArray;
*包含非常多的常用基本函数定义PackHeightmap、GetPositionInput、SinFromCos、Orthonormalize等等*
-----------------------------------------------------------------------------------------------------------------
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl"
Unpack888UIntToFloat2(uint3 x)、Unpack8ToFloat2(float f)、PackToR5G6B5(float3 rgb) 看起来是各种格式互转的方法
-----------------------------------------------------------------------------------------------------------------
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Version.hlsl"
UNITY_VERSION宏
-----------------------------------------------------------------------------------------------------------------
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
InputData结构体、很多常量输入(应该是unity给值,直接拿那种)比如_MainLightPosition
-----------------------------------------------------------------------------------------------------------------
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl"
各种各样的方法,GetWorldSpaceViewDir之类的获取各种输入结构、LinearDepthToEyeDepth变换方法、TransformScreenUV(inout float2 uv)甚至编码解码什么的
*/
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
/*
各种光照方法LightingLambert、LightingSpecular、LightingPhysicallyBased、VertexLighting、CalculateLightingColor、CalculateBlinnPhong、UniversalFragmentPBR、UniversalFragmentBlinnPhong、UniversalFragmentBakedLit
结构体LightingData获取各种光照信息
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/BRDF.hlsl"  光反射模型
结构体BRDFData、方法:InitializeBRDFData、EnvironmentBRDFSpecular、DirectBRDFSpecular
	{
	#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/BSDF.hlsl"  包括BRDF反射、BTDF透射、BDDF衍射
	#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
	方法:ClampRoughnessForRaytracing、BlendNormal、ConvertRoughnessToAnisotropy材质属性(粗糙度)相关的
	#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceData.hlsl"
	定义SurfaceData结构体,是物体表面的各种属性摩擦、金属、间接光遮....
	}
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Debug/Debugging3D.hlsl"
方法:CanDebugOverrideOutputColor、CalculateColorForDebugMaterial看起来都是debug用的方法
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/GlobalIllumination.hlsl"
方法:SampleProbeVolumePixel、SampleLightmap、GlossyEnvironmentReflection、GlobalIllumination都是一些全局照明所需要的方法
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RealtimeLights.hlsl"
定义Light结构、方法:灯光衰减DistanceAttenuation、GetMainLight等
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/AmbientOcclusion.hlsl"
定义AmbientOcclusionFactor结构,GetScreenSpaceAmbientOcclusion屏幕空间环境光遮挡
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl" 看起来是贴花相关的东西
*/
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
//只是声明深度图,附带采样、读取方法SampleSceneDepth、LoadSceneDepth
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
//阴影相关,定义ShadowSamplingData结构体,GetMainLightShadowSamplingData、GetMainLightShadowParams、SampleShadowmap、ApplyShadowBias


/*
像这样做就是放在常量缓冲区,下面这种做法是unity宏,确保在支持时使用它们
cbuffer UnityPerFrame {
	float4x4 unity_MatrixVP;
};
*/
CBUFFER_START(UnityPerMaterial)
float3 _HeadForward;
float3 _HeadRight;

sampler2D _BaseMap;
float4 _BaseMap_ST;

#if _AREA_FACE
	sampler2D _FaceColorMap;
#elif _AREA_HAIR
	sampler2D _HairColorMap;
#elif _AREA_UPPERBODY
	sampler2D _UpperBodyColorMap;
#elif _AREA_LOWERBODY
	sampler2D _LowerBodyColorMap;
#endif

float3 _FrontFaceTintColor;
float3 _BackFaceTintColor;

float _Alpha;
float _AlphaClip;

#if _AREA_HAIR
	sampler2D _HairLightMap;
#elif _AREA_UPPERBODY
	sampler2D _UpperBodyLightMap;
#elif _AREA_LOWERBODY
	sampler2D _LowerBodyLightMap;
#endif

#if _AREA_HAIR
	sampler2D _HairCoolRamp;
	sampler2D _HairWarmRamp;
#elif _AREA_FACE || _AREA_UPPERBODY || _AREA_LOWERBODY
	sampler2D _BodyCoolRamp;
	sampler2D _BodyWarmRamp;
#endif

float _IndirectLightFlattenNormal;
float _IndirectLightUsage;
float _IndirectLightOcclusionUsage;
float _IndirectLightMixBaseColor;

float _MainLightColorUsage;
float _ShadowThresholdCenter;
float _ShadowThresholdSoftness;
float _ShadowRampOffset;

#if _AREA_FACE
	sampler2D _FaceMap;
	float _FaceShadowOffset;
	float _FaceShadowTransitionSoftness;
#endif

#if _AREA_HAIR || _AREA_UPPERBODY || _AREA_LOWERBODY
	float _SpecularExpon;
	float _SpecularKsNonMetal;
	float _SpecularKsMetal;
	float _SpecularBrightness;
#endif

#if _AREA_UPPERBODY || _AREA_LOWERBODY
	#if _AREA_UPPERBODY
		sampler2D _UpperBodyStockings;
	#elif _AREA_LOWERBODY
		sampler2D _LowerBodyStockings;
	#endif
	float3 _StockingsDarkColor;
	float3 _StockingsLightColor;
	float3 _StockingsTransitionColor;
	float _StockingsTransitionThreshold;
	float _StockingsTransitionPower;
	float _StockingsTransitionHardness;
	float _StockingsTextureUsage;
#endif

float _RimLightWidth;
float _RimLightThreshold;
float _RimLightFadeout;
float3 _RimLightTintColor;
float _RimLightBrightness;
float _RimLightMixAlbedo;

#if _OUTLINE_ON
	float _OutlineWidth;
	float _OutlineGamma;
#endif

CBUFFER_END

#endif

 SRUniversalDrawOutlinePass

struct Attributes 
{
    float3 positionOS   : POSITION;
    float3 normalOS     : NORMAL;
    float4 tangentOS    : TANGENT;
    float4 color        : COLOR;
    float4 uv           : TEXCOORD0;
};

struct Varyings
{
    float4 positionCS               : SV_POSITION;
    float2 uv                       : TEXCOORD0;
    float fogFactor                 : TEXCOORD1;
    float4 color                    : TEXCOORD2;

};

float GetCameraFOV()
{
    //https://answers.unity.com/questions/770838/how-can-i-extract-the-fov-information-from-the-pro.html
    float t = unity_CameraProjection._m11;
    float Rad2Deg = 180 / 3.1415;
    float fov = atan(1.0f / t) * 2.0 * Rad2Deg;
    return fov;
}
float ApplyOutlineDistanceFadeOut(float inputMulFix)
{
    //make outline "fadeout" if character is too small in camera's view
    return saturate(inputMulFix);
}
float GetOutlineCameraFovAndDistanceFixMultiplier(float positionVS_Z)
{
    float cameraMulFix;
    if(unity_OrthoParams.w == 0)
    {
        
        // Perspective camera case
        

        // keep outline similar width on screen accoss all camera distance       
        cameraMulFix = abs(positionVS_Z);

        // can replace to a tonemap function if a smooth stop is needed
        cameraMulFix = ApplyOutlineDistanceFadeOut(cameraMulFix);

        // keep outline similar width on screen accoss all camera fov
        cameraMulFix *= GetCameraFOV();       
    }
    else
    {
        
        // Orthographic camera case
        
        float orthoSize = abs(unity_OrthoParams.y);
        orthoSize = ApplyOutlineDistanceFadeOut(orthoSize);
        cameraMulFix = orthoSize * 50; // 50 is a magic number to match perspective camera's outline width
    }

    return cameraMulFix * 0.00005; // mul a const to make return result = default normal expand amount WS
}

Varyings vert(Attributes input)
{
    Varyings output = (Varyings)0;

    VertexPositionInputs vertexPositionInput = GetVertexPositionInputs(input.positionOS);
    VertexNormalInputs vertexNormalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);

    float width = _OutlineWidth;
    width *= GetOutlineCameraFovAndDistanceFixMultiplier(vertexPositionInput.positionVS.z);

    float3 positionWS = vertexPositionInput.positionWS;
    positionWS += vertexNormalInput.normalWS * width;
    output.positionCS = TransformWorldToHClip(positionWS);

    output.uv = TRANSFORM_TEX(input.uv, _BaseMap);  //  加偏移
    output.fogFactor = ComputeFogFactor(vertexPositionInput.positionCS.z);

    return output;
}

float4 frag(Varyings input) : SV_TARGET
{
    //描边加颜色,用的采样出来的Ramp
    float3 coolRamp = 0;
    float warmRamp = 0;

    #if _AREA_HAIR
        float2 outlineUV = float2(0, 0.5);
        coolRamp = tex2D(_HairCoolRamp, outlineUV).rgb;
        warmRamp = tex2D(_HairWarmRamp, outlineUV).rgb;
    #elif _AREA_UPPERBODY || _AREA_LOWERBODY
        float4 lightMap = 0;
        #if _AREA_UPPERBODY
            lightMap = tex2D(_UpperBodyLightMap, input.uv);
        #elif _AREA_LOWERBODY
            lightMap = tex2D(_LowerBodyLightMap, input.uv);
        #endif
        float materialEnum = lightMap.a;
        float materialEnumOffset = materialEnum + 0.0425;
        float outlineUVy = lerp(materialEnumOffset, materialEnumOffset + 0.5 > 1 ? materialEnumOffset + 0.5 - 1 : materialEnumOffset + 0.5 , fmod((round(materialEnumOffset/0.0625) - 1)/2, 2));
        float2 outlineUV = float2(0, outlineUVy);
        coolRamp = tex2D(_BodyCoolRamp, outlineUV).rgb;
        warmRamp = tex2D(_BodyWarmRamp, outlineUV).rgb;
    #elif _AREA_FACE
        float2 outlineUV = float2(0, 0.0625);
        coolRamp = tex2D(_BodyCoolRamp, outlineUV).rgb;
        warmRamp = tex2D(_BodyWarmRamp, outlineUV).rgb;
    #endif

    float3 ramp = lerp(coolRamp, warmRamp, 0.5);
    float3 albedo = pow(saturate(ramp), _OutlineGamma);

    float4 color = float4(albedo,1);
    color.rgb = MixFog(color.rgb, input.fogFactor);

    return color;
}

SRUniversalDrawCorePass

#ifndef _SR_UNIVERSAL_DRAW_CORE_PASS_INCLUDED
#define _SR_UNIVERSAL_DRAW_CORE_PASS_INCLUDED

struct Attributes
{
	float3 positionOS	: POSITION;
	half3 normalOS		: NORMAL;
	half4 tangentOS		: TANGENT;
	float2 uv			: TEXCOORD0;
};

struct Varyings
{
	float2 uv						: TEXCOORD0;
	float4 positionWSAndFogFactor	: TEXCOORD1;  // xyz:positionWS, W:vertex fog factor
	float3 normalWS					: TEXCOORD2;
	float3 viewDirectionWS			: TEXCOORD3;
	float3 SH						: TEXCOORD4;
	float4 positionCS				: SV_POSITION;
};

//去饱和方法
float3 desaturation(float3 colorIn)
{
    float3 grayXfer = float3(0.3, 0.59, 0.11);  //  心理学灰度因子
    float grayf = dot(colorIn, grayXfer);
    return float3(grayf, grayf, grayf);
}

struct Gradient
{
	int colorsLength;
	float4 colors[8];
};

//梯度颜色
Gradient GradientConstruct()
{
	Gradient g;
	g.colorsLength = 2;
	g.colors[0] = float4(1,1,1,0);
	g.colors[1] = float4(1,1,1,1);
	g.colors[2] = float4(0,0,0,0);
	g.colors[3] = float4(0,0,0,0);
	g.colors[4] = float4(0,0,0,0);
	g.colors[5] = float4(0,0,0,0);
	g.colors[6] = float4(0,0,0,0);
	g.colors[7] = float4(0,0,0,0);
	return g;
}

//渐变采样方法
float3 SampleGradient(Gradient Gradient, float Time)
{
    float3 tempColor = Gradient.colors[0].rgb;
    for (int c = 1; c < Gradient.colorsLength; c++)
    {
        float colorPos = saturate((Time - Gradient.colors[c - 1].w) / (Gradient.colors[c].w - Gradient.colors[c - 1].w)) * step(c, Gradient.colorsLength - 1);
        tempColor = lerp(tempColor, Gradient.colors[c].rgb, colorPos);
    }
#ifdef UNITY_COLORSPACE_GAMMA
    tempColor = LinearToSRGB(tempColor);
#endif
	return tempColor;
}

Varyings vert(Attributes input)
{
	Varyings output = (Varyings)0;
	
	//VertexPositionInputs和VertexNormalInputs两个结构体定义在Core.hlsl中,
	VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS);  
	VertexNormalInputs vertexNormalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
	/*
	这两个方法在ShaderVariablesFunctions文件中,用于获取VertexPositionInputs和VertexNormalInputs,它们包括大部分需求的信息
	struct VertexPositionInputs
	{
		float3 positionWS; // World space position
		float3 positionVS; // View space position
		float4 positionCS; // Homogeneous clip space position
		{
			input.positionCS.x:通常表示屏幕上的水平位置。其范围通常从0(屏幕的左边缘)到屏幕宽度减1(屏幕的右边缘,以像素为单位)。
			input.positionCS.y:通常表示屏幕上的垂直位置。其范围通常从0(屏幕的顶部)到屏幕高度减1(屏幕的底部,以像素为单位)。
			input.positionCS.z:通常表示片段在深度缓冲区中的深度值。其范围通常在0.0到1.0之间,表示从近裁剪平面到远裁剪平面的深度值。
			input.positionCS.w:通常表示屏幕空间中的透视除法系数。在一般情况下,你不太会直接使用它来进行计算。
		}
		float4 positionNDC;// Homogeneous normalized device coordinates
		{
			positionNDC.x:通常表示裁剪坐标系中的X坐标,其范围从-1表示裁剪体积的左侧边界,到1表示裁剪体积的右侧边界。
			positionNDC.y:通常表示裁剪坐标系中的Y坐标,其范围从-1表示裁剪体积的底部边界,到1表示裁剪体积的顶部边界。
			positionNDC.z:通常表示裁剪坐标系中的Z坐标,其范围从-1表示裁剪体积的近裁剪平面,到1表示裁剪体积的远裁剪平面。
			positionNDC.w:通常用于执行透视除法,将坐标从裁剪坐标系转换为标准化设备坐标系。它在顶点着色器中通常为非零值,但在片段着色器中可能会变为零,因为透视除法是在顶点着色器中执行的。
		}
	};
	struct VertexNormalInputs
	{
		real3 tangentWS;
		real3 bitangentWS;
		float3 normalWS;
	};
	*/
	output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
	/*
	TRANSFORM_TEX这个宏定义在Macros.hlsl文件中,这个文件中还有还能多定义比如PI:3.1415926、LOG2_E:1.442695
	#define TRANSFORM_TEX(tex, name) ((tex.xy) * name##_ST.xy + name##_ST.zw) 看起来不过是添加ST控制,不需要的话不做这个操作也可以,比如这里,_BaseMap不过是个白图
	*/
	output.positionWSAndFogFactor = float4(vertexInput.positionWS, ComputeFogFactor(vertexInput.positionCS.z));  //  做个组合w分量利用一下,省一点空间
	output.normalWS = vertexNormalInput.normalWS;
	
	output.viewDirectionWS = unity_OrthoParams.w == 0 ? GetCameraPositionWS() - vertexInput.positionWS : GetWorldToViewMatrix()[2].xyz;
	/*
	如果不是ortho,直接坐标相减计算视角向量,如果是ortho,GetWorldToViewMatrix返回的是float4x4 UNITY_MATRIX_V,
	unity_OrthoParams这个量来自StdLib.hlsl文件,这里面也有关于PI的定义,看起来很多量都可以从这里拿
	float4x4 unity_CameraProjection;
	float4x4 unity_MatrixVP;
	float4x4 unity_ObjectToWorld;
	float4x4 unity_WorldToCamera;
	float3 _WorldSpaceCameraPos;
	float4 _ProjectionParams;         // x: 1 (-1 flipped), y: near,     z: far,       w: 1/far
	float4 unity_ColorSpaceLuminance;
	float4 unity_DeltaTime;           // x: dt,             y: 1/dt,     z: smoothDt,  w: 1/smoothDt
	float4 unity_OrthoParams;         // x: width,          y: height,   z: unused,    w: ortho ? 1 : 0
	float4 _ZBufferParams;            // x: 1-far/near,     y: far/near, z: x/far,     w: y/far   or in case of a reversed depth buffer (UNITY_REVERSED_Z is 1)  x = -1+far/near,  y = 1,  z = x/far,  w = 1/far 这里看来是后者
	float4 _ScreenParams;             // x: width,          y: height,   z: 1+1/width, w: 1+1/height
	float4 _Time;                     // x: t/20,           y: t,        z: t*2,       w: t*3
	float4 _SinTime;                  // x: sin(t/20),      y: sin(t),   z: sin(t*2),  w: sin(t*3)
	float4 _CosTime;                  // x: cos(t/20),      y: cos(t),   z: cos(t*2),  w: cos(t*3)
	还有一些方法GradientNoise、LinearEyeDepth。也定义了TRANSFORM_TEX,不止一个地方定义过这个呢
	*/
	output.SH = SampleSH(lerp(vertexNormalInput.normalWS, float3(0,0,0), _IndirectLightFlattenNormal));  //  球谐函数,就是传入法线返回unity保存好的漫反射数据中对应位置的颜色,这里加了参数控制,因为卡渲有时不需要太多细节
	/*
	half3 SampleSH(half3 normalWS)
{
    // LPPV is not supported in Ligthweight Pipeline
    real4 SHCoefficients[7];
    SHCoefficients[0] = unity_SHAr;
    SHCoefficients[1] = unity_SHAg;
    SHCoefficients[2] = unity_SHAb;
    SHCoefficients[3] = unity_SHBr;
    SHCoefficients[4] = unity_SHBg;
    SHCoefficients[5] = unity_SHBb;
    SHCoefficients[6] = unity_SHC;

    return max(half3(0, 0, 0), SampleSH9(SHCoefficients, normalWS));
}
	*/

	output.positionCS = vertexInput.positionCS;

	return output;
}

float4 frag(Varyings input, bool isFrontFace : SV_IsFrontFace): SV_TARGET
{
	float3 positionWS = input.positionWSAndFogFactor.xyz;
	float4 shadowCoord = TransformWorldToShadowCoord(positionWS);
	Light mainLight = GetMainLight(shadowCoord);

	float3 lightDirectionWS = normalize(mainLight.direction);
	float3 normalWS = normalize(input.normalWS);
	float3 viewDirectionWS = normalize(input.viewDirectionWS);

	float3 baseColor = tex2D(_BaseMap, input.uv);  //  baseColor是纯白画布
	float4 areaMap = 0;
	#if _AREA_FACE
		areaMap = tex2D(_FaceColorMap, input.uv);
	#elif _AREA_HAIR
		areaMap = tex2D(_HairColorMap, input.uv);
	#elif _AREA_UPPERBODY
		areaMap = tex2D(_UpperBodyColorMap, input.uv);
	#elif _AREA_LOWERBODY
		areaMap = tex2D(_LowerBodyColorMap, input.uv);
	#endif
	baseColor = areaMap.rgb;
	//baseColor *= lerp((1,1,1), (1,1,1), 0.5);
	baseColor *= lerp(_BackFaceTintColor, _FrontFaceTintColor, isFrontFace);

	float4 lightMap = 0;
	#if _AREA_HAIR || _AREA_UPPERBODY || _AREA_LOWERBODY
	{
		#if _AREA_HAIR
			lightMap = tex2D(_HairLightMap, input.uv);
		#elif _AREA_UPPERBODY
			lightMap = tex2D(_UpperBodyLightMap, input.uv);
		#elif _AREA_LOWERBODY
			lightMap = tex2D(_LowerBodyLightMap, input.uv);
		#endif
	}
	#endif
	float4 faceMap = 0;
	#if _AREA_FACE
		faceMap = tex2D(_FaceMap, input.uv);
	#endif

	//-----------------------------------------------------------------------------------------------------------------
	//间接光
	float3 indirectLightColor = input.SH.rgb * _IndirectLightUsage;
	//  添加光照图细节
	#if _AREA_HAIR || _AREA_UPPERBODY || _AREA_LOWERBODY
		indirectLightColor *= lerp(1, lightMap.r, _IndirectLightOcclusionUsage);
	#elif _AREA_FACE
		indirectLightColor *= lerp(1, lerp(faceMap.g, 1, step(faceMap.r, 0.01)), _IndirectLightOcclusionUsage);
	/*
	注意,step是后面值小于前面值,返回0,反之返回1,别想反了
	step(faceMap.r, 0.01)分出来是否受距离影响的区域 根据图片,不受距离影响的区域即r通道有值的部分:为嘴巴,眼睛,眉毛等。值为0,反之受影响的为1
	即lerp(faceMap.g, 1, step(faceMap.r, 0.01))不受距离影响地方r通道有值的部分:用的是faceMap.g的值,受距离影响的r通道没有值的部分是1,也就是正常漫反射光
	但是g通道有一部分本身即是1,那么根据rg通道的差异,得出的r通道覆盖的区域减去g通道覆盖的区域,也就是只有口部分是0其他部分全为1,这里做这么多只是为了筛选出口部分
	*/
	#endif
	indirectLightColor *= lerp(1, baseColor, _IndirectLightMixBaseColor);
	//-----------------------------------------------------------------------------------------------------------------
	//主光
	float mainLightShadow = 1;
	float3 mainLightColor = lerp(desaturation(mainLight.color), mainLight.color, _MainLightColorUsage);  //  这里做了降饱和插值提升效果,不然直接=mainLight.color;就行
	#if _AREA_HAIR || _AREA_UPPERBODY || _AREA_LOWERBODY
	{
		float NoL = dot(normalWS, lightDirectionWS);  //  计算法相和主光向量的dot  [-1, 1]
		float remapNoL = NoL * 0.5 + 0.5; // [0, 1]
		mainLightShadow = smoothstep(1 - lightMap.g + _ShadowThresholdCenter - _ShadowThresholdSoftness, 1 - lightMap.g + _ShadowThresholdCenter + _ShadowThresholdSoftness, remapNoL);  //  二值化朗伯特
		//  在光照图的g通道,有很多衣服上阴影的细节,接下来添加上去
		mainLightShadow *= lightMap.r;
		//  光照图的r通道,用于标识一些点不亮的也就是所谓的AO区域,直接乘上去,头发上的蝴蝶结就是点不亮的标识区域
	}
	#elif _AREA_FACE
	{
		float3 headForward = normalize(_HeadForward);
		float3 headRight = normalize(_HeadRight);
		float3 headUp = cross(headForward, headRight);

		float3 fixedLightDirectionWS = normalize(lightDirectionWS - dot(lightDirectionWS, headUp) * headUp);  //  看起来是投影,把主光源的光照方向投影在垂直于headUp的平面上
		/*
		投影到垂直于headUp的平面上,ws-up*(|ws.y|/|up|)
		因为ws为单位向量,所以|ws.y| = cosθ、|up| = 1
		所以简化为ws-up*cosθ。而cosθ=  dot(lightDirectionWS, headUp)
		故求出投影到垂直于headUp的平面上的向量
		*/  
		float2 sdfUV = float2(sign(dot(fixedLightDirectionWS, headRight)), 1) * float2(-1, 1) * input.uv;  //  判断光照在左脸(负)还是右脸(正)  float2(-1, 1)是因为sdf图应该左白右黑,所以反向
		/*
		float2(sign(dot(fixedLightDirectionWS, headRight)), 1)如果光照方向是右半就是float2(1,1),反之float2(-1,1)。只需要处理x是1还是-1,因为按照脸部中心对称
		但是这张图是左右反过来的,所以我们再反一次* float2(-1, 1),最后* input.uv。算出脸部修正uv
		但是在光和headRight夹角为90时,也就是光从正后方打来时,看cos曲线为0,由于sign的特性(-1,0,1),也就是说sdfUV的x永远为0
		*/
		float sdfValue = tex2D(_FaceMap, sdfUV).a;  //  如果是光和headRight夹角为90时,看那张图的a通道,采样的都是最左边白色,都是1,所以即使阈值是1也没办法奈何他
		//  然后用这个脸部修正uv采样sdf图获取sdf值

		float sdfThreshold = 1 - (dot(fixedLightDirectionWS, headForward) * 0.5 + 0.5);  //主光越接近正面,阈值越接近0,阈值越低表示越容易被点亮 [0,1]
		sdfThreshold += _FaceShadowOffset;  //  所以为了限制光和headRight夹角为90时sdfUV的x永远为0,采样的都是最左边白色,都是1的情况,把阈值做个小小的偏移,加上一点点0.01就好,只要超过1就行
		float sdf = smoothstep(sdfThreshold - _FaceShadowTransitionSoftness, sdfThreshold + _FaceShadowTransitionSoftness, sdfValue);
		mainLightShadow = lerp(faceMap.g, sdf, step(faceMap.r, 0.01));  //  sdf不应该影响ao区域,
	}
	#endif
	//-----------------------------------------------------------------------------------------------------------------
	//Ramp
	//计算uv
	float2 rampUV;

	int rampRowIndex = 0;
	int rampRowNum = 1;
	#if _AREA_HAIR
	{
		rampRowIndex = 0;
		rampRowNum = 1;
	}
	#elif _AREA_UPPERBODY || _AREA_LOWERBODY
	{
		int rawIndex = (round((lightMap.a + 0.0425)/0.0625) - 1)/2; 
		rampRowIndex = lerp(rawIndex, rawIndex + 4 < 8 ? rawIndex + 4 : rawIndex + 4 - 8, fmod(rawIndex, 2));
		rampRowNum = 8;
	}
	#elif _AREA_FACE
	{
		rampRowIndex = 0;
		rampRowNum = 8;
	}
	#endif
	//  使用预规定的方式计算行数,用于计算采样uv的y坐标,x坐标使用阴影值
	float rampUVy = (2 * rampRowIndex + 1) * (1.0 / (rampRowNum * 2));
	float rampUVx =  mainLightShadow * (1 - _ShadowRampOffset) + _ShadowRampOffset;  //  做个映射,[0.75,1]这张图在0.85左右往后就是白色,我感觉不映射直接用mainLightShadow效果差异也不大
	rampUV = float2(rampUVx, rampUVy);
	//采样
	float3 rampColor;

	float3 coolRamp = 1;
	float3 warmRamp = 1;
	#if _AREA_HAIR
	{
		coolRamp = tex2D(_HairCoolRamp, rampUV).rgb;
		warmRamp = tex2D(_HairWarmRamp, rampUV).rgb;
	}
	#elif _AREA_FACE || _AREA_UPPERBODY || _AREA_LOWERBODY
	{
		coolRamp = tex2D(_BodyCoolRamp, rampUV).rgb;
		warmRamp = tex2D(_BodyWarmRamp, rampUV).rgb;
	}
	#endif
	//根据白天夜晚插值冷暖色
	float isDay = lightDirectionWS.y * 0.5 + 0.5;
	rampColor = lerp(coolRamp, warmRamp, 1);
	//-----------------------------------------------------------------------------------------------------------------
	//修补主光源
	float3 fixedMainLightColor = mainLightColor * baseColor * rampColor;
	//-----------------------------------------------------------------------------------------------------------------
	//高光
	float3 specularColor = 0;
	#if _AREA_HAIR || _AREA_UPPERBODY || _AREA_LOWERBODY
	{
		//布林冯,但是直接这样看起来太光滑
		float3 halfVectorWS = normalize(viewDirectionWS + lightDirectionWS);
		float NoH = dot(normalWS, halfVectorWS);
		float blinnPhong = pow(saturate(NoH), _SpecularExpon);
		//用光照图的蓝色通道作为阈值,超过阈值才会产生高光  这里金属和非金属分别处理
		//非金属 反射率Ks,固定0.04
		float nonMetalSpecular = step(1.0 - blinnPhong, lightMap.b) * _SpecularKsNonMetal;
		//金属 反射率都比较大且差异较大,蓝色通道控制各个金属部分差异
		float metalSpecular = blinnPhong * lightMap.b * _SpecularKsMetal;
		//得有一个lerp控制什么时候是金属什么时候是非金属,在光照图的阿尔法通道,0.52灰度值表示金属
		//找出金属部分,metallic越接近于0就越靠近金属
		float metallic = 0;
		#if _AREA_UPPERBODY || _AREA_LOWERBODY
		{
			metallic = saturate( (1 - 10 * abs(lightMap.a - 0.52)) );  //  金属影响系数,暂定10
		}
		#endif
		specularColor = lerp(nonMetalSpecular, metalSpecular * baseColor, metallic);
		specularColor *= mainLight.color;
		specularColor *= _SpecularBrightness;
	}
	#endif
	//-----------------------------------------------------------------------------------------------------------------
	//黑丝
	float3 stockingsEffect = 1;
	#if _AREA_UPPERBODY || _AREA_LOWERBODY
	{
		//红通道是遮罩,绿通道是丝的透光度,蓝通道是丝的细节,uv放大一定倍数采样可以提升细节量
		//采样
		float2 stockingsMapRG = 0;
		float stockingsMapB = 0;
		#if _AREA_UPPERBODY
		{
			stockingsMapRG = tex2D(_UpperBodyStockings, input.uv).rg;
			stockingsMapB = tex2D(_UpperBodyStockings, input.uv * 20).b;
		}
		#elif _AREA_LOWERBODY
		{
			stockingsMapRG = tex2D(_LowerBodyStockings, input.uv).rg;
			stockingsMapB = tex2D(_LowerBodyStockings, input.uv * 20).b;
		}
		#endif

		//混入细节
		float NoV = dot(normalWS, viewDirectionWS);
		//都不过是为了加控制
		float fac = pow(saturate(NoV), _StockingsTransitionPower);;
		fac = saturate( (fac - _StockingsTransitionHardness/2) / (1 - _StockingsTransitionHardness) );
		fac = fac * (stockingsMapB * _StockingsTextureUsage + (1 - _StockingsTextureUsage));
		fac = lerp(fac, 1, stockingsMapRG.g);
		//构造颜色曲线
		Gradient curve = GradientConstruct();
		curve.colorsLength = 3;
		curve.colors[0] = float4(_StockingsDarkColor, 0);
		curve.colors[1] = float4(_StockingsTransitionColor, _StockingsTransitionThreshold);
		curve.colors[2] = float4(_StockingsLightColor, 1);
		//采样渐变
		float3 stockingsColor = SampleGradient(curve, fac);
		//加遮罩
		stockingsEffect = lerp(1, stockingsColor, stockingsMapRG.r);
	}
	#endif
	//-----------------------------------------------------------------------------------------------------------------
	//鼻尖的描边
    float fakeOutlineEffect = 0;
    float3 fakeOutlineColor = 0;
    #if _AREA_FACE && _OUTLINE_ON
    {
        float4 faceMap = tex2D(_FaceMap, input.uv);
        float fakeOutline = faceMap.b;
        float3 headForward = normalize(_HeadForward);
        fakeOutlineEffect = smoothstep(0.0, 0.25, pow(saturate(dot(headForward, viewDirectionWS)), 20) * fakeOutline);

        float2 outlineUV = float2(0, 0.0625);
        float3 coolRamp = tex2D(_BodyCoolRamp, outlineUV).rgb;
        float3 warmRamp = tex2D(_BodyWarmRamp, outlineUV).rgb;
        float3 ramp = lerp(coolRamp, warmRamp, 0.5);
        fakeOutlineColor = pow(ramp, _OutlineGamma);
    }
    #endif
	//-----------------------------------------------------------------------------------------------------------------
	//边缘光  打开URP深度图
	float linearEyeDepth = LinearEyeDepth(input.positionCS.z, _ZBufferParams) / 10;  //  离相机越近就越黑  正确
	float3 normalVS = mul((float3x3)UNITY_MATRIX_V, normalWS);  //  正确
	float2 uvOffset = float2(sign(normalVS.x), 0) * _RimLightWidth / (1 + linearEyeDepth);  //  正确
	int2 loadTexPos = input.positionCS.xy + uvOffset * _ScaledScreenParams.xy;  //  正确
	float offsetSceneDepth = LoadSceneDepth(loadTexPos) * 5;
	float offsetLinearEyeDepth = LinearEyeDepth(offsetSceneDepth, _ZBufferParams);
	float rimLight = saturate(offsetLinearEyeDepth - (linearEyeDepth + _RimLightThreshold)) / _RimLightFadeout;
	//这里模型的深度图因材质问题搞不定,放置了,之后再说吧
	//-----------------------------------------------------------------------------------------------------------------
	float3 albedo = 0;
	albedo += indirectLightColor;
	albedo += fixedMainLightColor;
	albedo += specularColor;
	albedo *= stockingsEffect;
	albedo = lerp(albedo, fakeOutlineColor, fakeOutlineEffect);

	float alpha = _Alpha;

	//albedo = viewDirectionWS.rgb;
	float4 finalColor = float4(albedo, alpha);
	return finalColor;
}
#endif

 SendVectorToShader脚本

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Mathematics;
using Unity.VisualScripting;
using UnityEngine;

public class SendVectorToShader : MonoBehaviour
{
    public Transform HeadBoneTransform;
    public Transform HeadForwardTransform;
    public Transform HeadRightTransform;

    private Renderer[] allRenderers;

    private int HeadForwardID = Shader.PropertyToID("_HeadForward");
    private int HeadRightID = Shader.PropertyToID("_HeadRight");

#if UNITY_EDITOR
    private void OnValidate()
    {
        LateUpdate();
    }
#endif
    private void LateUpdate()
    {
        if(allRenderers == null)
        {
            allRenderers = GetComponentsInChildren<Renderer>(true);
        }
        Renderer tempRenderer;
        for (int i = 0; i < allRenderers.Count(); i++)
        {
            tempRenderer = allRenderers[i];
            foreach(Material tempMaterial in tempRenderer.sharedMaterials)
            {
                if (tempMaterial.shader)
                {
                    if(tempMaterial.shader.name == "Unlit/SRUniversal")
                    {
                        tempMaterial.SetVector(HeadForwardID, HeadForwardTransform.position - HeadBoneTransform.position);
                        tempMaterial.SetVector(HeadRightID, HeadRightTransform.position - HeadBoneTransform.position);
                    }

                }
            }
        }
    }
}

cg/hlsl语法链接:

Unity URP CG/HLSL精简规范总结_凯尔315的博客-CSDN博客

unitypackage资源链接:
https://download.csdn.net/download/qq_55895529/88370041?spm=1001.2014.3001.5503

原作视频链接:

【Unity/虚幻5/Blender】3种引擎 崩坏: 星穹铁道风格 卡通渲染 从球谐光照到眉毛透过刘海 完整流程_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值