SRP_04_DirectionalShadows


一、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

image-20221117194118295

二、矩阵访问

  • Matrix .m23,含义是 第三行第四列
    image-20221117194650541

  • Matrix 创建时是按照列创建的

  • image-20221117194804669

  • Matrix[index] 是按列访问的

  • image-20221117194959319

三、阴影数据计算

  • _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 中

  • image-20221117195810960
  • image-20221117195930117

  • image-20221117201442092

  • 通过判断到摄像机的距离进行裁剪阴影,会得到一个物体在SphereCulling内,但是阴影却被裁掉的问题

  • image-20221117201605615

  • 可以结合 ViewSpace 的 Z depth 值进行裁剪处理

  • image-20221117201700252

  • 常规的 Fade 函数

    • d is the surface depth
    • m is the max shadow distance
    • f is a fade range
  • image-20221117201947674

五、Shadow acne(阴影自遮挡问题)

image-20221117202222123

  • DX 裁剪空间 Z坐标是【0,1】,OPENGL 裁剪空间 Z坐标是【-1,1】

  • 反转 Z

    • 由于大部分物体都集中在近裁剪平面的地方,导致深度贴图中记录的数字分布非常不均匀,都集中在靠近0的地方。为了深度图充分利用,和提高精度,可以进行反转 Z,及近平面的值为1,远平面的值为0,DX 下Z坐标为[1,0]

    • image-20221117202439139

六、PCF

  • PCF unity 采样时 ten - filter 通过计算面积占比,进行优化采样,PCF3x3本来需要采样9次,优化后只需要采样4次,即可达到同样的效果,算法使用的是 GPU pro 360 guide to shadows 的方法

image-20221117202606777

七、PCSF

image-20221117202653668

八、资料

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/


文章作者: 血魂S
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 血魂S !
  目录