一、Shadermap 贴图
RenderTextureFormat.Shadowmap 在支持的平台使用 shadowmap 格式,不支持的平台会返回使用 RenderTextureFormat.Depth 格式,可以使用 SystemInfo.SupportsRenderTextureFormat 来检测
Shadowmap 贴图格式,在GPU 会自动进行 shadowmap comparisons
RenderTextureFormat.Depth 在 OpenGL it is the native “depth component” format (usually 24 or 16 bits), on Direct3D9 it is the 32 bit floating point (“R32F”) format.
在安卓设备上,经常是 D24S8 格式。即 24位深度,8位stencil。
CommandBuffer.GetTemporaryRT 默认是ARGB格式,采样模式为 FliterMode.Point

二、矩阵访问
Matrix .m23,含义是 第三行第四列

Matrix 创建时是按照列创建的
Matrix[index] 是按列访问的
三、阴影数据计算
- _cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives 获取阴影灯光的 VP矩阵,和阴影设置数据
- _CommandBuffer.SetViewport 进行渲染贴图分割渲染
- Context.DrawShadow 进行确切的渲染阴影贴图,只会执行有 ShadowCaster Pass 的 Shader
private void RenderDirectionalShadows(int index, int split, int tileSize)
{
ShadowedDirectionalLight light = _shadowedDirectionalLights[index];
ShadowDrawingSettings shadowDrawingSettings = new ShadowDrawingSettings(_cullingResults, light.VisibleLightIndex)
{
useRenderingLayerMaskTest = true
};
int cascadeCount = _shadowSettings.directional.cascadeCount;
int tileOffset = index * cascadeCount;
Vector3 ratios = _shadowSettings.directional.cascadeRadios;
float cullingFactor = Mathf.Max(0f, 0.8f - _shadowSettings.directional.cascadeFade);
float tileScale = 1f / split;
for (int i = 0; i < cascadeCount; i++)
{
_cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(
light.VisibleLightIndex, i, cascadeCount, ratios, tileSize, light.NearPlaneOffset,
out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix, out ShadowSplitData shadowSplitData
);
shadowSplitData.shadowCascadeBlendCullingFactor = cullingFactor;
shadowDrawingSettings.splitData = shadowSplitData;
if (index == 0)
{
SetCascadeData(i, shadowSplitData.cullingSphere, tileSize);
}
int tileIndex = tileOffset + i;
_dirShadowMatrices[tileIndex] = ConvertToAtlasMatrix(projMatrix * viewMatrix, SetTileViewport(tileIndex, split, tileSize), tileScale);
_buffer.SetViewProjectionMatrices(viewMatrix, projMatrix);
_buffer.SetGlobalDepthBias(0f, light.SlopeScaleBias);
ExecuteBuffer();
_context.DrawShadows(ref shadowDrawingSettings);
_buffer.SetGlobalDepthBias(0f, 0f);
}
}
private void SetCascadeData(int index, Vector4 cullingSphere, float tileSize)
{
float texelSize = 2f * cullingSphere.w / tileSize;
float filterSize = texelSize * ((float) _shadowSettings.directional.filterMode + 1f);
cullingSphere.w -= filterSize;
cullingSphere.w *= cullingSphere.w;
_cascadeData[index] = new Vector4(1f / cullingSphere.w, filterSize * 1.41242136f);
_cascadeCullingSpheres[index] = cullingSphere;
}
private Vector2 SetTileViewport(int index, int split, float tileSize)
{
Vector2 offset = new Vector2(index % split, index / split);
_buffer.SetViewport(new Rect(offset.x * tileSize, offset.y * tileSize, tileSize, tileSize));
return offset;
}
private Matrix4x4 ConvertToAtlasMatrix(Matrix4x4 matrix4X4, Vector2 offset, float scale)
{
if (SystemInfo.usesReversedZBuffer)
{
matrix4X4.m20 = -matrix4X4.m20;
matrix4X4.m21 = -matrix4X4.m21;
matrix4X4.m22 = -matrix4X4.m22;
matrix4X4.m23 = -matrix4X4.m23;
}
matrix4X4.m00 = (0.5f * (matrix4X4.m00 + matrix4X4.m30) + offset.x * matrix4X4.m30) * scale;
matrix4X4.m01 = (0.5f * (matrix4X4.m01 + matrix4X4.m31) + offset.x * matrix4X4.m31) * scale;
matrix4X4.m02 = (0.5f * (matrix4X4.m02 + matrix4X4.m32) + offset.x * matrix4X4.m32) * scale;
matrix4X4.m03 = (0.5f * (matrix4X4.m03 + matrix4X4.m33) + offset.x * matrix4X4.m33) * scale;
matrix4X4.m10 = (0.5f * (matrix4X4.m10 + matrix4X4.m30) + offset.y * matrix4X4.m30) * scale;
matrix4X4.m11 = (0.5f * (matrix4X4.m11 + matrix4X4.m31) + offset.y * matrix4X4.m31) * scale;
matrix4X4.m12 = (0.5f * (matrix4X4.m12 + matrix4X4.m32) + offset.y * matrix4X4.m32) * scale;
matrix4X4.m13 = (0.5f * (matrix4X4.m13 + matrix4X4.m33) + offset.y * matrix4X4.m33) * scale;
matrix4X4.m20 = 0.5f * (matrix4X4.m20 + matrix4X4.m30);
matrix4X4.m21 = 0.5f * (matrix4X4.m21 + matrix4X4.m31);
matrix4X4.m22 = 0.5f * (matrix4X4.m22 + matrix4X4.m32);
matrix4X4.m23 = 0.5f * (matrix4X4.m23 + matrix4X4.m33);
return matrix4X4;
}
四、级联阴影数据计算
Cascade Shadow 有一个 CullingSphere, 在shader 中通过计算在哪一个 CullingShphere 来确定应该在那个一个 Cascade Shadow 中
通过判断到摄像机的距离进行裁剪阴影,会得到一个物体在SphereCulling内,但是阴影却被裁掉的问题
可以结合 ViewSpace 的 Z depth 值进行裁剪处理
常规的 Fade 函数
- d is the surface depth
- m is the max shadow distance
- f is a fade range

五、Shadow acne(阴影自遮挡问题)
DX 裁剪空间 Z坐标是【0,1】,OPENGL 裁剪空间 Z坐标是【-1,1】
反转 Z
由于大部分物体都集中在近裁剪平面的地方,导致深度贴图中记录的数字分布非常不均匀,都集中在靠近0的地方。为了深度图充分利用,和提高精度,可以进行反转 Z,及近平面的值为1,远平面的值为0,DX 下Z坐标为[1,0]
六、PCF
- PCF unity 采样时 ten - filter 通过计算面积占比,进行优化采样,PCF3x3本来需要采样9次,优化后只需要采样4次,即可达到同样的效果,算法使用的是 GPU pro 360 guide to shadows 的方法
七、PCSF
八、资料
Cameras and depth textures
来自 https://docs.unity3d.com/Manual/SL-CameraDepthTexture.html
浅谈RenderTexture的shadowmap格式
来自 https://gameinstitute.qq.com/community/detail/124214
Shader中 UNITY_REVERSED_Z 的定义是什么
来自 https://answer.uwa4d.com/question/617fa23f8f8c834241fbbd7a
Unity中的shadows(二)cast shadows
https://blog.51cto.com/u_15069471/4545408
Unity PCF 优化算法原理,见最后的推荐链接
https://forum.unity.com/threads/pcf-size-in-urp.1126025/
The Witness
http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/