// Function to cleanup all the data stored in the LPV grid
    private void LPVGridCleanup(ref LPVCascade cascade)
    {
        int kernelHandle = lpvCleanupShader.FindKernel("CSMain");

        if (backBuffering)
        {
            if (bDisplayBackBuffer)
            {
                lpvCleanupShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSH);
                lpvCleanupShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSH);
                lpvCleanupShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSH);
                lpvCleanupShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminance);
            }
            else
            {
                lpvCleanupShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSHBackBuffer);
                lpvCleanupShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSHBackBuffer);
                lpvCleanupShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSHBackBuffer);
                lpvCleanupShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminanceBackBuffer);
            }
        }
        else
        {
            lpvCleanupShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSH);
            lpvCleanupShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSH);
            lpvCleanupShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSH);
            lpvCleanupShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminance);
        }

        lpvCleanupShader.Dispatch(kernelHandle, lpvDimension, lpvDimension, lpvDimension);
    }
    // Function to initialize an LPV cascade
    private void InitializeLPVCascade(ref LPVCascade cascade)
    {
        cascade.lpvRedSH     = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvGreenSH   = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvBlueSH    = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvLuminance = new RenderTexture(lpvTextureDescriptorLuminance);

        cascade.lpvRedPropagationBuffer   = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvGreenPropagationBuffer = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvBluePropagationBuffer  = new RenderTexture(lpvTextureDescriptorSH);

        cascade.lpvRedSHBackBuffer     = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvGreenSHBackBuffer   = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvBlueSHBackBuffer    = new RenderTexture(lpvTextureDescriptorSH);
        cascade.lpvLuminanceBackBuffer = new RenderTexture(lpvTextureDescriptorLuminance);

        cascade.lpvRedSH.filterMode     = FilterMode.Trilinear;
        cascade.lpvGreenSH.filterMode   = FilterMode.Trilinear;
        cascade.lpvBlueSH.filterMode    = FilterMode.Trilinear;
        cascade.lpvLuminance.filterMode = FilterMode.Trilinear;

        cascade.lpvRedPropagationBuffer.filterMode   = FilterMode.Trilinear;
        cascade.lpvGreenPropagationBuffer.filterMode = FilterMode.Trilinear;
        cascade.lpvBluePropagationBuffer.filterMode  = FilterMode.Trilinear;

        cascade.lpvRedSHBackBuffer.filterMode     = FilterMode.Trilinear;
        cascade.lpvGreenSHBackBuffer.filterMode   = FilterMode.Trilinear;
        cascade.lpvBlueSHBackBuffer.filterMode    = FilterMode.Trilinear;
        cascade.lpvLuminanceBackBuffer.filterMode = FilterMode.Trilinear;

        cascade.lpvRedSH.Create();
        cascade.lpvGreenSH.Create();
        cascade.lpvBlueSH.Create();
        cascade.lpvLuminance.Create();

        cascade.lpvRedPropagationBuffer.Create();
        cascade.lpvGreenPropagationBuffer.Create();
        cascade.lpvBluePropagationBuffer.Create();

        cascade.lpvRedSHBackBuffer.Create();
        cascade.lpvGreenSHBackBuffer.Create();
        cascade.lpvBlueSHBackBuffer.Create();
        cascade.lpvLuminanceBackBuffer.Create();
    }
    // Function to propagate the lighting stored as spherical harmonics in the LPV grid to its neightbouring cells
    private void LPVGridPropagation(ref LPVCascade cascade)
    {
        int kernelHandle = lpvPropagationShader.FindKernel("CSMain");

        lpvPropagationShader.SetTexture(kernelHandle, "lpvRedSHOutput", cascade.lpvRedPropagationBuffer);
        lpvPropagationShader.SetTexture(kernelHandle, "lpvGreenSHOutput", cascade.lpvGreenPropagationBuffer);
        lpvPropagationShader.SetTexture(kernelHandle, "lpvBlueSHOutput", cascade.lpvBluePropagationBuffer);

        if (backBuffering)
        {
            if (bDisplayBackBuffer)
            {
                lpvPropagationShader.SetTexture(kernelHandle, "lpvRedSHInput", cascade.lpvRedSH);
                lpvPropagationShader.SetTexture(kernelHandle, "lpvGreenSHInput", cascade.lpvGreenSH);
                lpvPropagationShader.SetTexture(kernelHandle, "lpvBlueSHInput", cascade.lpvBlueSH);
            }
            else
            {
                lpvPropagationShader.SetTexture(kernelHandle, "lpvRedSHInput", cascade.lpvRedSHBackBuffer);
                lpvPropagationShader.SetTexture(kernelHandle, "lpvGreenSHInput", cascade.lpvGreenSHBackBuffer);
                lpvPropagationShader.SetTexture(kernelHandle, "lpvBlueSHInput", cascade.lpvBlueSHBackBuffer);
            }
        }
        else
        {
            lpvPropagationShader.SetTexture(kernelHandle, "lpvRedSHInput", cascade.lpvRedSH);
            lpvPropagationShader.SetTexture(kernelHandle, "lpvGreenSHInput", cascade.lpvGreenSH);
            lpvPropagationShader.SetTexture(kernelHandle, "lpvBlueSHInput", cascade.lpvBlueSH);
        }

        lpvPropagationShader.SetInt("lpvDimension", lpvDimension);
        lpvPropagationShader.Dispatch(kernelHandle, lpvDimension, lpvDimension, lpvDimension);

        kernelHandle = lpvPropagationCompositionShader.FindKernel("CSMain");

        lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvRedSHInput", cascade.lpvRedPropagationBuffer);
        lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvGreenSHInput", cascade.lpvGreenPropagationBuffer);
        lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvBlueSHInput", cascade.lpvBluePropagationBuffer);

        if (backBuffering)
        {
            if (bDisplayBackBuffer)
            {
                lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvRedSHOutput", cascade.lpvRedSH);
                lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvGreenSHOutput", cascade.lpvGreenSH);
                lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvBlueSHOutput", cascade.lpvBlueSH);
            }
            else
            {
                lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvRedSHOutput", cascade.lpvRedSHBackBuffer);
                lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvGreenSHOutput", cascade.lpvGreenSHBackBuffer);
                lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvBlueSHOutput", cascade.lpvBlueSHBackBuffer);
            }
        }
        else
        {
            lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvRedSHOutput", cascade.lpvRedSH);
            lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvGreenSHOutput", cascade.lpvGreenSH);
            lpvPropagationCompositionShader.SetTexture(kernelHandle, "lpvBlueSHOutput", cascade.lpvBlueSH);
        }

        lpvPropagationCompositionShader.Dispatch(kernelHandle, lpvDimension, lpvDimension, lpvDimension);
    }
    // Function to inject the vpl data into LPV grid as spherical harmonics
    private void LPVGridInjection(ref LPVCascade cascade, float cascadeBoundary)
    {
        int kernelHandle = lpvInjectionShader.FindKernel("CSMain");

        if (rsmVPLInjection)
        {
            for (int i = 0; i < rsmCameras.Count; ++i)
            {
                if (rsmCameras [i] != null)
                {
                    // RSM textures injection
                    if (backBuffering)
                    {
                        if (bDisplayBackBuffer)
                        {
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSH);
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSH);
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSH);
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminance);
                        }
                        else
                        {
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSHBackBuffer);
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSHBackBuffer);
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSHBackBuffer);
                            lpvInjectionShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminanceBackBuffer);
                        }
                    }
                    else
                    {
                        lpvInjectionShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSH);
                        lpvInjectionShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSH);
                        lpvInjectionShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSH);
                        lpvInjectionShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminance);
                    }

                    lpvInjectionShader.SetInt("lpvDimension", lpvDimension);
                    lpvInjectionShader.SetFloat("worldVolumeBoundary", cascadeBoundary);
                    lpvInjectionShader.SetTexture(kernelHandle, "lightingTexture", rsmCameras[i].GetComponent <RSMCameraScript>().lightingTexture);
                    lpvInjectionShader.SetTexture(kernelHandle, "positionTexture", rsmCameras[i].GetComponent <RSMCameraScript>().positionTexture);
                    lpvInjectionShader.SetTexture(kernelHandle, "normalTexture", rsmCameras[i].GetComponent <RSMCameraScript>().normalTexture);
                    lpvInjectionShader.Dispatch(kernelHandle, rsmCameras[i].GetComponent <RSMCameraScript>().resolution.x, rsmCameras[i].GetComponent <RSMCameraScript>().resolution.y, 1);
                }
            }
        }

        if (screenSpaceVPLInjection)
        {
            // Screen textures injection
            // RSM textures injection
            if (backBuffering)
            {
                if (bDisplayBackBuffer)
                {
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSH);
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSH);
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSH);
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminance);
                }
                else
                {
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSHBackBuffer);
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSHBackBuffer);
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSHBackBuffer);
                    lpvInjectionShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminanceBackBuffer);
                }
            }
            else
            {
                lpvInjectionShader.SetTexture(kernelHandle, "lpvRedSH", cascade.lpvRedSH);
                lpvInjectionShader.SetTexture(kernelHandle, "lpvGreenSH", cascade.lpvGreenSH);
                lpvInjectionShader.SetTexture(kernelHandle, "lpvBlueSH", cascade.lpvBlueSH);
                lpvInjectionShader.SetTexture(kernelHandle, "lpvLuminance", cascade.lpvLuminance);
            }

            lpvInjectionShader.SetInt("lpvDimension", lpvDimension);
            lpvInjectionShader.SetFloat("cascadeBoundary", cascadeBoundary);
            lpvInjectionShader.SetTexture(kernelHandle, "lightingTexture", lightingTexture);
            lpvInjectionShader.SetTexture(kernelHandle, "positionTexture", positionTexture);
            lpvInjectionShader.SetTexture(kernelHandle, "normalTexture", normalTexture);
            lpvInjectionShader.Dispatch(kernelHandle, screenSpaceVPLTextureResolution.x, screenSpaceVPLTextureResolution.y, 1);
        }
    }