323 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			323 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								## 级联阴影
							 | 
						|||
| 
								 | 
							
								阴影贴图的缺点:阴影边缘的锯齿严重。原因是阴影贴图的分辨率低,在对阴影贴图采样时,多个不同的顶点对同一个像素采样,导致生成锯齿。为了解决这种问题,我们使用多张阴影贴图,离相机近的地方使用精细的阴影贴图,离相机远的地方使用粗糙的阴影贴图,这样不仅优化了阴影效果,还保证了渲染效率因此,级联阴影的关键就是生成和使用不同精细度的阴影贴图
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								阴影贴图的原理:在灯光方向架一台摄像机,获取深度图,然后再正常渲染自己的场景,再在正常渲染场景的时候把fragment转换到光源空间,把它和之前渲染的Shadowmap中高度深度作比较,看它是否在影子里,如果是就返回0,不是就返回1
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								主要的步骤是取得场景中灯光的设置并且传递到Shader中,之后在`RenderDirectionalShadows`中取得`GetTemporaryRT`在设置完绘制属性后(将渲染结果传递至RT而不是摄像机上),通过`RenderDirectionalShadows`绘制各个方向光的阴影。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								`RenderDirectionalShadows`主要是通过`cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives`来计算视图矩阵、投影矩阵与ShadowSplitData结构,该函数第一个参数是可见光指数。接下来的三个参数是两个整数和一个Vector3,它们控制阴影级联。稍后我们将处理级联,因此现在使用零,一和零向量。然后是纹理尺寸,我们需要使用平铺尺寸。第六个参数是靠近平面的阴影,我们现在将其忽略并将其设置为零。之后操作为:
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								shadowSettings.splitData = splitData;
							 | 
						|||
| 
								 | 
							
								//设置矩阵
							 | 
						|||
| 
								 | 
							
								buffer.SetViewProjectionMatrices(viewMatrix,projectionMatrix);
							 | 
						|||
| 
								 | 
							
								ExecuteBuffer();
							 | 
						|||
| 
								 | 
							
								//根据ShadowDrawingSettings绘制阴影
							 | 
						|||
| 
								 | 
							
								context.DrawShadows(ref shadowSettings);
							 | 
						|||
| 
								 | 
							
								```     
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								之后开始往`Lit.Shader`添加绘制阴影Pass
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								Pass{
							 | 
						|||
| 
								 | 
							
								    Tags {
							 | 
						|||
| 
								 | 
							
								        "LightMode" = "ShadowCaster"
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    
							 | 
						|||
| 
								 | 
							
								    ColorMask 0
							 | 
						|||
| 
								 | 
							
								    
							 | 
						|||
| 
								 | 
							
								    HLSLPROGRAM
							 | 
						|||
| 
								 | 
							
								    #pragma target 3.5
							 | 
						|||
| 
								 | 
							
								    #pragma shader_feature _CLIPPING
							 | 
						|||
| 
								 | 
							
								    #pragma multi_compile_instancing
							 | 
						|||
| 
								 | 
							
								    #pragma vertex ShadowCasterPassVertex
							 | 
						|||
| 
								 | 
							
								    #pragma fragment ShadowCasterPassFragment
							 | 
						|||
| 
								 | 
							
								    #include "ShadowCasterPass.hlsl"
							 | 
						|||
| 
								 | 
							
								    ENDHLSL
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								其中FragmentShader只负责裁剪
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								#ifndef CUSTOM_SHADOW_CASTER_PASS_INCLUDED
							 | 
						|||
| 
								 | 
							
								#define CUSTOM_SHADOW_CASTER_PASS_INCLUDED
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#include "../ShaderLibrary/Common.hlsl"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								TEXTURE2D(_BaseMap);
							 | 
						|||
| 
								 | 
							
								SAMPLER(sampler_BaseMap);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
							 | 
						|||
| 
								 | 
							
									UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
							 | 
						|||
| 
								 | 
							
									UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
							 | 
						|||
| 
								 | 
							
									UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
							 | 
						|||
| 
								 | 
							
								UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								struct Attributes {
							 | 
						|||
| 
								 | 
							
									float3 positionOS : POSITION;
							 | 
						|||
| 
								 | 
							
									float2 baseUV : TEXCOORD0;
							 | 
						|||
| 
								 | 
							
									UNITY_VERTEX_INPUT_INSTANCE_ID
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								struct Varyings {
							 | 
						|||
| 
								 | 
							
									float4 positionCS : SV_POSITION;
							 | 
						|||
| 
								 | 
							
									float2 baseUV : VAR_BASE_UV;
							 | 
						|||
| 
								 | 
							
									UNITY_VERTEX_INPUT_INSTANCE_ID
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Varyings ShadowCasterPassVertex (Attributes input) {
							 | 
						|||
| 
								 | 
							
									Varyings output;
							 | 
						|||
| 
								 | 
							
									UNITY_SETUP_INSTANCE_ID(input);
							 | 
						|||
| 
								 | 
							
									UNITY_TRANSFER_INSTANCE_ID(input, output);
							 | 
						|||
| 
								 | 
							
									float3 positionWS = TransformObjectToWorld(input.positionOS);
							 | 
						|||
| 
								 | 
							
									output.positionCS = TransformWorldToHClip(positionWS);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
							 | 
						|||
| 
								 | 
							
									output.baseUV = input.baseUV * baseST.xy + baseST.zw;
							 | 
						|||
| 
								 | 
							
									return output;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void ShadowCasterPassFragment (Varyings input) {
							 | 
						|||
| 
								 | 
							
									UNITY_SETUP_INSTANCE_ID(input);
							 | 
						|||
| 
								 | 
							
									float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
							 | 
						|||
| 
								 | 
							
									float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
							 | 
						|||
| 
								 | 
							
									float4 base = baseMap * baseColor;
							 | 
						|||
| 
								 | 
							
									#if defined(_CLIPPING)
							 | 
						|||
| 
								 | 
							
										clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
							 | 
						|||
| 
								 | 
							
									#endif
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								`clip()`是HLSL内置函数,当传入数值小于0时丢弃当前像素。`_Cutoff`是一个设定的浮点值,默认为0。不是0就是1。在`ShadowCaster`Pass中是为了正确渲染Alpha物体阴影。
							 | 
						|||
| 
								 | 
							
								`ShadowCaster`Pass是用来渲染阴影贴图(灯光空间的深度贴图),如果相机空间物体表面深度大于阴影贴图中深度,则代表物体处于阴影。取得阴影值在`GetDirectionalShadowAttenuation()=>FilterDirectionalShadow()`里面,采样完阴影后`GetDirectionalShadowAttenuation()`里进行一次插值,`return lerp(1.0,shadow,directional.strength);`,最后在`GetLighting()`取得计算完的阴影值。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								在`FilterDirectionalShadow()`中调用了`SAMPLE_TEXTURE2D_SHADOW`宏。`SAMPLE_TEXTURE2D_SHADOW`宏本质是`SampleCmpLevelZero()`,函数会对指定的纹理坐标进行采样,将采样的结果与传入的参数z(当前texel在光源空间的深度)进行比较,小于等于z视为通过(说明此texel没被遮挡),否则视为不通过(说明此texel位于阴影中)。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								另外注意`SamplerComparisonState`是用来进行深度采样比较的采样器。需要与`SampleCmpLevelZero`一起使用。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### 添加级联效果
							 | 
						|||
| 
								 | 
							
								由于定向光会影响最大阴影距离范围内的所有物体,因此它们的阴影贴图最终会覆盖较大的区域。由于阴影贴图使用正交投影,因此阴影贴图中的每个纹理像素都具有固定的世界空间大小。如果该尺寸太大,则清晰可见单个阴影纹理,从而导致锯齿状的阴影边缘和小的阴影可能消失。可以通过增加图集大小来缓解这种情况,但仅限于一定程度。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### 添加设置
							 | 
						|||
| 
								 | 
							
								#### ShadowSettings类
							 | 
						|||
| 
								 | 
							
								添加`ShadowSettings`类:
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								using UnityEngine;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								[System.Serializable]
							 | 
						|||
| 
								 | 
							
								public class ShadowSettings
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    [Min(0f)] 
							 | 
						|||
| 
								 | 
							
								    public float maxDistance = 100f;
							 | 
						|||
| 
								 | 
							
								    
							 | 
						|||
| 
								 | 
							
								    public enum TextureSize
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        _256=256,_512=512,_1024=1024,_2048=2048,_4096=4096,_8192=8192
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    [System.Serializable]
							 | 
						|||
| 
								 | 
							
								    public struct Directional
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public TextureSize atlasSize;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    public Directional directional = new Directional { atlasSize = TextureSize._1024};
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								往`CustomRenderPipelineAsset`、`CustomRenderPipeline`、`CameraRenderer` 、`Lighting`依次添加`ShadowSettings`变量以及对应函数中的形参。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### Shadow类
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								public class Shadows
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    const int maxShadowedDirectionalLightCount = 1;
							 | 
						|||
| 
								 | 
							
								    int ShadowedDirectionalLightCount;
							 | 
						|||
| 
								 | 
							
								    const string bufferName = "shadows";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    CommandBuffer buffer = new CommandBuffer
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        name = bufferName
							 | 
						|||
| 
								 | 
							
								    };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    ScriptableRenderContext context;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    CullingResults cullingResults;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    ShadowSettings settings;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    struct ShadowedDirectionalLight
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public int visibleLightIndex;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    private ShadowedDirectionalLight[] ShadowedDirectionalLights =
							 | 
						|||
| 
								 | 
							
								        new ShadowedDirectionalLight[maxShadowedDirectionalLightCount];
							 | 
						|||
| 
								 | 
							
								    
							 | 
						|||
| 
								 | 
							
								    public void Setup(
							 | 
						|||
| 
								 | 
							
								        ScriptableRenderContext context,CullingResults cullingResults,
							 | 
						|||
| 
								 | 
							
								        ShadowSettings settings
							 | 
						|||
| 
								 | 
							
								    )
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        ShadowedDirectionalLightCount = 0;
							 | 
						|||
| 
								 | 
							
								        this.context = context;
							 | 
						|||
| 
								 | 
							
								        this.cullingResults = cullingResults;
							 | 
						|||
| 
								 | 
							
								        this.settings = settings;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    void ExecuteBuffer()
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        context.ExecuteCommandBuffer(buffer);
							 | 
						|||
| 
								 | 
							
								        buffer.Clear();
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    /*
							 | 
						|||
| 
								 | 
							
								    * 存储方向光阴影信息
							 | 
						|||
| 
								 | 
							
								    * 灯光处于阴影有效且处于可见状态时,将信息存储在ShadowedDirectionalLights[]中
							 | 
						|||
| 
								 | 
							
								    */
							 | 
						|||
| 
								 | 
							
								    public void ReserveDirectionalShadows(Light light, int visibleLightIndex)
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        if (ShadowedDirectionalLightCount < maxShadowedDirectionalLightCount &&
							 | 
						|||
| 
								 | 
							
								            light.shadows!=LightShadows.None && light.shadowStrength>0f && 
							 | 
						|||
| 
								 | 
							
								            cullingResults.GetShadowCasterBounds(visibleLightIndex,out Bounds b)) 
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ShadowedDirectionalLights[ShadowedDirectionalLightCount++] = new ShadowedDirectionalLight() { visibleLightIndex = visibleLightIndex};
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								- 之后在`Lighting`类中添加`Shadow`类变量shadow,并且在`Setup`中添加`shadows.Setup(context,cullingResults,shadowSettings);`。以及在`SetupDirectionalLight`中添加`shadows.ReserveDirectionalShadows(visibleLight.light,index);`。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### 渲染
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### 阴影图集
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								TEXTURE2D_SHADOW(_DirectionalShadowAtlas);
							 | 
						|||
| 
								 | 
							
								#define SHADOW_SAMPLER sampler_linear_clamp_compare
							 | 
						|||
| 
								 | 
							
								SAMPLER_CMP(SHADOW_SAMPLER);
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### 3 级联阴影贴图
							 | 
						|||
| 
								 | 
							
								现在终于得到阴影,但它们看起来很糟糕。不应被阴影化的表面最终会被形成像素化带的阴影伪影所覆盖。这些是由于阴影贴图的有限分辨率导致的自我阴影化。使用不同的分辨率会更改伪影模式,但不会消除它们。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### 球形剔除
							 | 
						|||
| 
								 | 
							
								Unity通过为其创建一个选择球来确定每个级联覆盖的区域。由于阴影投影是正交的且呈正方形,因此它们最终会紧密契合其剔除球,但还会覆盖周围的一些空间。这就是为什么可以在剔除区域之外看到一些阴影的原因。同样,光的方向与球无关,因此所有定向光最终都使用相同的剔除球。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								剔除球是ComputeDirectionalShadowMatricesAndCullingPrimitives函数中计算出的`ShadowSplitData`分离出的数据。传递`_CascadeCount`级联数以及`_CascadeCullingSpheres`以及剔除球的位置数据。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								创建`ShadowData`结构体以传递级联级别以及阴影硬度。将会在`GetLighting`中通过像素的世界坐标是否在球体内来计算`ShadowData`结构体中的级联级别。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### 最大距离
							 | 
						|||
| 
								 | 
							
								此时阴影会在超过最后一个剔除球后消失,为了解决这个问题,会设置一个最大距离,超过最大距离阴影才会消失。具体操作是在`GetShadowData`中比较像素深度以及阴影最大距离值,如果超过则将`strength`设置为0。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### 给阴影添加衰减与级联渐变
							 | 
						|||
| 
								 | 
							
								阴影衰减见git,
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								级联衰减公式:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								其中f为下面式子的倒数:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								在`RenderDirectionalShadows`中计算fade因子后传递给`_ShadowDistanceFade`
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### 清除阴影的摩尔纹
							 | 
						|||
| 
								 | 
							
								##### 简单的清除方法
							 | 
						|||
| 
								 | 
							
								1. 最简单的方法是向阴影投射器的深度添加恒定的偏差,这虽然会产生不精确的阴影但可以消除摩尔纹。
							 | 
						|||
| 
								 | 
							
								2. 另一种方法是应用斜率比例偏差,方法是对SetGlobalDepthBias的第二个参数使用非零值。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								##### 法线偏差
							 | 
						|||
| 
								 | 
							
								新建级联数据变量用来传递级级联数据,x为剔除球半径倒数,y为使用`√2*2f*cullingSphere.w/tileSize`算出来的级联纹理大小,因为最坏的情况是以像素对角方向进行偏移,所以前面有乘以`√2`,存入shader后乘以Normal取得偏移值,之后对`surfaceWS`进行偏移。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								##### 可配置的偏差
							 | 
						|||
| 
								 | 
							
								从剔除数据中获取Light以及其shadowBias,之后传递给`ShadowedDirectionalLight`。在绘制阴影前将偏移值传递给`buffer.SetGlobalDepthBias(0,light.slopeScaleBiase);`;从剔除数据中获取Light以及其normalBias传递到Shader中,乘以上一步中的`normalBias`。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								##### 解决阴影裁剪问题
							 | 
						|||
| 
								 | 
							
								当摄像机处于物体中间时,会出现阴影被裁剪的问题。解决方法是在顶点着色器中添加:
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								#if UNITY_REVERSED_Z
							 | 
						|||
| 
								 | 
							
								    output.positionCS.z=min(output.positionCS.z,output.positionCS.w*UNITY_NEAR_CLIP_VALUE);
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
								    output.positionCS.z=max(output.positionCS.z,output.positionCS.w*UNITY_NEAR_CLIP_VALUE);
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								原理是当物体z坐标小于近剪裁面时,将顶点挤压or贴在近剪裁面上。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								对于大三角产生问题的原因不明白。解决方法是取得灯光的`ShadowNearPlane`之后传递给计算剔除球形参中。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								##### PCF过滤
							 | 
						|||
| 
								 | 
							
								到目前为止,我们仅对每个片段采样一次阴影贴图,且使用了硬阴影。阴影比较采样器使用特殊形式的双线性插值,在插值之前执行深度比较。这被称为百分比紧密过滤(percentage closer filtering 简称PCF),因为其中包含四个纹理像素,所以一般指是2×2 PCF过滤器。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								- 添加2x2、3x3、5x5,3种过滤枚举、传递shadowAtlasSize到Shader,以及对应的Shader宏,并且修改`SetKeywords()`。
							 | 
						|||
| 
								 | 
							
								- 为每种过滤器设置不同的采样次数与宏设置
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								`DIRECTIONAL_FILTER_SETUP`为SampleShadow_ComputeSamples_Tent_xxx通过Size与positionSTS.xy计算权重以及uv。之后对阴影进行对应次数的采样。
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								float FilterDirectionalShadow (float3 positionSTS)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								#if defined(DIRECTIONAL_FILTER_SETUP)
							 | 
						|||
| 
								 | 
							
									float weights[DIRECTIONAL_FILTER_SAMPLES];
							 | 
						|||
| 
								 | 
							
									float2 positions[DIRECTIONAL_FILTER_SAMPLES];
							 | 
						|||
| 
								 | 
							
									float4 size = _ShadowAtlasSize.yyxx;
							 | 
						|||
| 
								 | 
							
									DIRECTIONAL_FILTER_SETUP(size, positionSTS.xy, weights, positions);
							 | 
						|||
| 
								 | 
							
									float shadow = 0;
							 | 
						|||
| 
								 | 
							
									for (int i = 0; i < DIRECTIONAL_FILTER_SAMPLES; i++) {
							 | 
						|||
| 
								 | 
							
										shadow += weights[i] * SampleDirectionalShadowAtlas(
							 | 
						|||
| 
								 | 
							
											float3(positions[i].xy, positionSTS.z)
							 | 
						|||
| 
								 | 
							
										);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									return shadow;
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
									return SampleDirectionalShadowAtlas(positionSTS);
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								在`Shadows.cs`中修改`SetCascadeData()`。增大滤镜大小可使阴影更平滑,但也会导致粉刺再次出现。我们需要增加法向偏置以匹配滤波器尺寸。可以通过将纹理像素大小乘以1加上SetCascadeData中的过滤器模式来自动执行此操作。
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								void SetCascadeData(int index, Vector4 cullingSphere, float tileSize)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    float texelSize = 2f * cullingSphere.w / tileSize;
							 | 
						|||
| 
								 | 
							
								    float filterSize = texelSize * ((float)settings.directional.filter + 1f);
							 | 
						|||
| 
								 | 
							
								    cullingSphere.w -= filterSize;
							 | 
						|||
| 
								 | 
							
								    cullingSphere.w *= cullingSphere.w;
							 | 
						|||
| 
								 | 
							
								    cascadeCullingSpheres[index] = cullingSphere;
							 | 
						|||
| 
								 | 
							
								    cascadeData[index] = new Vector4(
							 | 
						|||
| 
								 | 
							
								        1f / cullingSphere.w, 
							 | 
						|||
| 
								 | 
							
								        filterSize*1.4142136f);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								##### 级联过渡
							 | 
						|||
| 
								 | 
							
								在`GetShadowData()`中根据距离计算fade,如果不是最后一个球就把fade赋值给cascadeBlend。最后一个球的`·`strength=strength*fade`
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								for (i=0;i<_CascadeCount;i++)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										float4 sphere=_CascadeCullingSpheres[i];
							 | 
						|||
| 
								 | 
							
										float distanceSqr=DistanceSquared(surfaceWS.position,sphere.xyz);
							 | 
						|||
| 
								 | 
							
										if(distanceSqr<sphere.w)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											if(i== _CascadeCount-1)
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												data.strength*=FadedShadowStrength(distanceSqr,_CascadeData[i].x,_ShadowDistanceFade.z);
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
											break;;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								##### 过渡抖动
							 | 
						|||
| 
								 | 
							
								提高级联过渡效果。在`LitPassFragment`中给像素计算抖动值
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								surface.dither=InterleavedGradientNoise(input.positionCS.xy,0);
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								当使用抖动混合时,如果我们不在上一个级联中,则当混合值小于抖动值时,跳到下一个级联。
							 | 
						|||
| 
								 | 
							
								```c#
							 | 
						|||
| 
								 | 
							
								#if defined(_CASCADE_BLEND_DITHER)
							 | 
						|||
| 
								 | 
							
									else if(data.cascadeBlend < surfaceWS.dither)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										i+=1;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								##### 其他功能效果
							 | 
						|||
| 
								 | 
							
								- 透明度
							 | 
						|||
| 
								 | 
							
								- 阴影模式
							 | 
						|||
| 
								 | 
							
								- 裁切阴影
							 | 
						|||
| 
								 | 
							
								- 抖动阴影
							 | 
						|||
| 
								 | 
							
								- 无阴影
							 | 
						|||
| 
								 | 
							
								- 不受光阴影投射器
							 | 
						|||
| 
								 | 
							
								- 接受阴影
							 |