/// <summary>
        /// Function that returns the ray tracing and path tracing effects that are enabled for a given camera.
        /// </summary>
        /// <param name="hdCamera">The input camera</param>
        /// <param name="rayTracedShadows">Flag that defines if at least one light has ray traced shadows.</param>
        /// <param name="rayTracedContactShadows">Flag that defines if at least one light has ray traced contact shadows</param>
        /// <returns>HDEffectsParameters type.</returns>
        public static HDEffectsParameters EvaluateEffectsParameters(HDCamera hdCamera, bool rayTracedShadows, bool rayTracedContactShadows)
        {
            HDEffectsParameters parameters = new HDEffectsParameters();

            // Aggregate the shadow requirements
            parameters.shadows = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ScreenSpaceShadows) && (rayTracedShadows || rayTracedContactShadows);

            // Aggregate the ambient occlusion parameters
            AmbientOcclusion aoSettings = hdCamera.volumeStack.GetComponent <AmbientOcclusion>();

            parameters.ambientOcclusion = aoSettings.rayTracing.value && hdCamera.frameSettings.IsEnabled(FrameSettingsField.SSAO);
            parameters.aoLayerMask      = aoSettings.layerMask.value;

            // Aggregate the reflections parameters
            ScreenSpaceReflection reflSettings = hdCamera.volumeStack.GetComponent <ScreenSpaceReflection>();

            parameters.reflections   = reflSettings.enabled.value && ScreenSpaceReflection.RayTracingActive(reflSettings) && hdCamera.frameSettings.IsEnabled(FrameSettingsField.SSR);
            parameters.reflLayerMask = reflSettings.layerMask.value;

            // Aggregate the global illumination parameters
            GlobalIllumination giSettings = hdCamera.volumeStack.GetComponent <GlobalIllumination>();

            parameters.globalIllumination = giSettings.enable.value && GlobalIllumination.RayTracingActive(giSettings) && hdCamera.frameSettings.IsEnabled(FrameSettingsField.SSGI);
            parameters.giLayerMask        = giSettings.layerMask.value;

            // Aggregate the global illumination parameters
            RecursiveRendering recursiveSettings = hdCamera.volumeStack.GetComponent <RecursiveRendering>();

            parameters.recursiveRendering = recursiveSettings.enable.value;
            parameters.recursiveLayerMask = recursiveSettings.layerMask.value;

            // Aggregate the sub surface parameters
            SubSurfaceScattering sssSettings = hdCamera.volumeStack.GetComponent <SubSurfaceScattering>();

            parameters.subSurface = sssSettings.rayTracing.value && hdCamera.frameSettings.IsEnabled(FrameSettingsField.SubsurfaceScattering);

            // Aggregate the path parameters
            PathTracing pathTracingSettings = hdCamera.volumeStack.GetComponent <PathTracing>();

            parameters.pathTracing = pathTracingSettings.enable.value;
            parameters.ptLayerMask = pathTracingSettings.layerMask.value;

            // We need to check if at least one effect will require the acceleration structure
            parameters.rayTracingRequired = parameters.ambientOcclusion || parameters.reflections ||
                                            parameters.globalIllumination || parameters.recursiveRendering || parameters.subSurface ||
                                            parameters.pathTracing || parameters.shadows;

            // Return the result
            return(parameters);
        }
        internal void BuildRayTracingAccelerationStructure(HDCamera hdCamera)
        {
            // Resets the rtas manager
            m_RTASManager.Reset();

            // Resets the light lists
            m_RayTracingLights.Reset();

            // Reset all the flags
            m_ValidRayTracingState            = false;
            m_ValidRayTracingCluster          = false;
            m_ValidRayTracingClusterCulling   = false;
            m_RayTracedShadowsRequired        = false;
            m_RayTracedContactShadowsRequired = false;

            // If the camera does not have a ray tracing frame setting or it is a preview camera (due to the fact that the sphere does not exist as a game object we can't create the RTAS) we do not want to build a RTAS
            if (!hdCamera.frameSettings.IsEnabled(FrameSettingsField.RayTracing))
            {
                return;
            }

            // Collect the lights
            CollectLightsForRayTracing(hdCamera, ref m_RTASManager.transformsDirty);

            // Evaluate the parameters of the effects
            HDEffectsParameters effectParameters = EvaluateEffectsParameters(hdCamera, m_RayTracedShadowsRequired, m_RayTracedContactShadowsRequired);

            if (!effectParameters.rayTracingRequired)
            {
                return;
            }

            // Grab the ray tracing settings
            RayTracingSettings rtSettings = hdCamera.volumeStack.GetComponent <RayTracingSettings>();

#if UNITY_EDITOR
            if (rtSettings.buildMode.value == RTASBuildMode.Automatic || hdCamera.camera.cameraType == CameraType.SceneView)
#else
            if (rtSettings.buildMode.value == RTASBuildMode.Automatic)
#endif
            {
                // Cull the scene for the RTAS
                RayTracingInstanceCullingResults cullingResults = m_RTASManager.Cull(hdCamera, effectParameters);

                // Update the material dirtiness for the PT
                if (effectParameters.pathTracing)
                {
                    m_RTASManager.transformsDirty |= cullingResults.transformsChanged;
                    for (int i = 0; i < cullingResults.materialsCRC.Length; i++)
                    {
                        RayTracingInstanceMaterialCRC matCRC = cullingResults.materialsCRC[i];
                        m_RTASManager.materialsDirty |= UpdateMaterialCRC(matCRC.instanceID, matCRC.crc);
                    }
                }

                // Build the ray tracing acceleration structure
                m_RTASManager.Build(hdCamera);

                // tag the structures as valid
                m_ValidRayTracingState = true;
            }
            else
            {
                // If the user fed a non null ray tracing acceleration structure, then we are all set.
                if (hdCamera.rayTracingAccelerationStructure != null)
                {
                    m_ValidRayTracingState = true;
                }
            }
        }
        /// <summary>
        /// Function that adds a renderer to a ray tracing acceleration structure.
        /// </summary>
        /// <param name="targetRTAS">Ray Tracing Acceleration structure the renderer should be added to.</param>
        /// <param name="currentRenderer">The renderer that should be added to the RTAS.</param>
        /// <param name="effectsParameters">Structure defining the enabled ray tracing and path tracing effects for a camera.</param>
        /// <param name="transformDirty">Flag that indicates if the renderer's transform has changed.</param>
        /// <param name="materialsDirty">Flag that indicates if any of the renderer's materials have changed.</param>
        /// <returns></returns>
        public static AccelerationStructureStatus AddInstanceToRAS(RayTracingAccelerationStructure targetRTAS, Renderer currentRenderer, HDEffectsParameters effectsParameters, ref bool transformDirty, ref bool materialsDirty)
        {
            // Get all the materials of the mesh renderer
            currentRenderer.GetSharedMaterials(materialArray);
            // If the array is null, we are done
            if (materialArray == null)
            {
                return(AccelerationStructureStatus.NullMaterial);
            }

            // For every sub-mesh/sub-material let's build the right flags
            int numSubMeshes = 1;

            if (!(currentRenderer.GetType() == typeof(SkinnedMeshRenderer)))
            {
                currentRenderer.TryGetComponent(out MeshFilter meshFilter);
                if (meshFilter == null || meshFilter.sharedMesh == null)
                {
                    return(AccelerationStructureStatus.MissingMesh);
                }
                numSubMeshes = meshFilter.sharedMesh.subMeshCount;
            }
            else
            {
                SkinnedMeshRenderer skinnedMesh = (SkinnedMeshRenderer)currentRenderer;
                if (skinnedMesh.sharedMesh == null)
                {
                    return(AccelerationStructureStatus.MissingMesh);
                }
                numSubMeshes = skinnedMesh.sharedMesh.subMeshCount;
            }

            // Let's clamp the number of sub-meshes to avoid throwing an unwanted error
            numSubMeshes = Mathf.Min(numSubMeshes, maxNumSubMeshes);

            // Get the layer of this object
            int objectLayerValue = 1 << currentRenderer.gameObject.layer;

            // We need to build the instance flag for this renderer
            uint instanceFlag = 0x00;

            bool doubleSided = false;
            bool materialIsOnlyTransparent = true;
            bool hasTransparentSubMaterial = false;

            // We disregard the ray traced shadows option when in Path Tracing
            bool rayTracedShadow = effectsParameters.shadows && !effectsParameters.pathTracing;

            // Deactivate Path Tracing if the object does not belong to the path traced layer(s)
            bool pathTracing = effectsParameters.pathTracing && (bool)((effectsParameters.ptLayerMask & objectLayerValue) != 0);

            for (int meshIdx = 0; meshIdx < numSubMeshes; ++meshIdx)
            {
                // Initially we consider the potential mesh as invalid
                bool validMesh = false;
                if (materialArray.Count > meshIdx)
                {
                    // Grab the material for the current sub-mesh
                    Material currentMaterial = materialArray[meshIdx];

                    // Make sure that the material is HDRP's and non-decal
                    if (IsValidRayTracedMaterial(currentMaterial))
                    {
                        // Mesh is valid given that all requirements are ok
                        validMesh = true;

                        // First mark the thing as valid
                        subMeshFlagArray[meshIdx] = RayTracingSubMeshFlags.Enabled;

                        // Evaluate what kind of materials we are dealing with
                        bool alphaTested         = IsAlphaTestedMaterial(currentMaterial);
                        bool transparentMaterial = IsTransparentMaterial(currentMaterial);

                        // Aggregate the transparency info
                        materialIsOnlyTransparent &= transparentMaterial;
                        hasTransparentSubMaterial |= transparentMaterial;

                        // Append the additional flags depending on what kind of sub mesh this is
                        if (!transparentMaterial && !alphaTested)
                        {
                            subMeshFlagArray[meshIdx] |= RayTracingSubMeshFlags.ClosestHitOnly;
                        }
                        else if (transparentMaterial)
                        {
                            subMeshFlagArray[meshIdx] |= RayTracingSubMeshFlags.UniqueAnyHitCalls;
                        }

                        // Check if we want to enable double-sidedness for the mesh
                        // (note that a mix of single and double-sided materials will result in a double-sided mesh in the AS)
                        doubleSided |= currentMaterial.doubleSidedGI || currentMaterial.IsKeywordEnabled("_DOUBLESIDED_ON");

                        // Check if the material has changed since last time we were here
                        if (!materialsDirty)
                        {
                            materialsDirty |= UpdateMaterialCRC(currentMaterial.GetInstanceID(), currentMaterial.ComputeCRC());
                        }
                    }
                }

                // If the mesh was not valid, exclude it (without affecting sidedness)
                if (!validMesh)
                {
                    subMeshFlagArray[meshIdx] = RayTracingSubMeshFlags.Disabled;
                }
            }

            // If the material is considered opaque, every sub-mesh has to be enabled and with unique any hit calls
            if (!materialIsOnlyTransparent && hasTransparentSubMaterial)
            {
                for (int meshIdx = 0; meshIdx < numSubMeshes; ++meshIdx)
                {
                    subMeshFlagArray[meshIdx] = RayTracingSubMeshFlags.Enabled | RayTracingSubMeshFlags.UniqueAnyHitCalls;
                }
            }


            // Propagate the opacity mask only if all sub materials are opaque
            bool isOpaque = !hasTransparentSubMaterial;

            if (isOpaque)
            {
                instanceFlag |= (uint)(RayTracingRendererFlag.Opaque);
            }

            if (rayTracedShadow || pathTracing)
            {
                if (hasTransparentSubMaterial)
                {
                    // Raise the shadow casting flag if needed
                    instanceFlag |= ((currentRenderer.shadowCastingMode != ShadowCastingMode.Off) ? (uint)(RayTracingRendererFlag.CastShadowTransparent) : 0x00);
                }
                else
                {
                    // Raise the shadow casting flag if needed
                    instanceFlag |= ((currentRenderer.shadowCastingMode != ShadowCastingMode.Off) ? (uint)(RayTracingRendererFlag.CastShadowOpaque) : 0x00);
                }
            }

            // We consider a mesh visible by reflection, gi, etc if it is not in the shadow only mode.
            bool meshIsVisible = currentRenderer.shadowCastingMode != ShadowCastingMode.ShadowsOnly;

            if (effectsParameters.ambientOcclusion && !materialIsOnlyTransparent && meshIsVisible)
            {
                // Raise the Ambient Occlusion flag if needed
                instanceFlag |= ((effectsParameters.aoLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.AmbientOcclusion) : 0x00;
            }

            if (effectsParameters.reflections && !materialIsOnlyTransparent && meshIsVisible)
            {
                // Raise the Screen Space Reflection flag if needed
                instanceFlag |= ((effectsParameters.reflLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.Reflection) : 0x00;
            }

            if (effectsParameters.globalIllumination && !materialIsOnlyTransparent && meshIsVisible)
            {
                // Raise the Global Illumination flag if needed
                instanceFlag |= ((effectsParameters.giLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.GlobalIllumination) : 0x00;
            }

            if (effectsParameters.recursiveRendering && meshIsVisible)
            {
                // Raise the Recursive Rendering flag if needed
                instanceFlag |= ((effectsParameters.recursiveLayerMask & objectLayerValue) != 0) ? (uint)(RayTracingRendererFlag.RecursiveRendering) : 0x00;
            }

            if (effectsParameters.pathTracing && meshIsVisible)
            {
                // Raise the Path Tracing flag if needed
                instanceFlag |= (uint)(RayTracingRendererFlag.PathTracing);
            }

            // If the object was not referenced
            if (instanceFlag == 0)
            {
                return(AccelerationStructureStatus.Added);
            }

            // Add it to the acceleration structure
            targetRTAS.AddInstance(currentRenderer, subMeshFlags: subMeshFlagArray, enableTriangleCulling: !doubleSided, mask: instanceFlag);

            // Indicates that a transform has changed in our scene (mesh or light)
            transformDirty |= currentRenderer.transform.hasChanged;
            currentRenderer.transform.hasChanged = false;

            // return the status
            return((!materialIsOnlyTransparent && hasTransparentSubMaterial) ? AccelerationStructureStatus.TransparencyIssue : AccelerationStructureStatus.Added);
        }