我们的策划想要让原本的假阴影做一个渐变的效果并且想要让边缘模糊。原本的效果时这样的
可以看到阴影就是一个黑影并且边缘有很多锯齿。
首先来分析两个事情,一个是锯齿,一个时渐变阴影。
一:阴影锯齿
阴影的锯齿主要是因为像素不足导致的,这个情况下如果是用shadowmap的话我们可以考虑用pcf等方法处理。市面上也有很多处理阴影锯齿的方法。
但是我们这个阴影并不是用shadowmap实现的,是直接压扁放到角色脚底实现的。这样就会导致锯齿只能全屏处理,比如msaa,taa等全屏操作。没办法在这个pass中直接解决。
然后基于获取阴影之后做抗锯齿的操作考虑有两种:
一种是阴影的pass之后每个角色执行多一个pass,在那个pass中解决抗锯齿。但是这样就会导致每个角色都多一个pass。性能消耗多一倍。
一种是把角色的阴影渲染到一张图上,最后对这张图做模糊处理。这样的好处是不用每个角色多一个pass处理。但他也有不好的地方,主要是需要截图然后做模糊,这里也会有一些消耗,特别是模糊的时候,需要多采样。
我用的是后面的这种方式:
首先需要保证角色都是在不透明渲染中执行的,然后我们可以用CommandBuffer执行在不透明渲染之后执行一次截图(相当于urp里的OpaqueTexture)
void OnPreRender()
{
copyCmb.Clear();
RenderTexture.ReleaseTemporary(CopyOpaqueTexture);
CopyOpaqueTexture = RenderTexture.GetTemporary(mainCamera.pixelWidth , mainCamera.pixelHeight, 0, RenderTextureFormat.ARGB32);
CopyOpaqueTexture.filterMode = FilterMode.Point;
CopyOpaqueTexture.useMipMap = false;
CopyOpaqueTexture.autoGenerateMips = false;
copyCmb.Blit(BuiltinRenderTextureType.CameraTarget, CopyOpaqueTexture);
Shader.SetGlobalTexture("_CameraColor", CopyOpaqueTexture);
}
这里是说把相机的内容放到CopyOpaqueTexture里去。
当然如果只是做这步我们能获取到不透明物体的所有角色,但是我们在图片上想要出现的只是阴影的效果,不是想要角色本身的效果。
如果有角色本身的效果的话后面做模糊处理会影响到角色的显示,比如效果会是这样:
那么我们可以利用不透明渲染的一个特点,就是我们处理alpha值对角色本身是不会有影响的,也就是他始终全部颜色都会显示。不会理会alpha值。而我们写到图片上时alpha信息时存在的,利用这个特性我们就能根据alpha提取出阴影信息了。
也就是说不透明渲染的shader把角色渲染的地方设置alpha值为0(渲染阴影的shader还是要正常的alpha):
这样就能得到阴影信息了:
得到后我们需要在透明渲染阶段对这个阴影图做模糊处理,一开始我时考虑用一个CommandBuffer来让他在透明渲染后执行,但后来法线他会挡着透明渲染里的物品,不太符合要求。所以就让这个渲染直接在transparent下执行了。
那么就相当于创建一个gameobject,然后设置正确的材质给他,这样来渲染:
CommandBuffer copyCmb;
Material shadowBlur_Material;
RenderTexture CopyOpaqueTexture;
void Awake() {
if (mainCamera == null) {
mainCamera = GetComponent<Camera>();
if (mainCamera != null) {
m_OrgCullingMask = mainCamera.cullingMask;
}
}
copyCmb = new CommandBuffer();
copyCmb.name = "copy texture";
mainCamera.AddCommandBuffer(CameraEvent.AfterImageEffectsOpaque, copyCmb);
shadowBlur_Material = new Material(Shader.Find("DiabloLite v2/Moster_Plus_Shadow_Blur"));
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Quad);
go.transform.SetParent(mainCamera.transform);
go.transform.localPosition = new Vector3(0, 0, 1);
float cameraHeight = mainCamera.orthographicSize * 2;
float percent = (float)mainCamera.pixelWidth / (float)mainCamera.pixelHeight;
go.transform.localScale = new Vector3(cameraHeight * percent, cameraHeight, 1);
go.transform.localRotation = Quaternion.identity;
var collider = go.GetComponent<MeshCollider>();
Component.Destroy(collider);
var renderer = go.GetComponent<MeshRenderer>();
renderer.sharedMaterial = shadowBlur_Material;
//Graphics.DrawTexture(rect, CopyOpaqueTexture, shadowBlur_Material, 0);
}
void OnPreRender()
{
copyCmb.Clear();
RenderTexture.ReleaseTemporary(CopyOpaqueTexture);
CopyOpaqueTexture = RenderTexture.GetTemporary(mainCamera.pixelWidth , mainCamera.pixelHeight, 0, RenderTextureFormat.ARGB32);
CopyOpaqueTexture.filterMode = FilterMode.Point;
CopyOpaqueTexture.useMipMap = false;
CopyOpaqueTexture.autoGenerateMips = false;
copyCmb.Blit(BuiltinRenderTextureType.CameraTarget, CopyOpaqueTexture);
Shader.SetGlobalTexture("_CameraColor", CopyOpaqueTexture);
}
然后我们需要做模糊,模糊有很多中,其中https://blog.csdn.net/poem_qianmo/article/details/105350519这里已经写得很清楚了。然后我用的时高斯模糊:
sampler2D _CameraColor;
float4 _CameraColor_ST;
float4 _CameraColor_TexelSize;
uniform half _ActorAlpha;
struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 disuv : TEXCOORD0;
float4 uv01 : TEXCOORD1;
float4 uv23 : TEXCOORD2;
float4 uv45 : TEXCOORD3;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = o.vertex = UnityObjectToClipPos(v.vertex);
o.disuv = TRANSFORM_TEX(v.texcoord, _CameraColor);
#if SHADER_API_D3D11
o.disuv.y = 1 - o.disuv.y;
#endif
half4 _offsets = 1 * _CameraColor_TexelSize.xyxy;
o.uv01 = o.disuv.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);
o.uv23 = o.disuv.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
o.uv45 = o.disuv.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 3.0;
return o;
}
float4 frag(v2f i) : SV_Target
{
//float4 d = _CameraColor_TexelSize.xyxy * float4(-test, -test, test, test);
/*half4 s = 0;
s = tex2D(_CameraColor, i.disuv + d.xy) * 0.25;
s += tex2D(_CameraColor, i.disuv + d.zy) * 0.25;
s += tex2D(_CameraColor, i.disuv + d.xw) * 0.25;
s += tex2D(_CameraColor, i.disuv + d.zw) * 0.25;*/
half4 color = float4(0, 0, 0, 0);
color += 0.40 * tex2D(_CameraColor, i.disuv);
color += 0.15 * tex2D(_CameraColor, i.uv01.xy);
color += 0.15 * tex2D(_CameraColor, i.uv01.zw);
color += 0.10 * tex2D(_CameraColor, i.uv23.xy);
color += 0.10 * tex2D(_CameraColor, i.uv23.zw);
color += 0.05 * tex2D(_CameraColor, i.uv45.xy);
color += 0.05 * tex2D(_CameraColor, i.uv45.zw);
return color;
}
这里要注意这个quad要根据我们相机的大小来设置,我的是正交相机,所以我用size去处理
二:阴影渐变
解决了锯齿效果后就是渐变效果了,在现实生活中的渐变效果有一个特点,就是边缘模糊和离地面越近越深的颜色,越远越淡颜色。这也是因为两物体离得近时得到间接光照效果会逐渐减弱,合并在一起基本就没了(当然不是绝对的没有了)。
所以我们可以考虑根据角色离中心点的距离来设置他的透明度。
half perX = i.modelVertex.z < 0 ? (_ActorAlpha + i.modelVertex.z * inteval) : (i.modelVertex.z > 0 ? (_ActorAlpha - i.modelVertex.z * inteval) : _ActorAlpha);
half perY = i.modelVertex.y < 0 ? (_ActorAlpha + i.modelVertex.y * inteval) : (i.modelVertex.y > 0 ? (_ActorAlpha - i.modelVertex.y * inteval) : _ActorAlpha);
color.a *= perX;
color.a *= perY;
最终就可以得到比较柔和的阴影了