public void Render(RenderLoop loop, CullResults cullResults, out ShadowOutput packedShadows)
        {
            if (!m_Settings.enabled)
            {
                ClearPackedShadows(cullResults.visibleLights, out packedShadows);
            }

            // Pack all shadow quads into the texture
            if (!AutoPackLightsIntoShadowTexture(GetInputShadowLightData(cullResults), cullResults.visibleLights, out packedShadows))
            {
                // No shadowing lights found, so skip all rendering
                return;
            }

            RenderPackedShadows(loop, cullResults, ref packedShadows);
        }
        //---------------------------------------------------------------------------------------------------------------------------------------------------
        // Render shadows
        //---------------------------------------------------------------------------------------------------------------------------------------------------
        void RenderPackedShadows(RenderLoop loop, CullResults cullResults, ref ShadowOutput packedShadows)
        {
            var setRenderTargetCommandBuffer = new CommandBuffer();

            setRenderTargetCommandBuffer.name = "Render packed shadows";
            setRenderTargetCommandBuffer.GetTemporaryRT(m_ShadowTexName, m_Settings.shadowAtlasWidth, m_Settings.shadowAtlasHeight, k_DepthBuffer, FilterMode.Bilinear, RenderTextureFormat.Shadowmap, RenderTextureReadWrite.Linear);
            setRenderTargetCommandBuffer.SetRenderTarget(new RenderTargetIdentifier(m_ShadowTexName));

            setRenderTargetCommandBuffer.ClearRenderTarget(true, true, Color.green);
            loop.ExecuteCommandBuffer(setRenderTargetCommandBuffer);
            setRenderTargetCommandBuffer.Dispose();

            VisibleLight[] visibleLights = cullResults.visibleLights;
            var            shadowSlices  = packedShadows.shadowSlices;

            // Render each light's shadow buffer into a subrect of the shared depth texture
            for (int lightIndex = 0; lightIndex < packedShadows.shadowLights.Length; lightIndex++)
            {
                int shadowSliceCount = packedShadows.shadowLights[lightIndex].shadowSliceCount;
                if (shadowSliceCount == 0)
                {
                    continue;
                }

                Profiler.BeginSample("Shadows.GetShadowCasterBounds");
                Bounds bounds;
                if (!cullResults.GetShadowCasterBounds(lightIndex, out bounds))
                {
                    Profiler.EndSample();
                    return;
                }
                Profiler.EndSample();

                Profiler.BeginSample("Shadows.DrawShadows");

                Matrix4x4 proj;
                Matrix4x4 view;

                var lightType      = visibleLights[lightIndex].lightType;
                var lightDirection = visibleLights[lightIndex].light.transform.forward;
                var shadowNearClip = visibleLights[lightIndex].light.shadowNearPlane;

                int shadowSliceIndex = packedShadows.GetShadowSliceIndex(lightIndex, 0);

                if (lightType == LightType.Spot)
                {
                    var  settings      = new DrawShadowsSettings(cullResults, lightIndex);
                    bool needRendering = cullResults.ComputeSpotShadowsMatricesAndCullingPrimitives(lightIndex, out view, out proj, out settings.splitData);
                    SetupShadowSplitMatrices(ref packedShadows.shadowSlices[shadowSliceIndex], proj, view);
                    if (needRendering)
                    {
                        RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings);
                    }
                }
                else if (lightType == LightType.Directional)
                {
                    Vector3 splitRatio = m_Settings.directionalLightCascades;

                    for (int s = 0; s < 4; ++s)
                    {
                        packedShadows.directionalShadowSplitSphereSqr[s] = new Vector4(0, 0, 0, float.NegativeInfinity);
                    }

                    for (int s = 0; s < shadowSliceCount; ++s, shadowSliceIndex++)
                    {
                        var  settings         = new DrawShadowsSettings(cullResults, lightIndex);
                        var  shadowResolution = shadowSlices[shadowSliceIndex].shadowResolution;
                        bool needRendering    = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(lightIndex, s, shadowSliceCount, splitRatio, shadowResolution, shadowNearClip, out view, out proj, out settings.splitData);

                        packedShadows.directionalShadowSplitSphereSqr[s]    = settings.splitData.cullingSphere;
                        packedShadows.directionalShadowSplitSphereSqr[s].w *= packedShadows.directionalShadowSplitSphereSqr[s].w;

                        SetupShadowSplitMatrices(ref shadowSlices[shadowSliceIndex], proj, view);
                        if (needRendering)
                        {
                            RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings);
                        }
                    }
                }
                else if (lightType == LightType.Point)
                {
                    for (int s = 0; s < shadowSliceCount; ++s, shadowSliceIndex++)
                    {
                        var  settings      = new DrawShadowsSettings(cullResults, lightIndex);
                        bool needRendering = cullResults.ComputePointShadowsMatricesAndCullingPrimitives(lightIndex, (CubemapFace)s, 2.0f, out view, out proj, out settings.splitData);

                        SetupShadowSplitMatrices(ref shadowSlices[shadowSliceIndex], proj, view);
                        if (needRendering)
                        {
                            RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings);
                        }
                    }
                }
                Profiler.EndSample();
            }
        }
 public static void ClearPackedShadows(VisibleLight[] lights, out ShadowOutput packedShadows)
 {
     packedShadows.directionalShadowSplitSphereSqr = null;
     packedShadows.shadowSlices = null;
     packedShadows.shadowLights = new ShadowLight[lights.Length];
 }
        //---------------------------------------------------------------------------------------------------------------------------------------------------
        bool AutoPackLightsIntoShadowTexture(List <InputShadowLightData> shadowLights, VisibleLight[] lights, out ShadowOutput packedShadows)
        {
            var activeShadowLights = new Dictionary <int, InputShadowLightData>();
            var shadowIndices      = new List <int>();

            //@TODO: Disallow multiple directional lights

            for (int i = 0; i < shadowLights.Count; i++)
            {
                shadowIndices.Add(shadowLights[i].lightIndex);
                activeShadowLights[shadowLights[i].lightIndex] = shadowLights[i];
            }

            // World's stupidest sheet packer:
            //    1. Sort all lights from largest to smallest
            //    2. In a left->right, top->bottom pattern, fill quads until you reach the edge of the texture
            //    3. Move position to x=0, y=bottomOfFirstTextureInThisRow
            //    4. Goto 2.
            // Yes, this will produce holes as the quads shrink, but it's good enough for now. I'll work on this more later to fill the gaps.

            // Sort all lights from largest to smallest
            shadowIndices.Sort(
                delegate(int l1, int l2)
            {
                var nCompare = 0;
                // Sort shadow-casting lights by shadow resolution
                nCompare = activeShadowLights[l1].shadowResolution.CompareTo(activeShadowLights[l2].shadowResolution); // Sort by shadow size

                if (nCompare == 0)                                                                                     // Same, so sort by range to stabilize sort results
                {
                    nCompare = lights[l1].range.CompareTo(lights[l2].range);                                           // Sort by shadow size
                }
                if (nCompare == 0)                                                                                     // Still same, so sort by instance ID to stabilize sort results
                {
                    nCompare = lights[l1].light.GetInstanceID().CompareTo(lights[l2].light.GetInstanceID());
                }

                return(nCompare);
            }
                );

            // Start filling lights into texture
            var requestedPages = new List <AtlasEntry>();

            packedShadows.shadowLights = new ShadowLight[lights.Length];
            for (int i = 0; i != shadowIndices.Count; i++)
            {
                var numShadowSplits = CalculateNumShadowSplits(shadowIndices[i], lights);

                packedShadows.shadowLights[shadowIndices[i]].shadowSliceCount = numShadowSplits;
                packedShadows.shadowLights[shadowIndices[i]].shadowSliceIndex = requestedPages.Count;

                for (int s = 0; s < numShadowSplits; s++)
                {
                    requestedPages.Add(new AtlasEntry(requestedPages.Count, shadowIndices[i]));
                }
            }

            var nCurrentX = 0;
            var nCurrentY = -1;
            var nNextY    = 0;

            packedShadows.shadowSlices = new ShadowSliceData[requestedPages.Count];
            packedShadows.directionalShadowSplitSphereSqr = new Vector4[4];

            foreach (var entry in requestedPages)
            {
                var shadowResolution = activeShadowLights[entry.lightIndex].shadowResolution;

                // Check if first texture is too wide
                if (nCurrentY == -1)
                {
                    if ((shadowResolution > m_Settings.shadowAtlasWidth) || (shadowResolution > m_Settings.shadowAtlasHeight))
                    {
                        Debug.LogError("ERROR! Shadow packer ran out of space in the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n");
                        m_FailedToPackLastTime = true;
                        ClearPackedShadows(lights, out packedShadows);
                        return(false);
                    }
                }

                // Goto next scanline
                if ((nCurrentY == -1) || ((nCurrentX + shadowResolution) > m_Settings.shadowAtlasWidth))
                {
                    nCurrentX = 0;
                    nCurrentY = nNextY;
                    nNextY   += shadowResolution;
                }

                // Check if we've run out of space
                if ((nCurrentY + shadowResolution) > m_Settings.shadowAtlasHeight)
                {
                    Debug.LogError("ERROR! Shadow packer ran out of space in the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n");
                    m_FailedToPackLastTime = true;
                    ClearPackedShadows(lights, out packedShadows);
                    return(false);
                }

                // Save location to light
                packedShadows.shadowSlices[entry.splitIndex].atlasX           = nCurrentX;
                packedShadows.shadowSlices[entry.splitIndex].atlasY           = nCurrentY;
                packedShadows.shadowSlices[entry.splitIndex].shadowResolution = shadowResolution;

                // Move ahead
                nCurrentX += shadowResolution;

                //Debug.Log( "Sheet packer: " + vl.m_cachedLight.name + " ( " + vl.m_shadowX + ", " + vl.m_shadowY + " ) " + vl.m_shadowResolution + "\n\n" );
            }

            if (m_FailedToPackLastTime)
            {
                m_FailedToPackLastTime = false;
                Debug.Log("SUCCESS! Shadow packer can now fit all lights into the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n");
            }

            return(requestedPages.Count != 0);
        }
        //---------------------------------------------------------------------------------------------------------------------------------------------------
        void UpdateLightConstants(VisibleLight[] visibleLights, ref ShadowOutput shadow)
        {
            var numLightsIncludingTooMany = 0;

            var numLights = 0;

            var lightColor = new Vector4[k_MaxLights];
            var lightPosition_invRadius        = new Vector4[k_MaxLights];
            var lightDirection                 = new Vector4[k_MaxLights];
            var lightShadowIndex_lightParams   = new Vector4[k_MaxLights];
            var lightFalloffParams             = new Vector4[k_MaxLights];
            var spotLightInnerOuterConeCosines = new Vector4[k_MaxLights];
            var matWorldToShadow               = new Matrix4x4[k_MaxLights * k_MaxShadowmapPerLights];
            var dirShadowSplitSpheres          = new Vector4[k_MaxDirectionalSplit];

            for (int nLight = 0; nLight < visibleLights.Length; nLight++)
            {
                numLightsIncludingTooMany++;
                if (numLightsIncludingTooMany > k_MaxLights)
                {
                    continue;
                }

                var light               = visibleLights[nLight];
                var lightType           = light.lightType;
                var position            = light.light.transform.position;
                var lightDir            = light.light.transform.forward.normalized;
                var additionalLightData = light.light.GetComponent <AdditionalLightData>();

                // Setup shadow data arrays
                var hasShadows = shadow.GetShadowSliceCountLightIndex(nLight) != 0;

                if (lightType == LightType.Directional)
                {
                    lightColor[numLights] = light.finalColor;
                    lightPosition_invRadius[numLights] = new Vector4(
                        position.x - (lightDir.x * k_DirectionalLightPullbackDistance),
                        position.y - (lightDir.y * k_DirectionalLightPullbackDistance),
                        position.z - (lightDir.z * k_DirectionalLightPullbackDistance),
                        -1.0f);
                    lightDirection[numLights] = new Vector4(lightDir.x, lightDir.y, lightDir.z);
                    lightShadowIndex_lightParams[numLights]   = new Vector4(0, 0, 1, 1);
                    lightFalloffParams[numLights]             = new Vector4(0.0f, 0.0f, float.MaxValue, (float)lightType);
                    spotLightInnerOuterConeCosines[numLights] = new Vector4(0.0f, -1.0f, 1.0f);

                    if (hasShadows)
                    {
                        for (int s = 0; s < k_MaxDirectionalSplit; ++s)
                        {
                            dirShadowSplitSpheres[s] = shadow.directionalShadowSplitSphereSqr[s];
                        }
                    }
                }
                else if (lightType == LightType.Point)
                {
                    lightColor[numLights] = light.finalColor;

                    lightPosition_invRadius[numLights]        = new Vector4(position.x, position.y, position.z, 1.0f / light.range);
                    lightDirection[numLights]                 = new Vector4(0.0f, 0.0f, 0.0f);
                    lightShadowIndex_lightParams[numLights]   = new Vector4(0, 0, 1, 1);
                    lightFalloffParams[numLights]             = new Vector4(1.0f, 0.0f, light.range * light.range, (float)lightType);
                    spotLightInnerOuterConeCosines[numLights] = new Vector4(0.0f, -1.0f, 1.0f);
                }
                else if (lightType == LightType.Spot)
                {
                    lightColor[numLights] = light.finalColor;
                    lightPosition_invRadius[numLights]      = new Vector4(position.x, position.y, position.z, 1.0f / light.range);
                    lightDirection[numLights]               = new Vector4(lightDir.x, lightDir.y, lightDir.z);
                    lightShadowIndex_lightParams[numLights] = new Vector4(0, 0, 1, 1);
                    lightFalloffParams[numLights]           = new Vector4(1.0f, 0.0f, light.range * light.range, (float)lightType);

                    var flInnerConePercent = AdditionalLightData.GetInnerSpotPercent01(additionalLightData);
                    var spotAngle          = light.light.spotAngle;
                    var flPhiDot           = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * Mathf.Deg2Rad), 0.0f, 1.0f);                      // outer cone
                    var flThetaDot         = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * flInnerConePercent * Mathf.Deg2Rad), 0.0f, 1.0f); // inner cone
                    spotLightInnerOuterConeCosines[numLights] = new Vector4(flThetaDot, flPhiDot, 1.0f / Mathf.Max(0.01f, flThetaDot - flPhiDot));
                }

                if (hasShadows)
                {
                    // Enable shadows
                    lightShadowIndex_lightParams[numLights].x = 1;
                    for (int s = 0; s < shadow.GetShadowSliceCountLightIndex(nLight); ++s)
                    {
                        var shadowSliceIndex = shadow.GetShadowSliceIndex(nLight, s);
                        matWorldToShadow[numLights * k_MaxShadowmapPerLights + s] = shadow.shadowSlices[shadowSliceIndex].shadowTransform.transpose;
                    }
                }

                numLights++;
            }

            // Warn if too many lights found
            if (numLightsIncludingTooMany > k_MaxLights)
            {
                if (numLightsIncludingTooMany > m_WarnedTooManyLights)
                {
                    Debug.LogError("ERROR! Found " + numLightsIncludingTooMany + " runtime lights! Valve renderer supports up to " + k_MaxLights +
                                   " active runtime lights at a time!\nDisabling " + (numLightsIncludingTooMany - k_MaxLights) + " runtime light" +
                                   ((numLightsIncludingTooMany - k_MaxLights) > 1 ? "s" : "") + "!\n");
                }
                m_WarnedTooManyLights = numLightsIncludingTooMany;
            }
            else
            {
                if (m_WarnedTooManyLights > 0)
                {
                    m_WarnedTooManyLights = 0;
                    Debug.Log("SUCCESS! Found " + numLightsIncludingTooMany + " runtime lights which is within the supported number of lights, " + k_MaxLights + ".\n\n");
                }
            }

            // Send constants to shaders
            Shader.SetGlobalInt("g_nNumLights", numLights);

            // New method for Unity 5.4 to set arrays of constants
            Shader.SetGlobalVectorArray("g_vLightPosition_flInvRadius", lightPosition_invRadius);
            Shader.SetGlobalVectorArray("g_vLightColor", lightColor);
            Shader.SetGlobalVectorArray("g_vLightDirection", lightDirection);
            Shader.SetGlobalVectorArray("g_vLightShadowIndex_vLightParams", lightShadowIndex_lightParams);
            Shader.SetGlobalVectorArray("g_vLightFalloffParams", lightFalloffParams);
            Shader.SetGlobalVectorArray("g_vSpotLightInnerOuterConeCosines", spotLightInnerOuterConeCosines);
            Shader.SetGlobalMatrixArray("g_matWorldToShadow", matWorldToShadow);
            Shader.SetGlobalVectorArray("g_vDirShadowSplitSpheres", dirShadowSplitSpheres);

            // Time
            #if (UNITY_EDITOR)
            {
                Shader.SetGlobalFloat("g_flTime", Time.realtimeSinceStartup);
                //Debug.Log( "Time " + Time.realtimeSinceStartup );
            }
            #else
            {
                Shader.SetGlobalFloat("g_flTime", Time.timeSinceLevelLoad);
                //Debug.Log( "Time " + Time.timeSinceLevelLoad );
            }
            #endif

            // PCF 3x3 Shadows
            var texelEpsilonX      = 1.0f / m_ShadowSettings.shadowAtlasWidth;
            var texelEpsilonY      = 1.0f / m_ShadowSettings.shadowAtlasHeight;
            var shadow3x3PCFTerms0 = new Vector4(20.0f / 267.0f, 33.0f / 267.0f, 55.0f / 267.0f, 0.0f);
            var shadow3x3PCFTerms1 = new Vector4(texelEpsilonX, texelEpsilonY, -texelEpsilonX, -texelEpsilonY);
            var shadow3x3PCFTerms2 = new Vector4(texelEpsilonX, texelEpsilonY, 0.0f, 0.0f);
            var shadow3x3PCFTerms3 = new Vector4(-texelEpsilonX, -texelEpsilonY, 0.0f, 0.0f);

            Shader.SetGlobalVector("g_vShadow3x3PCFTerms0", shadow3x3PCFTerms0);
            Shader.SetGlobalVector("g_vShadow3x3PCFTerms1", shadow3x3PCFTerms1);
            Shader.SetGlobalVector("g_vShadow3x3PCFTerms2", shadow3x3PCFTerms2);
            Shader.SetGlobalVector("g_vShadow3x3PCFTerms3", shadow3x3PCFTerms3);
        }