阴影实现 - 另一个平面阴影Planar Shadow

最终效果

1) 两个阴影重叠的时候,基本正常

2) 在斜坡上,阴影穿插了进去

3) 在平地上遇到障碍物,阴影也会穿插进障碍物

4) 在平台边缘,镂空的地方也显示了阴影

5) 阴影衰减做了

 

原理看这边

使用顶点投射的方法制作实时阴影 - 知乎 (zhihu.com)

【Unity Shader】平面阴影(Planar Shadow) - 知乎 (zhihu.com)

 

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class PlanarShadow2 : MonoBehaviour
{
    public PlayerControl m_Player;
    public Camera m_PlanarShadowCamera; //阴影绘制的相机, 一般和Player的相机相同

    private List<Renderer> m_Renderers = new List<Renderer>();
    private CommandBuffer m_PlanarShadowCmd;
    private Material m_PlanarShadowMat;

    private List<Material> m_TempMatList = new List<Material>();

    void Awake()
    {
        if (null == m_Player)
            m_Player = GetComponent<PlayerControl>();
        if (null == m_PlanarShadowCamera)
            m_PlanarShadowCamera = Camera.main;
    }

    void Start()
    {
        GetComponentsInChildren<Renderer>(true, m_Renderers);

        m_PlanarShadowCmd = new CommandBuffer();
        m_PlanarShadowCmd.name = "Planer Shadow Render Cmd";
        m_PlanarShadowCamera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, m_PlanarShadowCmd);

        m_PlanarShadowMat = new Material(Shader.Find("My/Shadow/PlanarShadow2"));
        m_PlanarShadowMat.SetFloat("_GroundY", 0.02f);
        //m_PlanarShadowMat.SetFloat("_ShadowFalloff", 0.02f);
        //m_PlanarShadowMat.SetColor("_ShadowColor", new Color(0, 0, 0, 0.5f));
    }

    void Update()
    {
        m_PlanarShadowMat.SetFloat("_GroundY", m_Player.GetGroundY());
m_PlanarShadowCmd.Clear();
for (var i = 0; i < m_Renderers.Count; ++i) { var renderer = m_Renderers[i]; if (null == renderer || !renderer.enabled) continue; m_TempMatList.Clear(); renderer.GetSharedMaterials(m_TempMatList); for (int submeshIndex = 0; submeshIndex < m_TempMatList.Count; submeshIndex++) { m_PlanarShadowCmd.DrawRenderer(renderer, m_PlanarShadowMat, submeshIndex); //用指定的材质绘制renderer } } } }

PlanarShadow2.shader

Shader "My/Shadow/PlanarShadow2"
{
    Properties
    {
        _GroundY("GroundY", Float) = 0 //地面的Y坐标值(世界坐标)
        _ShadowColor("ShadowColor", Color) = (0, 0, 0, 0.5) //阴影颜色
        _ShadowFalloff("ShadowFalloff", Range(0, 1)) = 0.05 //阴影衰减
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent-1"}
        LOD 100

        Pass 
        {
            //处理阴影重叠时的alpha显示正确(地上已经有阴影了, 重叠上了的阴影会通不过stencil测试)
            Stencil
            {
                Ref 0
                Comp equal
                Pass incrWrap
                Fail keep
                ZFail keep
            }
          
            Blend SrcAlpha OneMinusSrcAlpha

            Cull Off
            ZWrite Off //半透明一般会关闭深度写入
            Offset -1 , 0 //深度稍微偏移防止阴影与地面穿插

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION; //模型空间坐标
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 color : COLOR;
            };

            float _GroundY;
            float4 _ShadowColor;
            float _ShadowFalloff;

            float3 GetShadowPos(float4 vertex)
            {
                float3 worldPos = mul(unity_ObjectToWorld, vertex).xyz; //顶点世界空间坐标
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //光源方向

                //阴影的世界空间坐标(低于地面的部分不做改变)
                float3 shadowPos;
                shadowPos.y = min(worldPos.y, _GroundY);
                shadowPos.xz = worldPos.xz - lightDir.xz * max(0, worldPos.y - _GroundY) / lightDir.y; //地面就是xz平面

                return shadowPos;
            }

            v2f vert(appdata v)
            {
                v2f o;
                float3 shadowPos = GetShadowPos(v.vertex); //得到阴影的世界空间坐标
                o.vertex = UnityWorldToClipPos(shadowPos); //转换到裁剪空间

                //得到中心点世界坐标(Object->World的变换矩阵, 第4列为模型空间原点的世界坐标)
                float3 center = float3(unity_ObjectToWorld[0].w, _GroundY, unity_ObjectToWorld[2].w);
                float falloff = 1 - saturate(distance(shadowPos, center) * _ShadowFalloff); //计算阴影衰减

                o.color = _ShadowColor;
                o.color.a *= falloff;
                return o; 
            }  

            fixed4 frag(v2f i) : SV_Target
            {
                return i.color;  
            }
                 
            ENDCG
        }
    }
}

 

相关链接

Unity平面阴影(王者荣耀阴影实现) - 知乎 (zhihu.com)

 
posted @ 2023-05-12 00:28  yanghui01  阅读(256)  评论(0)    收藏  举报