动态平面阴影

Shader "fake_shadow"
{
    Properties
    {
        //材质属性面板
        _MainTex ("主贴图",2D) = "white"{}

        _GroundY ("地面Y高度 (外部传入)",float) = 0
        _Shadow_Color("影子颜色",Color) = (1,1,1,1)
        _Shadow_Length("影子长度",float) = 0
        _Shadow_Rotated("影子旋转角度",float) = 0
    }

    SubShader
    {
        Tags
        {
            "Queue" = "Transparent+5" /*"RenderType"="Transparence"  "RenderPipeline" = "UniversalPipeline" *///注意这里很重要,因为影子是要绘制在地面上,所以地面必须应该先绘制,否则blend混合的时候就是和背后的skybox进行混合了
        }

        pass
        {
            Stencil{

                Ref 1
                //Comp取值依次为  0:Disabled  1:Never  2:Less  3:Equal  4:LessEqual  5:Greater  6:NotEqual  7:GreaterEqual  8:Always
                Comp NotEqual //或者改成NotEqual
                //Pass取值依次为  0:Keep  1:Zero  2:Replace  3:IncrementSaturate  4:DecrementSaturate  5:Invert  6:IncrementWrap  7:DecrementWrap
                Pass Replace
            }

            Blend SrcAlpha oneMinusSrcAlpha   

            //因为和地面重叠所以做个偏移
            //也可以不做偏移,将传入的地面高度抬高一点即可
            Offset -2,-2

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                //这里worldPos一定是float4,因为vert()中实际是手动两次空间变换如果是float3会导致w分量丢失,透视除法会出错
                //如果不参与变换,只是传到frag()中使用的话,比如进行Blinn-Phong光照计算V向量那么float3就够了
                float4 worldPos : TEXCOORD0;
                //做阴影插值和Clip地面以下阴影用
                float cacheWorldY : TEXCOORD1;
                float worldPosY : TEXCOORD2;
};
 //           CBUFFER_START
CBUFFER_START(UnityPerMaterial)
            half _GroundY;
            half4 _Shadow_Color;   
            half _Shadow_Length;     
            half _Shadow_Rotated;
CBUFFER_END
//            CBUFFER_END
            
            v2f vert(appdata v)
            {
                v2f o = (v2f)0;

                //获取世界空间的位置
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                //缓存世界空间下的y分量,后续两点作用
                //第一点 : 做插值用做计算xz的偏移量的多少
                //第二点 : 防止在地面以下
                o.cacheWorldY = o.worldPos.y;
    
                o.worldPosY = UnityObjectToWorldNormal(v.vertex).y;

                //设置世界空间下y的值全部都设置为传入的地面高度值
                o.worldPos.y = _GroundY;

                //根据世界空间下模型y值减去传入的地面高度值_GroundY
                //以这个值为传入 lerp(0,_Shadow_Length) 进行线性插值
                //最后获取到模型y值由低到高的插值lerpVal
                //这个max()函数 假设腿部在地面以下则裁切掉腿部阴影,后续使用clip后无需Max
                //half lerpVal = lerp(0,_Shadow_Length,max(0,o.cacheWorldY-_GroundY));
                half lerpVal = lerp(0,_Shadow_Length,o.cacheWorldY-_GroundY);
                

                //常量PI
                //const float PI = 3.14159265;
                //角度转换成弧度
                half radian = _Shadow_Rotated / 180.0 * UNITY_PI;

                //旋转矩阵,对(0,1)向量进行旋转,计算旋转后的向量,该向量就是阴影方向
                //2D旋转矩阵如下
                // [x]        [ cosθ , -sinθ ]
                // [ ]  乘以  
                // [y]        [ sinθ , cosθ  ]
                // x' = xcosθ - ysinθ
                // y' = xsinθ + ycosθ
                half2 ratatedAngle = half2((0*cos(radian)-1*sin(radian)),(0*sin(radian)+1*cos(radian)));
                
                //用以y轴高度为参考计算的插值 lerpVal 去 乘以一个旋转后的方向向量,作为阴影的方向
                //最终得到偏移后的阴影位置
                o.worldPos.xz += lerpVal * ratatedAngle;
                
                //变换到裁剪空间
                o.pos = mul(UNITY_MATRIX_VP,o.worldPos);

                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET
            {
                //剔除低于地面部分的片段
                clip(i.cacheWorldY - _GroundY);
                //用作阴影的Pass直接输出颜色即可
                return _Shadow_Color;
            }

            ENDCG
        }
    }
}
using UnityEngine;

public class SetShadowRotationAndHeight : MonoBehaviour
{

    public int materialIndex = 2; // 设置材质索引

    [SerializeField]
    private Transform rootTransform = null;          //创建模型时设置

    private Renderer rend = null;
    private bool isFindRend = false;
    private float extend = 30f;


    private Transform lightTrans=null;

    void Awake()
    {
        if (GetComponent<MeshFilter>() != null)
        {
            Mesh mesh = GetComponent<MeshFilter>().mesh;
            Bounds bd = mesh.bounds;
            bd.Expand(extend);
            mesh.bounds = bd;
        }
        else if(GetComponent<SkinnedMeshRenderer>() != null)
        {
            //SkinnedMeshRenderer skinnedMesh = GetComponent<SkinnedMeshRenderer>();
            //Bounds bd = skinnedMesh.bounds;
            //bd.Expand(extend);
            //skinnedMesh.bounds = bd;
        }
    }

    public void SetRootTransFrom(Transform root)
    {
        rootTransform = root;

        //GameObject mainLightObj = GameObject.Find("MainLight_Day");
        //if(mainLightObj==null)
        //{
        //    mainLightObj = GameObject.Find("FillLight_Day");
        //}
        //if (mainLightObj != null)
        //    lightTrans = mainLightObj.transform;
    }

    public Renderer GetTransformRenderer()
    {
        if (rend != null)
            return rend;
        if (isFindRend)
            return null;
        rend= GetComponent<Renderer>();
        isFindRend = true;
        return rend;
    }

    bool isDebug = false;

    void Update()
    {
        //if (lightTrans == null)
        //{
        //    if(!isDebug)
        //    {
        //        Debug.Log("找不到光源");
        //        isDebug = true;
        //    }
        //    return;
        //}
            

        if (rootTransform == null)
        {
            rootTransform = transform;
        }
        Renderer tempRend = GetTransformRenderer();

        if (tempRend == null)
            return;

        // 确保指定的材质索引有效
        if (materialIndex < 0 || materialIndex >= tempRend.materials.Length)
        {
            Debug.LogError(transform.name + "Invalid material index!");
            return;
        }

        Material mat = tempRend.materials[materialIndex]; // 获取指定索引的材质

        // 获取父节点的 Y 坐标
        float parentY = rootTransform.position.y + 0.2f;

        // 将父节点的 Y 坐标传递给指定索引的材质的 _GroundY 属性
        mat.SetFloat("_GroundY", parentY);

        // 获取主光源的方向

        if (RenderSettings.sun == null)
            return;

        Vector3 lightDirection = -RenderSettings.sun.transform.forward;
        //Vector3 lightDirection = -lightTrans.forward;

        // 计算主光源的旋转角度的 Y 值(使用 Atan2 函数)
        float shadowRotationY = Mathf.Atan2(lightDirection.x, lightDirection.z) * Mathf.Rad2Deg;

        // 将主光源的旋转角度的 Y 值传递给指定索引的材质的 _Shadow_Rotated 属性
        mat.SetFloat("_Shadow_Rotated", shadowRotationY);
    }
}

Material Blend Tools

写了个基于Height Map的Texture Mix的工具,应该还有提升的空间先看看效果

主要思路是通过splat_control这张贴图的四个通道控制_Splat0~_Splat3这四张贴图的混合,如果splat_control对应通道的值为1,那么这个通道对应的贴图就完全显示,为0则完全不显示,通过修改splat_control贴图就可以实现想要的混合效果了;

核心代码如下

fixed4 splat_control = tex2D (_Control, IN.uv_Control).rgba;	
	fixed3 lay1 = tex2D (_Splat0, IN.uv_Splat0);
	fixed3 lay2 = tex2D (_Splat1, IN.uv_Splat1);
	fixed3 lay3 = tex2D (_Splat2, IN.uv_Splat2);
	fixed3 lay4 = tex2D (_Splat3, IN.uv_Splat3);
	_Alpha = 0.0;
	Albedo.rgb = (lay1 * splat_control.r + lay2 * splat_control.g + lay3 * splat_control.b+ lay4 * splat_control.a);

混合处

float3 blend(float3 lay1, float3 lay2, float4 splat_control)
{
        float b1 = lay1.a * splat_control.r;
        float b2 = lay2.a * splat_control.g;
        float ma = max(b1,b2);
        b1 = max(b1 - (ma – 0.3), 0) * splat_control.r;
        b2 = max(b2 - (ma – 0.3), 0) * splat_control.g;

        return (lay1.rgb * b1 + lay2.rgb * b2)/(b1 + b2);
}

下面是工具

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
[RequireComponent(typeof(MeshCollider))]
public class MeshPainter : MonoBehaviour {

    void Start () {
	
	}
	

	void Update () {
	
	}
}

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;

[CustomEditor(typeof(MeshPainter))]
[CanEditMultipleObjects]
public class MeshPainterStyle : Editor
{
    string contolTexName = "";

    bool isPaint;

    float brushSize = 16f;
    float brushStronger = 0.5f;

    Texture[] brushTex;
    Texture[] texLayer;

    int selBrush = 0;
    int selTex = 0;


    int brushSizeInPourcent;
    Texture2D MaskTex;
    void OnSceneGUI()
    {
        if (isPaint)
        {
            Painter();
        }

    }
    public override void OnInspectorGUI()
        
    {
        if (Cheak())
        {
            GUIStyle boolBtnOn = new GUIStyle(GUI.skin.GetStyle("Button"));//得到Button样式
            GUILayout.BeginHorizontal();
                GUILayout.FlexibleSpace();
                isPaint = GUILayout.Toggle(isPaint, EditorGUIUtility.IconContent("ClothInspector.PaintValue"), boolBtnOn, GUILayout.Width(35), GUILayout.Height(25));//编辑模式开关
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
            brushSize = (int)EditorGUILayout.Slider("Brush Size", brushSize, 1, 36);//笔刷大小
            brushStronger = EditorGUILayout.Slider("Brush Stronger", brushStronger, 0, 1f);//笔刷强度

            IniBrush();
            layerTex();
            GUILayout.BeginHorizontal();
                GUILayout.FlexibleSpace();
                    GUILayout.BeginHorizontal("box", GUILayout.Width(340));
                    selTex = GUILayout.SelectionGrid(selTex, texLayer, 4, "gridlist", GUILayout.Width(340), GUILayout.Height(86));
                    GUILayout.EndHorizontal();
                GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
                GUILayout.FlexibleSpace();
                    GUILayout.BeginHorizontal("box", GUILayout.Width(318));
                    selBrush = GUILayout.SelectionGrid(selBrush, brushTex, 9, "gridlist", GUILayout.Width(340), GUILayout.Height(70));
                    GUILayout.EndHorizontal();
                GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
        }
        
    }

    //获取材质球中的贴图
    void layerTex()
    {
        Transform Select = Selection.activeTransform;
        texLayer = new Texture[4];
        texLayer[0] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat0")) as Texture;
        texLayer[1] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat1")) as Texture;
        texLayer[2] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat2")) as Texture;
        texLayer[3] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat3")) as Texture;
    }

    //获取笔刷  
    void IniBrush()
    {
        string MeshPaintEditorFolder = "Assets/MeshPaint/Editor/";
        ArrayList BrushList = new ArrayList();
        Texture BrushesTL;
        int BrushNum = 0;
        do
        {
            BrushesTL = (Texture)AssetDatabase.LoadAssetAtPath(MeshPaintEditorFolder + "Brushes/Brush" + BrushNum + ".png", typeof(Texture));

            if (BrushesTL)
            {
                BrushList.Add(BrushesTL);
            }
            BrushNum++;
        } while (BrushesTL);
        brushTex = BrushList.ToArray(typeof(Texture)) as Texture[];
    }

    //检查
    bool Cheak()
    {
        bool Cheak = false;
        Transform Select = Selection.activeTransform;
        Texture ControlTex = Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Control");
        if(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.shader == Shader.Find("4Tex_Blend_Normal") || Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.shader == Shader.Find("zcxshaderlibrary/texBlendWithBump"))
        {
            if(ControlTex == null)
            {
                EditorGUILayout.HelpBox("当前模型材质球中未找到Control贴图,绘制功能不可用!", MessageType.Error);
                if (GUILayout.Button("创建Control贴图"))
                {
                    creatContolTex();
                    //Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.SetTexture("_Control", creatContolTex());
                }
            }
            else
            {
                Cheak = true;
            }
        }
        else 
        {
            EditorGUILayout.HelpBox("当前模型shader错误!请更换!", MessageType.Error);
        }
        return Cheak;
    }

    //创建Contol贴图
    void creatContolTex()
    {

        //创建一个新的Contol贴图
        string ContolTexFolder = "Assets/MeshPaint/Controler/";
        Texture2D newMaskTex = new Texture2D(512, 512, TextureFormat.ARGB32, true);
        Color[] colorBase = new Color[512 * 512];
        for(int t = 0; t< colorBase.Length; t++)
        {
            colorBase[t] = new Color(1, 0, 0, 0);
        }
        newMaskTex.SetPixels(colorBase);

        //判断是否重名
        bool exporNameSuccess = true;
        for(int num = 1; exporNameSuccess; num++)
        {
            string Next = Selection.activeTransform.name +"_"+ num;
            if (!File.Exists(ContolTexFolder + Selection.activeTransform.name + ".png"))
            {
                contolTexName = Selection.activeTransform.name;
                exporNameSuccess = false;
            }
            else if (!File.Exists(ContolTexFolder + Next + ".png"))
            {
                contolTexName = Next;
                exporNameSuccess = false;
            }

        }

        string path = ContolTexFolder + contolTexName + ".png";
        byte[] bytes = newMaskTex.EncodeToPNG();
        File.WriteAllBytes(path, bytes);//保存


        AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);//导入资源
        //Contol贴图的导入设置
        TextureImporter textureIm = AssetImporter.GetAtPath(path) as TextureImporter;
        TextureImporterPlatformSettings texset = textureIm.GetDefaultPlatformTextureSettings();
        texset.format = TextureImporterFormat.RGBA32;
        //texset.maxTextureSize=512;
        textureIm.SetPlatformTextureSettings(texset);
        textureIm.isReadable = true;
        textureIm.anisoLevel = 9;
        textureIm.mipmapEnabled = false;
        textureIm.wrapMode = TextureWrapMode.Clamp;
        AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);//刷新


        setContolTex(path);//设置Contol贴图

    }

    //设置Contol贴图
    void setContolTex(string peth)
    {
        Texture2D ControlTex = (Texture2D)AssetDatabase.LoadAssetAtPath(peth, typeof(Texture2D));
        Selection.activeTransform.gameObject.GetComponent<MeshRenderer>().sharedMaterial.SetTexture("_Control", ControlTex);
    }

    void Painter()
    {
        
        
        Transform CurrentSelect = Selection.activeTransform;
        MeshFilter temp = CurrentSelect.GetComponent<MeshFilter>();//获取当前模型的MeshFilter
        float orthographicSize = (brushSize * CurrentSelect.localScale.x) * (temp.sharedMesh.bounds.size.x / 200);//笔刷在模型上的正交大小
        MaskTex = (Texture2D)CurrentSelect.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Control");//从材质球中获取Control贴图

        brushSizeInPourcent = (int)Mathf.Round((brushSize * MaskTex.width) / 100);//笔刷在模型上的大小
        bool ToggleF = false;
        Event e = Event.current;//检测输入
        HandleUtility.AddDefaultControl(0);
        RaycastHit raycastHit = new RaycastHit();
        Ray terrain = HandleUtility.GUIPointToWorldRay(e.mousePosition);//从鼠标位置发射一条射线
        if (Physics.Raycast(terrain, out raycastHit, Mathf.Infinity, 1 << LayerMask.NameToLayer("ground")))//射线检测名为"ground"的层
        {
            Handles.color = new Color(1f, 1f, 0f, 1f);//颜色
            Handles.DrawWireDisc(raycastHit.point, raycastHit.normal, orthographicSize);//根据笔刷大小在鼠标位置显示一个圆

            //鼠标点击或按下并拖动进行绘制
            if ((e.type == EventType.MouseDrag &amp;&amp; e.alt == false &amp;&amp; e.control == false &amp;&amp; e.shift == false &amp;&amp; e.button == 0) || (e.type == EventType.MouseDown &amp;&amp; e.shift == false &amp;&amp; e.alt == false &amp;&amp; e.control == false &amp;&amp; e.button == 0 &amp;&amp; ToggleF == false))
            {
                //选择绘制的通道
                Color targetColor = new Color(1f, 0f, 0f, 0f);
                switch (selTex)
                {
                    case 0:
                        targetColor = new Color(1f, 0f, 0f, 0f);
                        break;
                    case 1:
                        targetColor = new Color(0f, 1f, 0f, 0f);
                        break;
                    case 2:
                        targetColor = new Color(0f, 0f, 1f, 0f);
                        break;
                    case 3:
                        targetColor = new Color(0f, 0f, 0f, 1f);
                        break;

                }

                //targetColor = new Color(0f, 0f, 0f, 0f);

                Vector2 pixelUV = raycastHit.textureCoord;

                //计算笔刷所覆盖的区域
                int PuX = Mathf.FloorToInt(pixelUV.x * MaskTex.width);
                int PuY = Mathf.FloorToInt(pixelUV.y * MaskTex.height);
                int x = Mathf.Clamp(PuX - brushSizeInPourcent / 2, 0, MaskTex.width - 1);
                int y = Mathf.Clamp(PuY - brushSizeInPourcent / 2, 0, MaskTex.height - 1);
                int width = Mathf.Clamp((PuX + brushSizeInPourcent / 2), 0, MaskTex.width) - x;
                int height = Mathf.Clamp((PuY + brushSizeInPourcent / 2), 0, MaskTex.height) - y;

                Color[] terrainBay = MaskTex.GetPixels(x, y, width, height, 0);//获取Control贴图被笔刷所覆盖的区域的颜色

                Texture2D TBrush = brushTex[selBrush] as Texture2D;//获取笔刷性状贴图
                float[] brushAlpha = new float[brushSizeInPourcent * brushSizeInPourcent];//笔刷透明度

                //根据笔刷贴图计算笔刷的透明度
                for (int i = 0; i < brushSizeInPourcent; i++)
                {
                    for (int j = 0; j < brushSizeInPourcent; j++)
                    {
                        brushAlpha[j * brushSizeInPourcent + i] = TBrush.GetPixelBilinear(((float)i) / brushSizeInPourcent, ((float)j) / brushSizeInPourcent).a;
                    }
                }

                //计算绘制后的颜色
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        int index = (i * width) + j;
                        float Stronger = brushAlpha[Mathf.Clamp((y + i) - (PuY - brushSizeInPourcent / 2), 0, brushSizeInPourcent - 1) * brushSizeInPourcent + Mathf.Clamp((x + j) - (PuX - brushSizeInPourcent / 2), 0, brushSizeInPourcent - 1)] * brushStronger;

                        terrainBay[index] = Color.Lerp(terrainBay[index], targetColor, Stronger);
                    }
                }
                Undo.RegisterCompleteObjectUndo(MaskTex, "meshPaint");//保存历史记录以便撤销

                MaskTex.SetPixels(x, y, width, height, terrainBay, 0);//把绘制后的Control贴图保存起来
                MaskTex.Apply();
                ToggleF = true;
            }

              if(e.type == EventType.MouseUp &amp;&amp; e.alt == false &amp;&amp; e.button == 0 &amp;&amp; ToggleF == true)
            {

                SaveTexture();//绘制结束保存Control贴图
                ToggleF = false;
            }
        }
    }
    public void SaveTexture()
    {
        var path = AssetDatabase.GetAssetPath(MaskTex);
        var bytes = MaskTex.EncodeToPNG();
        File.WriteAllBytes(path, bytes);
        AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);//刷新
    }
}

Shader模块

Shader "zcxshaderlibrary/texBlendWithBump"
{
    Properties
    {
        _SpecularColor("SpecularColor",Color)=(1,1,1,1)
        _Smoothness("Smoothness",range(0,20))=10
        _Cutoff("Cutoff",float)=0.5
        [Space(10)][Header(Layer)]
        [Space(30)][Header(Layer1Map)]
        _Splat0 ("Layer 1(RGBA)",2D) = "white"{}
        _Splat0_Color("_Splat1 Color",Color) = (1,1,1,1)
        _BumpSplat0 ("Layer 1 Normal(Bump)", 2D) = "Bump" {}
        _BumpSplat0Scale("Layer 1 Normal Scale",float)=1
        [Space(30)][Header(Layer2Map)]
        _Splat1 ("Layer 2(RGBA)", 2D) = "white" {}
        _Splat1_Color("_Splat2 Color",Color) = (1,1,1,1)
        _BumpSplat1 ("Layer 2 Normal(Bump)", 2D) = "Bump" {}
        _BumpSplat1Scale("Layer 2 Normal Scale",float)=1
        [Space(30)][Header(Layer3Map)]
        _Splat2 ("Layer 3(RGBA)", 2D) = "white" {}
        _Splat2_Color("_Splat3 Color",Color) = (1,1,1,1)
        _BumpSplat2 ("Layer 3 Normal(Bump)", 2D) = "Bump" {}
        _BumpSplat2Scale("Layer 3 Normal Scale",float)=1
        [Space(30)][Header(Layer4Map)]
        _Splat3 ("Layer 4(RGBA)", 2D) = "white" {}
        _Splat3_Color("_Splat4 Color",Color) = (1,1,1,1)
        _BumpSplat3 ("Layer 4 Normal(Bump)", 2D) = "Bump" {}
        _BumpSplat3Scale("Layer 4 Normal Scale",float)=1

        [Space(30)][Header(Blend Texture)]
        _Control ("Control (RGBA)", 2D) = "white" {}
        _Weight("Blend Weight" , Range(0.001,1)) = 0.2 
        
    }
    SubShader
    {
        Tags
        {
            "RenderPipeline"="UniversalPipeline"
            "Queue"="Geometry"
            "RenderType"="Opaque"
        }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        CBUFFER_START(UnityPerMaterial)
        
        float4 _BaseColor;
        float4 _Splat0_ST;
        float4 _Splat0_Color;
        float4 _SpecularColor;
        float4 _BumpSplat0_ST;
        float4 _Splat1_ST;
        float4 _Splat1_Color;
        float4 _BumpSplat1_ST;
        float4 _Splat2_ST;
        float4 _Splat2_Color;
        float4 _BumpSplat2_ST;
        float4 _Splat3_ST;
        float4 _Splat3_Color;
        float4 _BumpSplat3_ST;
        float4 _Control_ST;

        //float _Control;
        float _Weight;

        float _Smoothness;
        float _Cutoff;
        
        float _BumpSplat0Scale;
        float _BumpSplat1Scale;
        float _BumpSplat2Scale;
        float _BumpSplat3Scale;


        TEXTURE2D(_Splat0);
        SAMPLER(sampler_Splat0);  
        TEXTURE2D(_Splat1);
        SAMPLER(sampler_Splat1);  
        TEXTURE2D(_Splat2);
        SAMPLER(sampler_Splat2);  
        TEXTURE2D(_Splat3);
        SAMPLER(sampler_Splat3);  
        TEXTURE2D(_Control);
        SAMPLER(sampler_Control); 
        TEXTURE2D(_BumpSplat0);
        SAMPLER(sampler_BumpSplat0);
        TEXTURE2D(_BumpSplat1);
        SAMPLER(sampler_BumpSplat1);
        TEXTURE2D(_BumpSplat2);
        SAMPLER(sampler_BumpSplat2);  
        TEXTURE2D(_BumpSplat3);
        SAMPLER(sampler_BumpSplat3);  





        CBUFFER_END
        float4 _BaseMap_ST;
        
        ENDHLSL
    

        Pass
        {
            Name "URPSimpleLit" 
            Tags{"LightMode"="UniversalForward"}

            HLSLPROGRAM            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            #pragma vertex vert
            #pragma fragment frag

            

            struct Attributes
            {
                float4 positionOS : POSITION;
                float4 normalOS : NORMAL;
                float4 tangentOS  : TANGENT;
                float2 uv0 : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
                float2 uv2 : TEXCOORD2;
                float2 uv3 : TEXCOORD3;
                float2 uv4 : TEXCOORD4;
            };
            struct Varings
            {
                float4 positionCS : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
                float2 uv2 : TEXCOORD2;
                float2 uv3 : TEXCOORD3;
                float2 uv4 : TEXCOORD4;
                float3 positionWS : TEXCOORD5;
                float3 viewDirWS : TEXCOORD6;
                float3 normalWS : NORMAL_WS;
                //float3 normalWS1   : NORMAL_WS;
                float4 tangentWS  : TANGENT_WS;
            };
            
  
            
            inline float4 Blend(float depth1 ,float depth2,float depth3,float depth4 , float4 control) 
            {
                float4 blend ;
                
                blend.r =depth1 * control.r;
                blend.g =depth2 * control.g;
                blend.b =depth3 * control.b;
                blend.a =depth4 * control.a;
                
                float ma = max(blend.r, max(blend.g, max(blend.b, blend.a)));
                blend = max(blend - ma +_Weight , 0) * control;
                return blend/(blend.r + blend.g + blend.b + blend.a);
            }

            Varings vert(Attributes IN)
            {
                Varings OUT;
                VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
                VertexNormalInputs normalInputs = GetVertexNormalInputs(IN.normalOS.xyz);
                real sign = IN.tangentOS.w * GetOddNegativeScale();
                OUT.positionCS = positionInputs.positionCS;
                OUT.positionWS = positionInputs.positionWS;
                OUT.viewDirWS = GetCameraPositionWS() - positionInputs.positionWS;
                OUT.normalWS = normalInputs.normalWS;
                OUT.tangentWS  = real4(normalInputs.tangentWS, sign);
                OUT.uv0=TRANSFORM_TEX(IN.uv0,_Splat0);
                OUT.uv1=TRANSFORM_TEX(IN.uv1,_Splat1);
                OUT.uv2=TRANSFORM_TEX(IN.uv2,_Splat2);
                OUT.uv3=TRANSFORM_TEX(IN.uv3,_Splat3);
                OUT.uv4=TRANSFORM_TEX(IN.uv4,_Control);
                return OUT;
            }
            
            float4 frag(Varings IN):SV_Target
            {   
                //
                float4 splat_control = SAMPLE_TEXTURE2D(_Control, sampler_Control, IN.uv4);
                //纹理贴图
                float4 lay1 = SAMPLE_TEXTURE2D(_Splat0, sampler_Splat0, IN.uv0);     
                float4 lay2 = SAMPLE_TEXTURE2D(_Splat1, sampler_Splat1, IN.uv1);
                float4 lay3 = SAMPLE_TEXTURE2D(_Splat2, sampler_Splat2, IN.uv2);
                float4 lay4 = SAMPLE_TEXTURE2D(_Splat3, sampler_Splat3, IN.uv3); 

                //Bump贴图
                float3 nor1TS = UnpackNormalScale(SAMPLE_TEXTURE2D (_BumpSplat0, sampler_BumpSplat0,IN.uv0),_BumpSplat0Scale);
                float3 nor2TS = UnpackNormalScale(SAMPLE_TEXTURE2D (_BumpSplat1, sampler_BumpSplat1,IN.uv1),_BumpSplat1Scale);
                float3 nor3TS = UnpackNormalScale(SAMPLE_TEXTURE2D (_BumpSplat2, sampler_BumpSplat2,IN.uv2),_BumpSplat2Scale);
                float3 nor4TS = UnpackNormalScale(SAMPLE_TEXTURE2D (_BumpSplat3, sampler_BumpSplat3,IN.uv3),_BumpSplat3Scale);



                //BaseColor附加到各个图层
                lay1.rgb*=lay1.rgb*_Splat0_Color.rgb;
                lay2.rgb*=lay2.rgb*_Splat1_Color.rgb;
                lay3.rgb*=lay3.rgb*_Splat2_Color.rgb;
                lay4.rgb*=lay4.rgb*_Splat3_Color.rgb;
                
                

                //Normal计算
                real sgn = IN.tangentWS.w;
                real3 bitangent = sgn * cross(IN.normalWS.xyz, IN.tangentWS.xyz);
                real3 nor1WS = mul(nor1TS, real3x3(IN.tangentWS.xyz, bitangent.xyz, IN.normalWS.xyz));
                real3 nor2WS = mul(nor2TS, real3x3(IN.tangentWS.xyz, bitangent.xyz, IN.normalWS.xyz));
                real3 nor3WS = mul(nor3TS, real3x3(IN.tangentWS.xyz, bitangent.xyz, IN.normalWS.xyz));
                real3 nor4WS = mul(nor4TS, real3x3(IN.tangentWS.xyz, bitangent.xyz, IN.normalWS.xyz)); // 转换至世界空间
                //计算混合
                float4 blend = Blend(lay1.a,lay2.a,lay3.a,lay4.a,splat_control);
                float3 blendNor = nor1WS*blend.r+nor2WS*blend.g+nor3WS*blend.b+nor4WS*blend.a;

                //计算主光
                Light light = GetMainLight();
                float3 diffuse = LightingLambert(light.color, light.direction, IN.normalWS);
                float3 specular = LightingSpecular(light.color, light.direction, normalize(blendNor), normalize(IN.viewDirWS), _SpecularColor, _Smoothness);

                //计算附加光照
                uint pixelLightCount = GetAdditionalLightsCount();
                for (uint lightIndex = 0; lightIndex < pixelLightCount; ++lightIndex)
                {
                    Light light = GetAdditionalLight(lightIndex, IN.positionWS);
                    diffuse += LightingLambert(light.color, light.direction, IN.normalWS);
                    specular += LightingSpecular(light.color, light.direction, normalize(blendNor), normalize(IN.viewDirWS), _SpecularColor, _Smoothness);
                }


                

                float3 basecolor=lay1.rgb *blend.r  + lay2.rgb* blend.g + lay3.rgb * blend.b + lay4.rgb * blend.a;//混合
                
                
                float4 ambient=float4(SampleSH(blendNor), 1.0)*(basecolor,0);

                float3 diff=saturate(dot(light.direction,blendNor))*basecolor;

                real3 viewDirectionWS = SafeNormalize(GetCameraPositionWS() - IN.positionWS); // safe防止分母为0
                real3 h = SafeNormalize(viewDirectionWS + light.direction);
                float3 color=diff*diffuse+specular+ambient.rgb;
                clip(lay1.a*lay2.a*lay3.a*lay4.a-_Cutoff);
                return float4(color,0);
            }
            ENDHLSL            
        }

        
          Pass
        {
            Name "ShadowCaster"
            Tags{"LightMode" = "ShadowCaster"}

            ZWrite On
            ZTest LEqual
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _GLOSSINESS_FROM_BASE_ALPHA

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing

            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment


            //由于这段代码中声明了自己的CBUFFER,与我们需要的不一样,所以我们注释掉他
            //#include "Packages/com.unity.render-pipelines.universal/Shaders/SimpleLitInput.hlsl"
            //它还引入了下面2个hlsl文件
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }

    }
}