阴影实现 - 另一个平面阴影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)

浙公网安备 33010602011771号