public void RenderSpotShadow(int index, int splitCount, int tileSize)
        {
            OtherShadow           otherShadow           = otherShadows[index];
            ShadowDrawingSettings shadowDrawingSettings = new ShadowDrawingSettings(cullingResults, otherShadow.VisibleLightIndex);

            cullingResults.ComputeSpotShadowMatricesAndCullingPrimitives(otherShadow.VisibleLightIndex,
                                                                         out Matrix4x4 viewMatrix, out Matrix4x4 projectionMatrix, out ShadowSplitData shadowSplitData);
            shadowDrawingSettings.splitData = shadowSplitData;
            SetShadowMapViewport(index, splitCount, tileSize, out Vector2 offset);
            float inversedSplitCount = 1f / splitCount;

            // !!
            // ??
            float texelSize  = 2f / (tileSize * projectionMatrix.m00);
            float filterSize = texelSize * ((float)shadowSettings.OtherShadowSettings.PCFMode + 1f);
            float bias       = otherShadow.NoramlBias * filterSize * 1.4142136f;

            SetOtherTileData(index, offset, inversedSplitCount, bias);

            otherShadowMatrices[index] = ConvertClipSpaceToTileSpace(projectionMatrix * viewMatrix, offset, inversedSplitCount);
            commandBuffer.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
            commandBuffer.SetGlobalDepthBias(0f, otherShadow.SlopeScaleBias);
            ExecuteBuffer();
            renderContext.DrawShadows(ref shadowDrawingSettings);
            commandBuffer.SetGlobalDepthBias(0f, 0f);
        }
        public Vector4 ReserveOtherShadows(Light light, int visibleLightIndex)
        {
            if (light.shadows == LightShadows.None || light.shadowStrength <= 0)
            {
                return(new Vector4(0f, 0f, 0f, -1f));
            }

            int occlusionMaskChannel            = -1;
            LightBakingOutput lightBakingOutput = light.bakingOutput;

            if (lightBakingOutput.lightmapBakeType == LightmapBakeType.Mixed && lightBakingOutput.mixedLightingMode == MixedLightingMode.Shadowmask)
            {
                useShadowMask        = true;
                occlusionMaskChannel = lightBakingOutput.occlusionMaskChannel;
            }

            bool isPoint       = light.type == LightType.Point;
            int  newLightCount = currentOtherShadowCount + (isPoint ? 6 : 1);

            if (newLightCount >= maxOtherShadaowCount || !cullingResults.GetShadowCasterBounds(visibleLightIndex, out Bounds bounds))
            {
                // the shadow strength of light is negative, see GetDirectionalShadowAttenuation in WindsmoonShadow.hlsl
                // if the stength is positive, the shader may be handle the shadow as realtime shadow
                return(new Vector4(-light.shadowStrength, 0f, 0f, occlusionMaskChannel));
            }

            otherShadows[currentOtherShadowCount] = new OtherShadow()
            {
                VisibleLightIndex = visibleLightIndex,
                SlopeScaleBias    = light.shadowBias,
                NoramlBias        = light.shadowNormalBias,
                IsPoint           = isPoint
            };

            Vector4 data = new Vector4(light.shadowStrength, currentOtherShadowCount, isPoint ? 1f : 0f, occlusionMaskChannel);

            currentOtherShadowCount = newLightCount;
            return(data);
        }
        public void RenderPointShadow(int index, int splitCount, int tileSize)
        {
            OtherShadow           otherShadow           = otherShadows[index];
            ShadowDrawingSettings shadowDrawingSettings = new ShadowDrawingSettings(cullingResults, otherShadow.VisibleLightIndex);

            // !!
            // ??
            float texelSize          = 2f / tileSize;
            float filterSize         = texelSize * ((float)shadowSettings.OtherShadowSettings.PCFMode + 1f);
            float bias               = otherShadow.NoramlBias * filterSize * 1.4142136f;
            float inversedSplitCount = 1f / splitCount;
            float fovBias            = Mathf.Atan(1f + bias + filterSize) * Mathf.Rad2Deg * 2f - 90f;

            for (int i = 0; i < 6; ++i)
            {
                cullingResults.ComputePointShadowMatricesAndCullingPrimitives(otherShadow.VisibleLightIndex, (CubemapFace)i, fovBias,
                                                                              out Matrix4x4 viewMatrix, out Matrix4x4 projectionMatrix, out ShadowSplitData shadowSplitData);

                // ??
                viewMatrix.m11 = -viewMatrix.m11;
                viewMatrix.m12 = -viewMatrix.m12;
                viewMatrix.m13 = -viewMatrix.m13;

                shadowDrawingSettings.splitData = shadowSplitData;
                int tileIndex = index + i;

                SetShadowMapViewport(tileIndex, splitCount, tileSize, out Vector2 offset);
                SetOtherTileData(tileIndex, offset, inversedSplitCount, bias);
                otherShadowMatrices[tileIndex] = ConvertClipSpaceToTileSpace(projectionMatrix * viewMatrix, offset, inversedSplitCount);

                commandBuffer.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
                commandBuffer.SetGlobalDepthBias(0f, otherShadow.SlopeScaleBias);
                ExecuteBuffer();
                renderContext.DrawShadows(ref shadowDrawingSettings);
                commandBuffer.SetGlobalDepthBias(0f, 0f);
            }
        }