public Vector4 ReserveDirectionalShadows(Light light, int visibleLightIndex)
        {
            if (currentDirectionalLightShadowCount < maxDirectionalShadowCount && light.shadows != LightShadows.None && light.shadowStrength > 0f)
            {
                float             maskChannel;
                LightBakingOutput lightBakingOutput = light.bakingOutput;

                if (lightBakingOutput.lightmapBakeType == LightmapBakeType.Mixed && lightBakingOutput.mixedLightingMode == MixedLightingMode.Shadowmask)
                {
                    useShadowMask = true;
                    maskChannel   = lightBakingOutput.occlusionMaskChannel; // can not use the light index because it may be changed in runtime
                }

                else
                {
                    maskChannel = -1;
                }

                // GetShadowCasterBounds now returns true for directional lights even when there is nothing within the shadow range
                if (!cullingResults.GetShadowCasterBounds(visibleLightIndex, out Bounds b))
                {
                    // 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, 0, maskChannel));
                }

                directionalShadows[currentDirectionalLightShadowCount] = new DirectionalShadow()
                {
                    visibleLightIndex = visibleLightIndex, slopeScaleBias = light.shadowBias, nearPlaneOffset = light.shadowNearPlane
                };
                return(new Vector4(light.shadowStrength, shadowSettings.DirectionalShadowSetting.CascadeCount * currentDirectionalLightShadowCount++, light.shadowNormalBias, maskChannel));
            }

            return(new Vector4(0f, 0f, 0f, -1));
        }
        private void RenderDirectionalShadow(int index, int splitCount, int tileSize)
        {
            DirectionalShadow     directionalShadow     = directionalShadows[index];
            ShadowDrawingSettings shadowDrawingSettings = new ShadowDrawingSettings(cullingResults, directionalShadow.visibleLightIndex);

            int     cascadeCount         = shadowSettings.DirectionalShadowSetting.CascadeCount;
            int     tileOffset           = index * cascadeCount;
            Vector3 cascadeRatios        = shadowSettings.DirectionalShadowSetting.CascadeRatios;
            float   cascadeCullingFactor = Mathf.Max(0f, 0.8f - shadowSettings.DirectionalShadowSetting.CascadeFade); // control how much shadow casters will cast shadow in larger cascade
            float   inversedSplitCount   = 1f / splitCount;

            for (int i = 0; i < cascadeCount; ++i)
            {
                // note : the split data contains information about how shadow caster objects should be culled
                cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(directionalShadow.visibleLightIndex, i, cascadeCount,
                                                                                    cascadeRatios, tileSize, directionalShadow.nearPlaneOffset, out Matrix4x4 viewMatrix, out Matrix4x4 projectionMatrix, out ShadowSplitData shadowSplitData);
                shadowSplitData.shadowCascadeBlendCullingFactor = cascadeCullingFactor;
                shadowDrawingSettings.splitData = shadowSplitData;

                if (index == 0) // set culling spheres, all directional light use only one group of culling spheres
                {
                    // note :  as the shadow projections are orthographic and square they end up closely fitting their culling sphere, but also cover some space around them
                    // that's why some shadows can be seen outside the culling regions
                    // also the light direction doesn't matter to the sphere, so all directional lights end up using the same culling spheres
                    // the camera is not at the sphere's center, but the surface of the sphere, all spheres will intersect at this point
                    Vector4 cullingSphere = shadowSplitData.cullingSphere; // w means sphere's radius
                    SetCascadeInfo(i, cullingSphere, tileSize);
                }

                int tileIndex = tileOffset + i;
                SetShadowMapViewport(tileIndex, splitCount, tileSize, out Vector2 offset);
                // Matrix4x4 vpMatrix = projectionMatrix * viewMatrix
                directionalShadowMatrices[tileIndex] = ConvertClipSpaceToTileSpace(projectionMatrix * viewMatrix, offset, inversedSplitCount);
                // directionalShadowMatrices[index] = projectionMatrix * viewMatrix;
                commandBuffer.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
                commandBuffer.SetGlobalDepthBias(0f, directionalShadow.slopeScaleBias);
                ExecuteBuffer();
                renderContext.DrawShadows(ref shadowDrawingSettings);
                commandBuffer.SetGlobalDepthBias(0f, 0f);
            }
        }
        public Vector3 ReserveDirectionalShadows(Light light, int visibleLightIndex)
        {
            // GetShadowCasterBounds  return true if the light affects at least one shadow casting object in the Scene
            if (currentDirectionalLightShadowCount >= maxDirectionalShadowCount || light.shadows == LightShadows.None || light.shadowStrength <= 0f ||
                cullingResults.GetShadowCasterBounds(visibleLightIndex, out Bounds bouds) == false)
            {
                return(Vector3.zero);
            }

            LightBakingOutput lightBakingOutput = light.bakingOutput;

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

            directionalShadows[currentDirectionalLightShadowCount] = new DirectionalShadow()
            {
                visibleLightIndex = visibleLightIndex, slopeScaleBias = light.shadowBias, nearPlaneOffset = light.shadowNearPlane
            };
            return(new Vector3(light.shadowStrength, shadowSettings.DirectionalShadowSetting.CascadeCount * currentDirectionalLightShadowCount++, light.shadowNormalBias));
        }